From e958e615ada29b6c21819d548de344d9899fa462 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Wed, 28 May 2025 19:08:56 +0200 Subject: [PATCH 001/465] config: wifi: enable iwlmld driver JIRA: https://issues.redhat.com/browse/RHEL-89168 Upstream-status: RHEL only ARK MR: cki-project/kernel-ark!3897 New WiFi7 intel cards are going to be handled with the new iwlmld driver in order to support the new firmwares. Signed-off-by: Jose Ignacio Tornos Martinez --- redhat/configs/common/generic/CONFIG_IWLMLD | 1 + 1 file changed, 1 insertion(+) create mode 100644 redhat/configs/common/generic/CONFIG_IWLMLD diff --git a/redhat/configs/common/generic/CONFIG_IWLMLD b/redhat/configs/common/generic/CONFIG_IWLMLD new file mode 100644 index 000000000000..b11111c46b3b --- /dev/null +++ b/redhat/configs/common/generic/CONFIG_IWLMLD @@ -0,0 +1 @@ +CONFIG_IWLMLD=m From 097c142f6c787b04c6cd316db96270947f9a78db Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Wed, 28 May 2025 19:14:22 +0200 Subject: [PATCH 002/465] config: wifi: disable new unsupported configuration options JIRA: https://issues.redhat.com/browse/RHEL-89168 Upstream Status: RHEL-Only (kernel-ark commit e899badc00f2) New CONFIG_RTW88_8814AE and CONFIG_RTW88_8814AU for the related devices are not supported at this moment. No functional change intended. Signed-off-by: Jose Ignacio Tornos Martinez --- redhat/configs/common/generic/CONFIG_RTW88_8814AE | 1 + redhat/configs/common/generic/CONFIG_RTW88_8814AU | 1 + 2 files changed, 2 insertions(+) create mode 100644 redhat/configs/common/generic/CONFIG_RTW88_8814AE create mode 100644 redhat/configs/common/generic/CONFIG_RTW88_8814AU diff --git a/redhat/configs/common/generic/CONFIG_RTW88_8814AE b/redhat/configs/common/generic/CONFIG_RTW88_8814AE new file mode 100644 index 000000000000..8b6aff9eb6c3 --- /dev/null +++ b/redhat/configs/common/generic/CONFIG_RTW88_8814AE @@ -0,0 +1 @@ +# CONFIG_RTW88_8814AE is not set diff --git a/redhat/configs/common/generic/CONFIG_RTW88_8814AU b/redhat/configs/common/generic/CONFIG_RTW88_8814AU new file mode 100644 index 000000000000..414c246fdbaa --- /dev/null +++ b/redhat/configs/common/generic/CONFIG_RTW88_8814AU @@ -0,0 +1 @@ +# CONFIG_RTW88_8814AU is not set From 70ff09ffbb8a2ea5665df7f751e19cb1daa9cc36 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:22 +0200 Subject: [PATCH 003/465] wifi: ath11k: convert timeouts to secs_to_jiffies() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b29425972c5234a59b6fb634125420ed74266377 Author: Easwar Hariharan Date: Tue Dec 10 22:02:45 2024 +0000 wifi: ath11k: convert timeouts to secs_to_jiffies() Commit b35108a51cf7 ("jiffies: Define secs_to_jiffies()") introduced secs_to_jiffies(). As the value here is a multiple of 1000, use secs_to_jiffies() instead of msecs_to_jiffies to avoid the multiplication. This is converted using scripts/coccinelle/misc/secs_to_jiffies.cocci with the following Coccinelle rules: @@ constant C; @@ - msecs_to_jiffies(C * 1000) + secs_to_jiffies(C) @@ constant C; @@ - msecs_to_jiffies(C * MSEC_PER_SEC) + secs_to_jiffies(C) Link: https://lkml.kernel.org/r/20241210-converge-secs-to-jiffies-v3-14-ddfefd7e9f2a@linux.microsoft.com Acked-by: Jeff Johnson Signed-off-by: Easwar Hariharan Cc: Alexander Gordeev Cc: Andrew Lunn Cc: Anna-Maria Behnsen Cc: Catalin Marinas Cc: Christian Borntraeger Cc: Christophe Leroy Cc: Daniel Mack Cc: David Airlie Cc: David S. Miller Cc: Dick Kennedy Cc: Eric Dumazet Cc: Florian Fainelli Cc: Greg Kroah-Hartman Cc: Haojian Zhuang Cc: Heiko Carstens Cc: Ilya Dryomov Cc: Jack Wang Cc: Jakub Kicinski Cc: James Bottomley Cc: James Smart Cc: Jaroslav Kysela Cc: Jeff Johnson Cc: Jens Axboe Cc: Jeroen de Borst Cc: Jiri Kosina Cc: Joe Lawrence Cc: Johan Hedberg Cc: Josh Poimboeuf Cc: Jozsef Kadlecsik Cc: Julia Lawall Cc: Kalle Valo Cc: Louis Peens Cc: Lucas De Marchi Cc: Luiz Augusto von Dentz Cc: Maarten Lankhorst Cc: Madhavan Srinivasan Cc: Marcel Holtmann Cc: Martin K. Petersen Cc: Maxime Ripard Cc: Michael Ellerman Cc: Miroslav Benes Cc: Naveen N Rao Cc: Nicholas Piggin Cc: Nicolas Palix Cc: Oded Gabbay Cc: Ofir Bitton Cc: Pablo Neira Ayuso Cc: Paolo Abeni Cc: Petr Mladek Cc: Praveen Kaligineedi Cc: Ray Jui Cc: Robert Jarzmik Cc: Rodrigo Vivi Cc: Roger Pau Monné Cc: Russell King Cc: Scott Branden Cc: Shailend Chand Cc: Simona Vetter Cc: Simon Horman Cc: Sven Schnelle Cc: Takashi Iwai Cc: Thomas Hellström Cc: Thomas Zimmermann Cc: Vasily Gorbik Cc: Xiubo Li Signed-off-by: Andrew Morton Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 57281a135dd7..bf192529e3fe 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -178,7 +178,7 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar, * received 'update stats' event, we keep a 3 seconds timeout in case, * fw_stats_done is not marked yet */ - timeout = jiffies + msecs_to_jiffies(3 * 1000); + timeout = jiffies + secs_to_jiffies(3); ath11k_debugfs_fw_stats_reset(ar); From c8367a48b66c3628a062a5dbb3458b1dd486c9c5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:22 +0200 Subject: [PATCH 004/465] wifi: ath9k: cleanup struct ath_tx_control and ath_tx_prepare() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7f2e104f2eebc2c3e15ae34740c7b05d0362040a Author: Dmitry Antipov Date: Wed Jan 15 20:17:49 2025 +0300 wifi: ath9k: cleanup struct ath_tx_control and ath_tx_prepare() After switching to mac80211 software queues, pointer to 'struct ath_node' in 'struct ath_tx_control' is still assigned but not actually used. So drop it and cleanup related things in 'ath_tx_prepare()'. Compile tested only. Signed-off-by: Dmitry Antipov Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20250115171750.259917-1-dmantipov@yandex.ru Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/xmit.c | 9 --------- 2 files changed, 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a728cc0387df..3a7164bf537a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -274,7 +274,6 @@ struct ath_node { struct ath_tx_control { struct ath_txq *txq; - struct ath_node *an; struct ieee80211_sta *sta; u8 paprd; }; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index db07ce6dbc08..0ac9212e42f7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2291,19 +2291,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; - struct ath_vif *avp; struct ath_softc *sc = hw->priv; int frmlen = skb->len + FCS_LEN; int padpos, padsize; - /* NOTE: sta can be NULL according to net/mac80211.h */ - if (sta) - txctl->an = (struct ath_node *)sta->drv_priv; - else if (vif && ieee80211_is_data(hdr->frame_control)) { - avp = (void *)vif->drv_priv; - txctl->an = &avp->mcast_node; - } - if (info->control.hw_key) frmlen += info->control.hw_key->icv_len; From d719d42b4bbc48d74be8958cc55a8e004db59084 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:22 +0200 Subject: [PATCH 005/465] wifi: ath9k: use unsigned long for activity check timestamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8fe64b0fedcb7348080529c46c71ae23f60c9d3e Author: Dmitry Antipov Date: Wed Jan 15 20:17:50 2025 +0300 wifi: ath9k: use unsigned long for activity check timestamp Since 'rx_active_check_time' of 'struct ath_softc' is in jiffies, prefer 'unsigned long' over 'u32' to avoid possible truncation in 'ath_hw_rx_inactive_check()'. Found with clang's -Wshorten-64-to-32, compile tested only. Signed-off-by: Dmitry Antipov Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20250115171750.259917-2-dmantipov@yandex.ru Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 3a7164bf537a..6e38aa7351e3 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -1017,7 +1017,7 @@ struct ath_softc { u8 gtt_cnt; u32 intrstatus; - u32 rx_active_check_time; + unsigned long rx_active_check_time; u32 rx_active_count; u16 ps_flags; /* PS_* */ bool ps_enabled; From 0262cfd103de760eab0b8dd7ba85a117f068139a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:22 +0200 Subject: [PATCH 006/465] wifi: ath12k: Refactor the monitor Rx parser handler argument JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b238941e8e97c17380f6a6a2dc51520b1829c167 Author: Karthikeyan Periyasamy Date: Tue Dec 24 20:06:11 2024 +0530 wifi: ath12k: Refactor the monitor Rx parser handler argument Currently, the monitor Rx parser handlers ath12k_dp_mon_rx_parse_status_tlv() and ath12k_dp_mon_parse_rx_dest() take the device handle from the caller. However, these handlers functionality is technically pdev specific. Additionally, the device handle can be retrieved from the pdev handle. Therefore, for better code understanding, change the monitor Rx parser handlers argument from the device handle to the pdev handle. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Acked-by: Kalle Valo Link: https://patch.msgid.link/20241224143613.164921-2-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 5a21961cfd46..85ff89b91624 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -563,11 +563,12 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info * } static enum hal_rx_mon_status -ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, +ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, u32 tlv_tag, const void *tlv_data, u32 userid) { + struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; u32 info[7]; @@ -1180,7 +1181,7 @@ mon_deliver_fail: } static enum hal_rx_mon_status -ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon, +ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct sk_buff *skb) { struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; @@ -1208,7 +1209,7 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon if (tlv_tag == HAL_RX_PPDU_END) tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); - hal_status = ath12k_dp_mon_rx_parse_status_tlv(ab, pmon, + hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, tlv_tag, ptr, tlv_userid); ptr += tlv_len; ptr = PTR_ALIGN(ptr, HAL_TLV_64_ALIGN); @@ -1228,14 +1229,13 @@ ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct sk_buff *skb, struct napi_struct *napi) { - struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct dp_mon_mpdu *tmp; struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; struct sk_buff *head_msdu, *tail_msdu; enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; - ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); list_for_each_entry_safe(mon_mpdu, tmp, &pmon->dp_rx_mon_mpdu_list, list) { list_del(&mon_mpdu->list); @@ -2477,7 +2477,7 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, for (i = 0; i < dest_idx; i++) { skb = pmon->dest_skb_q[i]; - hal_status = ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); if (ppdu_info->peer_id == HAL_INVALID_PEERID || hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { From ee9e330c3b75639a3d2a01ef0cf1f97fca3f415d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 007/465] wifi: ath12k: Refactor the monitor Tx/RX handler procedure arguments JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 844c256892e162e85db52ce2e1f3d14053ecd488 Author: Karthikeyan Periyasamy Date: Tue Dec 24 20:06:12 2024 +0530 wifi: ath12k: Refactor the monitor Tx/RX handler procedure arguments Currently, the pdev handle is given along with the mac id to all the monitor Tx/Rx handler procedure arguments. The mac id information is derived from the pdev handle itself. Therefore, remove the unnecessary mac id argument from the handler. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Acked-by: Kalle Valo Link: https://patch.msgid.link/20241224143613.164921-3-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 29 ++++++++++++------------ drivers/net/wireless/ath/ath12k/dp_mon.h | 7 +++--- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 85ff89b91624..ae2f6847bc88 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -845,7 +845,7 @@ static void ath12k_dp_mon_rx_msdus_set_payload(struct ath12k *ar, } static struct sk_buff * -ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id, +ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, struct sk_buff *head_msdu, struct sk_buff *tail_msdu, struct ieee80211_rx_status *rxs, bool *fcs_err) { @@ -1126,7 +1126,7 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } -static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, +static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, struct sk_buff *head_msdu, struct sk_buff *tail_msdu, struct hal_rx_mon_ppdu_info *ppduinfo, struct napi_struct *napi) @@ -1136,7 +1136,7 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, struct ieee80211_rx_status *rxs = &dp->rx_status; bool fcs_err = false; - mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mac_id, + mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, head_msdu, tail_msdu, rxs, &fcs_err); if (!mon_skb) @@ -1225,7 +1225,6 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi) { @@ -1243,7 +1242,7 @@ ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, tail_msdu = mon_mpdu->tail; if (head_msdu && tail_msdu) { - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, + ath12k_dp_mon_rx_deliver(ar, head_msdu, tail_msdu, ppdu_info, napi); } @@ -1924,7 +1923,7 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, } static void -ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, +ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, struct napi_struct *napi, struct dp_mon_tx_ppdu_info *tx_ppdu_info) { @@ -1938,7 +1937,7 @@ ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, tail_msdu = mon_mpdu->tail; if (head_msdu) - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, tail_msdu, + ath12k_dp_mon_rx_deliver(ar, head_msdu, tail_msdu, &tx_ppdu_info->rx_status, napi); kfree(mon_mpdu); @@ -1948,7 +1947,6 @@ ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id) @@ -1995,13 +1993,13 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, break; } while (tlv_status != DP_MON_TX_FES_STATUS_END); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_data_ppdu_info); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_prot_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_data_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_prot_ppdu_info); return tlv_status; } -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, enum dp_monitor_mode monitor_mode, struct napi_struct *napi) { @@ -2022,12 +2020,13 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, bool end_of_ppdu; struct hal_rx_mon_ppdu_info *ppdu_info; struct ath12k_peer *peer = NULL; + u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); ppdu_info = &pmon->mon_ppdu_info; memset(ppdu_info, 0, sizeof(*ppdu_info)); ppdu_info->peer_id = HAL_INVALID_PEERID; - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) { mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; @@ -2077,10 +2076,10 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, skb = pmon->dest_skb_q[i]; if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) - ath12k_dp_mon_rx_parse_mon_status(ar, pmon, mac_id, + ath12k_dp_mon_rx_parse_mon_status(ar, pmon, skb, napi); else - ath12k_dp_mon_tx_parse_mon_status(ar, pmon, mac_id, + ath12k_dp_mon_tx_parse_mon_status(ar, pmon, skb, napi, ppdu_id); peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); @@ -2538,7 +2537,7 @@ int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, if (!ar->monitor_started) ath12k_dp_mon_rx_process_stats(ar, mac_id, napi, &budget); else - num_buffs_reaped = ath12k_dp_mon_srng_process(ar, mac_id, &budget, + num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, monitor_mode, napi); return num_buffs_reaped; diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index fb9e9c176ce5..64c959c36459 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_MON_H @@ -77,12 +77,12 @@ struct dp_mon_tx_ppdu_info { enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, + struct sk_buff *skb, struct napi_struct *napi); int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab, struct dp_rxdma_mon_ring *buf_ring, int req_entries); -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, enum dp_monitor_mode monitor_mode, struct napi_struct *napi); int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, @@ -96,7 +96,6 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id); From 08e2f5d20534e84a3d0c4b29e251af291d465291 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 008/465] wifi: ath12k: Refactor Rx status TLV parsing procedure argument JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1254580d4f831808462e719e00d43a46e4254631 Author: Karthikeyan Periyasamy Date: Tue Dec 24 20:06:13 2024 +0530 wifi: ath12k: Refactor Rx status TLV parsing procedure argument Currently, ath12k_dp_mon_rx_parse_status_tlv() takes the TLV tag, TLV data and TLV userid as separate arguments from the caller. In the future, the TLV length will be needed for parsing the EHT TLV tag. Therefore, instead of increasing the number of arguments, pass the TLV header and retrieve the necessary fields from the TLV header itself. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Karthikeyan Periyasamy Acked-by: Kalle Valo Link: https://patch.msgid.link/20241224143613.164921-4-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index ae2f6847bc88..53f8e8f8959a 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -565,12 +565,16 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info * static enum hal_rx_mon_status ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, - u32 tlv_tag, const void *tlv_data, - u32 userid) + const struct hal_tlv_64_hdr *tlv) { struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - u32 info[7]; + const void *tlv_data = tlv->value; + u32 info[7], userid; + u16 tlv_tag; + + tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); + userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); switch (tlv_tag) { case HAL_RX_PPDU_START: { @@ -1187,7 +1191,6 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct hal_tlv_64_hdr *tlv; enum hal_rx_mon_status hal_status; - u32 tlv_userid; u16 tlv_tag, tlv_len; u8 *ptr = skb->data; @@ -1196,9 +1199,6 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, do { tlv = (struct hal_tlv_64_hdr *)ptr; tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); - tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); - tlv_userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); - ptr += sizeof(*tlv); /* The actual length of PPDU_END is the combined length of many PHY * TLVs that follow. Skip the TLV header and @@ -1208,10 +1208,11 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, if (tlv_tag == HAL_RX_PPDU_END) tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + else + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); - hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, - tlv_tag, ptr, tlv_userid); - ptr += tlv_len; + hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, tlv); + ptr += sizeof(*tlv) + tlv_len; ptr = PTR_ALIGN(ptr, HAL_TLV_64_ALIGN); if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) From 070242cfd14ce4e84f25214f36ac142a350d6b8b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 009/465] wifi: rtw89: coex: Add protect to avoid A2DP lag while Wi-Fi connecting JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5251fd321684b591245a072b765d01a6c22a112c Author: Ching-Te Ku Date: Fri Jan 10 09:54:14 2025 +0800 wifi: rtw89: coex: Add protect to avoid A2DP lag while Wi-Fi connecting To get a well Wi-Fi RF quality, Wi-Fi need to do RF calibrations. While Wi-Fi is doing RF calibrations, driver will pause the Bluetooth traffic to make sure the RF calibration will not be interfered by Bluetooth. However, if the RF calibrations take too much time, Bluetooth audio will perform a lag sound. Add a function to make Bluetooth can do traffic between the individual calibrations to avoid Bluetooth sound lag. And patch related A2DP coexistence mechanism actions. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250110015416.10704-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 90 +++++++++++-------- drivers/net/wireless/realtek/rtw89/coex.h | 2 + drivers/net/wireless/realtek/rtw89/core.h | 3 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 6 ++ .../net/wireless/realtek/rtw89/rtw8852bt.c | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 6 ++ 8 files changed, 85 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 68316d44b204..d5b8091e7541 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -4589,17 +4589,16 @@ static void _action_bt_a2dp(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + if (a2dp.vendor_id == 0x4c || dm->leak_ap || bt_linfo->slave_role) + dm->slot_dur[CXST_W1] = 20; + else + dm->slot_dur[CXST_W1] = 40; + + dm->slot_dur[CXST_B1] = BTC_B1_MAX; + switch (btc->cx.state_map) { case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, - BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP); - } else { - _set_policy(rtwdev, - BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP); - } + _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP); break; case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP */ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP); @@ -4609,15 +4608,10 @@ static void _action_bt_a2dp(struct rtw89_dev *rtwdev) break; case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP */ case BTC_WLINKING: /* wl-connecting + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, - BTC_ACT_BT_A2DP); - } else { - _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, - BTC_ACT_BT_A2DP); - } + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP); + else + _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP); break; case BTC_WIDLE: /* wl-idle + bt-A2DP */ _set_policy(rtwdev, BTC_CXP_AUTO_TD20B1, BTC_ACT_BT_A2DP); @@ -4645,7 +4639,10 @@ static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev) _set_policy(rtwdev, BTC_CXP_FIX_TD2060, BTC_ACT_BT_A2DPSINK); break; case BTC_WLINKING: /* wl-connecting + bt-A2dp_Sink */ - _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK); + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DPSINK); + else + _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK); break; case BTC_WIDLE: /* wl-idle + bt-A2dp_Sink */ _set_policy(rtwdev, BTC_CXP_FIX_TD2080, BTC_ACT_BT_A2DPSINK); @@ -4699,21 +4696,20 @@ static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + if (a2dp.vendor_id == 0x4c || dm->leak_ap || bt_linfo->slave_role) + dm->slot_dur[CXST_W1] = 20; + else + dm->slot_dur[CXST_W1] = 40; + + dm->slot_dur[CXST_B1] = BTC_B1_MAX; + switch (btc->cx.state_map) { case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+HID */ case BTC_WIDLE: /* wl-idle + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, - BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); - } else { - _set_policy(rtwdev, - BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP_HID); - } + _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); break; case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+HID */ - _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP_HID); + _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_HID); break; case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+HID */ @@ -4721,15 +4717,10 @@ static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev) break; case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+HID */ case BTC_WLINKING: /* wl-connecting + bt-A2DP+HID */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, - BTC_ACT_BT_A2DP_HID); - } else { - _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, - BTC_ACT_BT_A2DP_HID); - } + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP_HID); + else + _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); break; } } @@ -7002,7 +6993,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - if (wl->status.val & btc_scanning_map.val) { + if (wl->status.val & btc_scanning_map.val && !wl->rfk_info.con_rfk) { _action_wl_scan(rtwdev); bt->scan_rx_low_pri = true; goto exit; @@ -11037,3 +11028,24 @@ out: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] use version def[%d] = 0x%08x\n", (int)(btc->ver - rtw89_btc_ver_defs), btc->ver->fw_ver_code); } + +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms) +{ + struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt.link_info; + struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; + + if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) + return; + + if (!a2dp.exist) + return; + + fsleep(ms * 1000); +} +EXPORT_SYMBOL(rtw89_btc_ntfy_preserve_bt_time); + +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state) +{ + rtwdev->btc.cx.wl.rfk_info.con_rfk = state; +} +EXPORT_SYMBOL(rtw89_btc_ntfy_conn_rfk); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index dbdb56e063ef..757d03675cf4 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -290,6 +290,8 @@ void rtw89_coex_power_on(struct rtw89_dev *rtwdev); void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type); void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type); void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev); +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms); +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state); static inline u8 rtw89_btc_phymap(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ff4894c7fa8a..0f2a46e36b04 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1761,7 +1761,8 @@ struct rtw89_btc_wl_rfk_info { u32 phy_map: 2; u32 band: 2; u32 type: 8; - u32 rsvd: 14; + u32 con_rfk: 1; + u32 rsvd: 13; u32 start_time; u32 proc_time; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index c56f70267882..0487d1cbe605 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -1596,10 +1596,16 @@ static void rtw8851b_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8851b_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8851b_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8851b_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8851b_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8851b_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 9bd2842c27d5..8a9a5201b2ee 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1356,10 +1356,16 @@ static void rtw8852a_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852a_rx_dck(rtwdev, phy_idx, true, chanctx_idx); rtw8852a_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852a_tssi(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852a_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852a_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index dfb2bf61b0b8..cbaf54f88af6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -568,10 +568,16 @@ static void rtw8852b_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852b_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8852b_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852b_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852b_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852b_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index bde3e1fb7ca6..c0ae5dca3049 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -541,10 +541,16 @@ static void rtw8852bt_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852bt_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8852bt_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852bt_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852bt_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852bt_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index bc84b15e7826..106df618bb58 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -1853,10 +1853,16 @@ static void rtw8852c_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; rtw8852c_mcc_get_ch_info(rtwdev, phy_idx); + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852c_rx_dck(rtwdev, phy_idx, false); rtw8852c_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852c_tssi(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852c_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); rtw89_fw_h2c_rf_ntfy_mcc(rtwdev); } From 342b65870123955a45abbee7d62c81edd6dcd71f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 010/465] wifi: rtw89: coex: Separated Wi-Fi connecting event from Wi-Fi scan event JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4a57346652154bb339c48b41166df9154cff33f5 Author: Ching-Te Ku Date: Fri Jan 10 09:54:15 2025 +0800 wifi: rtw89: coex: Separated Wi-Fi connecting event from Wi-Fi scan event Wi-Fi connecting process don't need to assign to firmware slot control, if assign firmware slot control for Wi-Fi connecting event, firmware will not toggle slots because driver don't tell the slot schedule to firmware. Wi-Fi connecting event end should also cancel the 4way handshake status. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250110015416.10704-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index d5b8091e7541..cde65ee43bac 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -5399,7 +5399,8 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { + if (btc->cx.state_map != BTC_WLINKING && + RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { _action_wl_25g_mcc(rtwdev); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n"); } else if (rtwdev->dbcc_en) { @@ -7214,6 +7215,8 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx) _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); } + btc->dm.tdma_instant_excute = 1; + _run_coex(rtwdev, BTC_RSN_NTFY_SCAN_FINISH); } @@ -7662,7 +7665,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, else wl->status.map.connecting = 0; - if (state == BTC_ROLE_MSTS_STA_DIS_CONN) + if (state == BTC_ROLE_MSTS_STA_DIS_CONN || + state == BTC_ROLE_MSTS_STA_CONN_END) wl->status.map._4way = false; _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); From b66042003bd99d3649bc4760c0d8ebe8032c3f64 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 011/465] wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.2 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dbb6a738f6cb55c542df286f028fb7d524f71077 Author: Ching-Te Ku Date: Fri Jan 10 09:54:16 2025 +0800 wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.2 With this version, fix Wi-Fi scan/connecting/RF calibration triggered A2DP sound lag related issues. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250110015416.10704-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index cde65ee43bac..7e43ba0c4098 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -10,7 +10,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x07000113 +#define RTW89_COEX_VERSION 0x07000213 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 From b3a2ab4d31dc0c9487fad013e660a848389e3a61 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 012/465] wifi: rtw89: Correct immediate cfg_len calculation for scan_offload_be JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 361cb056e2468be534f47c1a6745f96581a721e3 Author: Liang Jie Date: Sun Jan 12 18:51:44 2025 +0800 wifi: rtw89: Correct immediate cfg_len calculation for scan_offload_be Ensures the correct calculation of `cfg_len` prior to the allocation of the skb in the `rtw89_fw_h2c_scan_offload_be` function, particularly when the `SCAN_OFFLOAD_BE_V0` firmware feature is enabled. It addresses an issue where an incorrect skb size might be allocated due to a delayed setting of `cfg_len`, potentially leading to memory inefficiencies. By moving the conditional check and assignment of `cfg_len` before skb allocation, the patch guarantees that `len`, which depends on `cfg_len`, is accurately computed, ensuring proper skb size and preventing any unnecessary memory reservation for firmware operations not supporting beyond the `w8` member of the command data structure. This correction helps to optimize memory usage and maintain consistent behavior across different firmware versions. Fixes: 6ca6b918f280 ("wifi: rtw89: 8922a: Add new fields for scan offload H2C command") Signed-off-by: Liang Jie Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250112105144.615474-1-buaajxlj@163.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5d4ad23cc3bd..5cc9ab78c09f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5311,6 +5311,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role; u8 opch_size = sizeof(*opch) * option->num_opch; u8 probe_id[NUM_NL80211_BANDS]; + u8 scan_offload_ver = U8_MAX; u8 cfg_len = sizeof(*h2c); unsigned int cond; u8 ver = U8_MAX; @@ -5321,6 +5322,11 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, rtw89_scan_get_6g_disabled_chan(rtwdev, option); + if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { + cfg_len = offsetofend(typeof(*h2c), w8); + scan_offload_ver = 0; + } + len = cfg_len + macc_role_size + opch_size; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -5392,10 +5398,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, RTW89_H2C_SCANOFLD_BE_W8_PROBE_RATE_6GHZ); } - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { - cfg_len = offsetofend(typeof(*h2c), w8); + if (scan_offload_ver == 0) goto flex_member; - } h2c->w9 = le32_encode_bits(sizeof(*h2c) / sizeof(h2c->w0), RTW89_H2C_SCANOFLD_BE_W9_SIZE_CFG) | From baecd82f3a4dd1c15f96ff3fd83c25bd32e5bb55 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 013/465] wifi: ath11k: Fix DMA buffer allocation to resolve SWIOTLB issues JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1bcd20981834928ccc5d981aacb806bb523d8b29 Author: P Praneesh Date: Sun Jan 19 22:12:18 2025 +0530 wifi: ath11k: Fix DMA buffer allocation to resolve SWIOTLB issues Currently, the driver allocates cacheable DMA buffers for rings like HAL_REO_DST and HAL_WBM2SW_RELEASE. The buffers for HAL_WBM2SW_RELEASE are large (1024 KiB), exceeding the SWIOTLB slot size of 256 KiB. This leads to "swiotlb buffer is full" error messages on systems without an IOMMU that use SWIOTLB, causing driver initialization failures. The driver calls dma_map_single() with these large buffers obtained from kzalloc(), resulting in ring initialization errors on systems without an IOMMU that use SWIOTLB. To address these issues, replace the flawed buffer allocation mechanism with the appropriate DMA API. Specifically, use dma_alloc_noncoherent() for cacheable DMA buffers, ensuring proper freeing of buffers with dma_free_noncoherent(). Error log: [ 10.194343] ath11k_pci 0000:04:00.0: swiotlb buffer is full (sz:1048583 bytes), total 32768 (slots), used 2529 (slots) [ 10.194406] ath11k_pci 0000:04:00.0: failed to set up tcl_comp ring (0) :-12 [ 10.194781] ath11k_pci 0000:04:00.0: failed to init DP: -12 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Reported-by: Tim Harvey Closes: https://lore.kernel.org/all/20241210041133.GA17116@lst.de/ Signed-off-by: P Praneesh Tested-by: Tim Harvey Link: https://patch.msgid.link/20250119164219.647059-2-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/dp.c | 35 +++++++++------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index fbf666d0ecf1..f124b7329e1a 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -104,14 +104,12 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) if (!ring->vaddr_unaligned) return; - if (ring->cached) { - dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size, - DMA_FROM_DEVICE); - kfree(ring->vaddr_unaligned); - } else { + if (ring->cached) + dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned, DMA_FROM_DEVICE); + else dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, ring->paddr_unaligned); - } ring->vaddr_unaligned = NULL; } @@ -249,25 +247,14 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, default: cached = false; } - - if (cached) { - ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); - if (!ring->vaddr_unaligned) - return -ENOMEM; - - ring->paddr_unaligned = dma_map_single(ab->dev, - ring->vaddr_unaligned, - ring->size, - DMA_FROM_DEVICE); - if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) { - kfree(ring->vaddr_unaligned); - ring->vaddr_unaligned = NULL; - return -ENOMEM; - } - } } - if (!cached) + if (cached) + ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size, + &ring->paddr_unaligned, + DMA_FROM_DEVICE, + GFP_KERNEL); + else ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, &ring->paddr_unaligned, GFP_KERNEL); From 0f741561588d34e4e9076f58d98dbfd5634b4d27 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 014/465] wifi: ath11k: Use dma_alloc_noncoherent for rx_tid buffer allocation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit eeadc6baf8b3dcd32787cc84f0473dc2a2850370 Author: P Praneesh Date: Sun Jan 19 22:12:19 2025 +0530 wifi: ath11k: Use dma_alloc_noncoherent for rx_tid buffer allocation Currently, the driver allocates cacheable DMA buffers for the rx_tid structure using kzalloc() and dma_map_single(). These buffers are long-lived and can persist for the lifetime of the peer, which is not advisable. Instead of using kzalloc() and dma_map_single() for allocating cacheable DMA buffers, utilize the dma_alloc_noncoherent() helper for the allocation of long-lived cacheable DMA buffers, such as the peer's rx_tid. Since dma_alloc_noncoherent() returns unaligned physical and virtual addresses, align them internally before use within the driver. This ensures proper allocation of non-coherent memory through the kernel helper. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Signed-off-by: P Praneesh Tested-by: Tim Harvey Link: https://patch.msgid.link/20250119164219.647059-3-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/dp.h | 6 +- drivers/net/wireless/ath/ath11k/dp_rx.c | 117 +++++++++++------------- 2 files changed, 58 insertions(+), 65 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index f777314db8b3..7a55afd33be8 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_H @@ -20,7 +20,6 @@ struct ath11k_ext_irq_grp; struct dp_rx_tid { u8 tid; - u32 *vaddr; dma_addr_t paddr; u32 size; u32 ba_win_sz; @@ -37,6 +36,9 @@ struct dp_rx_tid { /* Timer info related to fragments */ struct timer_list frag_timer; struct ath11k_base *ab; + u32 *vaddr_unaligned; + dma_addr_t paddr_unaligned; + u32 unaligned_size; }; #define DP_REO_DESC_FREE_THRESHOLD 64 diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 029ecf51c9ef..5e71e5d9ecb7 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -675,11 +675,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); rx_tid = &cmd->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd); } @@ -689,11 +689,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; rx_tid = &cmd_cache->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd_cache); } @@ -708,11 +708,11 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, if (status != HAL_REO_CMD_SUCCESS) ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - if (rx_tid->vaddr) { - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(dp->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -749,10 +749,10 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, if (ret) { ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -802,10 +802,10 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } void ath11k_peer_rx_tid_delete(struct ath11k *ar, @@ -831,14 +831,16 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, if (ret != -ESHUTDOWN) ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ar->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } rx_tid->paddr = 0; + rx_tid->paddr_unaligned = 0; rx_tid->size = 0; + rx_tid->unaligned_size = 0; } static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, @@ -982,10 +984,9 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, if (!rx_tid->active) goto unlock_exit; - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; rx_tid->active = false; @@ -1000,9 +1001,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; struct dp_rx_tid *rx_tid; - u32 hw_desc_sz; - u32 *addr_aligned; - void *vaddr; + u32 hw_desc_sz, *vaddr; + void *vaddr_unaligned; dma_addr_t paddr; int ret; @@ -1050,49 +1050,40 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, else hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { + rx_tid->unaligned_size = hw_desc_sz + HAL_LINK_DESC_ALIGN - 1; + vaddr_unaligned = dma_alloc_noncoherent(ab->dev, rx_tid->unaligned_size, &paddr, + DMA_BIDIRECTIONAL, GFP_ATOMIC); + if (!vaddr_unaligned) { spin_unlock_bh(&ab->base_lock); return -ENOMEM; } - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr); - if (ret) { - spin_unlock_bh(&ab->base_lock); - ath11k_warn(ab, "failed to setup dma map for peer %pM rx tid %d: %d\n", - peer_mac, tid, ret); - goto err_mem_free; - } - - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; + rx_tid->vaddr_unaligned = vaddr_unaligned; + vaddr = PTR_ALIGN(vaddr_unaligned, HAL_LINK_DESC_ALIGN); + rx_tid->paddr_unaligned = paddr; + rx_tid->paddr = rx_tid->paddr_unaligned + ((unsigned long)vaddr - + (unsigned long)rx_tid->vaddr_unaligned); + ath11k_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type); rx_tid->size = hw_desc_sz; rx_tid->active = true; + /* After dma_alloc_noncoherent, vaddr is being modified for reo qdesc setup. + * Since these changes are not reflected in the device, driver now needs to + * explicitly call dma_sync_single_for_device. + */ + dma_sync_single_for_device(ab->dev, rx_tid->paddr, + rx_tid->size, + DMA_TO_DEVICE); spin_unlock_bh(&ab->base_lock); - ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, rx_tid->paddr, + tid, 1, ba_win_sz); if (ret) { ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n", peer_mac, tid, ret); ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid); } - return ret; - -err_mem_free: - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; - return ret; } From a557648bc5650d0828e62fb4cac9e93bf953ff7a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:23 +0200 Subject: [PATCH 015/465] wifi: ath: create common testmode_i.h file for ath drivers JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6fabed4052176a18da9519fc14a18897ed4f820d Author: Aaradhana Sahu Date: Sun Jan 19 14:06:54 2025 +0530 wifi: ath: create common testmode_i.h file for ath drivers User space application requires that the testmode interface is exactly same between ath drivers. Move testmode_i.h file in ath directory to ensure that all ath driver uses same testmode interface instead of duplicating testmode_i.h for each ath drivers. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250119083657.1937557-2-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/testmode.c | 80 +++++++++---------- .../wireless/ath/{ath11k => }/testmode_i.h | 54 ++++++------- 2 files changed, 67 insertions(+), 67 deletions(-) rename drivers/net/wireless/ath/{ath11k => }/testmode_i.h (50%) diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 302d66092b97..9be1cd742339 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "testmode.h" @@ -10,18 +10,18 @@ #include "wmi.h" #include "hw.h" #include "core.h" -#include "testmode_i.h" +#include "../testmode_i.h" #define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) #define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) -static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { - [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, - .len = ATH11K_TM_DATA_MAX_LEN }, - [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +static const struct nla_policy ath11k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, }; static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab) @@ -73,9 +73,9 @@ static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) { + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { ath11k_warn(ab, "failed to populate testmode unsegmented event\n"); kfree_skb(nl_skb); goto out; @@ -140,7 +140,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, data_pos = ab->testmode.data_pos; - if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) { + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { ath11k_warn(ab, "Invalid ftm event length at %d: %d\n", data_pos, datalen); ret = -EINVAL; @@ -172,10 +172,10 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, - ATH11K_TM_CMD_WMI_FTM) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos, + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, &ab->testmode.eventdata[0])) { ath11k_warn(ab, "failed to populate segmented testmode event"); kfree_skb(nl_skb); @@ -235,23 +235,23 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd get version_major %d version_minor %d\n", - ATH11K_TESTMODE_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MINOR); + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, nla_total_size(sizeof(u32))); if (!skb) return -ENOMEM; - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MAJOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR); if (ret) { kfree_skb(skb); return ret; } - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, - ATH11K_TESTMODE_VERSION_MINOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR); if (ret) { kfree_skb(skb); return ret; @@ -277,7 +277,7 @@ static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[]) goto err; } - ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, + ar->ab->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL); if (!ar->ab->testmode.eventdata) { ret = -ENOMEM; @@ -310,25 +310,25 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[], mutex_lock(&ar->conf_mutex); - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { + if (!tb[ATH_TM_ATTR_WMI_CMDID]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); if (!buf_len) { ath11k_warn(ar->ab, "No data present in testmode wmi command\n"); ret = -EINVAL; goto out; } - cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); /* Make sure that the buffer length is long enough to * hold TLV and pdev/vdev id. @@ -409,13 +409,13 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) goto out; } - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); cmd_id = WMI_PDEV_UTF_CMDID; ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, @@ -476,25 +476,25 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) { struct ath11k *ar = hw->priv; - struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; int ret; - ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath11k_tm_policy, NULL); if (ret) return ret; - if (!tb[ATH11K_TM_ATTR_CMD]) + if (!tb[ATH_TM_ATTR_CMD]) return -EINVAL; - switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { - case ATH11K_TM_CMD_GET_VERSION: + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_GET_VERSION: return ath11k_tm_cmd_get_version(ar, tb); - case ATH11K_TM_CMD_WMI: + case ATH_TM_CMD_WMI: return ath11k_tm_cmd_wmi(ar, tb, vif); - case ATH11K_TM_CMD_TESTMODE_START: + case ATH_TM_CMD_TESTMODE_START: return ath11k_tm_cmd_testmode_start(ar, tb); - case ATH11K_TM_CMD_WMI_FTM: + case ATH_TM_CMD_WMI_FTM: return ath11k_tm_cmd_wmi_ftm(ar, tb); default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h similarity index 50% rename from drivers/net/wireless/ath/ath11k/testmode_i.h rename to drivers/net/wireless/ath/testmode_i.h index 91b83873d660..980ef2f3f05f 100644 --- a/drivers/net/wireless/ath/ath11k/testmode_i.h +++ b/drivers/net/wireless/ath/testmode_i.h @@ -1,59 +1,59 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ -/* "API" level of the ath11k testmode interface. Bump it after every +/* "API" level of the ath testmode interface. Bump it after every * incompatible interface change. */ -#define ATH11K_TESTMODE_VERSION_MAJOR 1 +#define ATH_TESTMODE_VERSION_MAJOR 1 /* Bump this after every _compatible_ interface change, for example * addition of a new command or an attribute. */ -#define ATH11K_TESTMODE_VERSION_MINOR 1 +#define ATH_TESTMODE_VERSION_MINOR 1 -#define ATH11K_TM_DATA_MAX_LEN 5000 -#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048 +#define ATH_TM_DATA_MAX_LEN 5000 +#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048 -enum ath11k_tm_attr { - __ATH11K_TM_ATTR_INVALID = 0, - ATH11K_TM_ATTR_CMD = 1, - ATH11K_TM_ATTR_DATA = 2, - ATH11K_TM_ATTR_WMI_CMDID = 3, - ATH11K_TM_ATTR_VERSION_MAJOR = 4, - ATH11K_TM_ATTR_VERSION_MINOR = 5, - ATH11K_TM_ATTR_WMI_OP_VERSION = 6, +enum ath_tm_attr { + __ATH_TM_ATTR_INVALID = 0, + ATH_TM_ATTR_CMD = 1, + ATH_TM_ATTR_DATA = 2, + ATH_TM_ATTR_WMI_CMDID = 3, + ATH_TM_ATTR_VERSION_MAJOR = 4, + ATH_TM_ATTR_VERSION_MINOR = 5, + ATH_TM_ATTR_WMI_OP_VERSION = 6, /* keep last */ - __ATH11K_TM_ATTR_AFTER_LAST, - ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1, + __ATH_TM_ATTR_AFTER_LAST, + ATH_TM_ATTR_MAX = __ATH_TM_ATTR_AFTER_LAST - 1, }; -/* All ath11k testmode interface commands specified in - * ATH11K_TM_ATTR_CMD +/* All ath testmode interface commands specified in + * ATH_TM_ATTR_CMD */ -enum ath11k_tm_cmd { - /* Returns the supported ath11k testmode interface version in - * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space +enum ath_tm_cmd { + /* Returns the supported ath testmode interface version in + * ATH_TM_ATTR_VERSION. Always guaranteed to work. User space * uses this to verify it's using the correct version of the * testmode interface */ - ATH11K_TM_CMD_GET_VERSION = 0, + ATH_TM_CMD_GET_VERSION = 0, /* The command used to transmit a WMI command to the firmware and * the event to receive WMI events from the firmware. Without * struct wmi_cmd_hdr header, only the WMI payload. Command id is - * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in - * ATH11K_TM_ATTR_DATA. + * provided with ATH_TM_ATTR_WMI_CMDID and payload in + * ATH_TM_ATTR_DATA. */ - ATH11K_TM_CMD_WMI = 1, + ATH_TM_CMD_WMI = 1, /* Boots the UTF firmware, the netdev interface must be down at the * time. */ - ATH11K_TM_CMD_TESTMODE_START = 2, + ATH_TM_CMD_TESTMODE_START = 2, /* The command used to transmit a FTM WMI command to the firmware * and the event to receive WMI events from the firmware. The data @@ -62,5 +62,5 @@ enum ath11k_tm_cmd { * The data payload size could be large and the driver needs to * send segmented data to firmware. */ - ATH11K_TM_CMD_WMI_FTM = 3, + ATH_TM_CMD_WMI_FTM = 3, }; From 7bfa29e873ca2583c466f661826c45473d588544 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 016/465] wifi: ath12k: export ath12k_wmi_tlv_hdr for testmode JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 786d5258faba099779fe0d14d344893f9215d6be Author: Aaradhana Sahu Date: Sun Jan 19 14:06:55 2025 +0530 wifi: ath12k: export ath12k_wmi_tlv_hdr for testmode Export ath12k_wmi_tlv_hdr() to further use in the testmode command processing. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250119083657.1937557-3-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/wmi.c | 4 ++-- drivers/net/wireless/ath/ath12k/wmi.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index abb510d235a5..fdb63001e38f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -173,7 +173,7 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_p2p_noa_event) }, }; -static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) { return le32_encode_bits(cmd, WMI_TLV_TAG) | le32_encode_bits(len, WMI_TLV_LEN); diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 45fe699ce8a5..30ba3f892da5 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_WMI_H @@ -5753,6 +5753,7 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, const u8 *buf, size_t buf_len); int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) From 9bce20aa83f4a340eb758e1ed1e72b95508d39c8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 017/465] wifi: ath12k: add factory test mode support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3bc374cbc49ec39891381d2e25d81a6f74fa3364 Author: Aaradhana Sahu Date: Sun Jan 19 14:06:56 2025 +0530 wifi: ath12k: add factory test mode support Add support to process factory test mode commands(FTM) for calibration. By default firmware start with MISSION mode and to process the FTM commands firmware needs to be restarted in FTM mode using module parameter ftm_mode. The pre-request is all the radios should be down before starting the test. All ath12k test mode interface related commands specified in enum ath_tm_cmd. When start command ATH_TM_CMD_TESTMODE_START is received, ar state is set to test Mode and FTM daemon sends test mode command to wifi driver via cfg80211. Wifi driver sends these command to firmware as wmi events. If it is segmented commands it will be broken down into multiple segments and encoded with TLV header else it is sent to firmware as it is. Firmware response via UTF events, wifi driver creates skb and send to cfg80211, cfg80211 sends firmware response to FTM daemon via netlink message. Command to boot in ftm mode insmod ath12k ftm_mode=1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250119083657.1937557-4-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/Makefile | 1 + drivers/net/wireless/ath/ath12k/core.c | 22 +- drivers/net/wireless/ath/ath12k/core.h | 11 + drivers/net/wireless/ath/ath12k/debug.h | 3 +- drivers/net/wireless/ath/ath12k/dp.c | 5 +- drivers/net/wireless/ath/ath12k/mac.c | 15 +- drivers/net/wireless/ath/ath12k/pci.c | 3 +- drivers/net/wireless/ath/ath12k/testmode.c | 395 +++++++++++++++++++++ drivers/net/wireless/ath/ath12k/testmode.h | 40 +++ drivers/net/wireless/ath/ath12k/wmi.c | 37 +- drivers/net/wireless/ath/ath12k/wmi.h | 20 ++ drivers/net/wireless/ath/ath12k/wow.c | 3 +- 12 files changed, 543 insertions(+), 12 deletions(-) create mode 100644 drivers/net/wireless/ath/ath12k/testmode.c create mode 100644 drivers/net/wireless/ath/ath12k/testmode.h diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index b5bb3e2599cd..4a7f5e87384c 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -28,6 +28,7 @@ ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o ath12k-$(CONFIG_PM) += wow.o ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o +ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 0606116d6b9c..20c8a7bed3db 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -23,6 +23,10 @@ unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); +bool ath12k_ftm_mode; +module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444); +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode"); + /* protected with ath12k_hw_group_mutex */ static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list); @@ -693,6 +697,11 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) { int ret; + if (ath12k_ftm_mode) { + ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM; + ath12k_info(ab, "Booting in ftm mode\n"); + } + ret = ath12k_qmi_init_service(ab); if (ret) { ath12k_err(ab, "failed to initialize qmi :%d\n", ret); @@ -741,8 +750,7 @@ static void ath12k_core_pdev_destroy(struct ath12k_base *ab) ath12k_dp_pdev_free(ab); } -static int ath12k_core_start(struct ath12k_base *ab, - enum ath12k_firmware_mode mode) +static int ath12k_core_start(struct ath12k_base *ab) { int ret; @@ -1068,7 +1076,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) struct ath12k_hw_group *ag = ath12k_ab_to_ag(ab); int ret, i; - ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL); + ret = ath12k_core_start_firmware(ab, ab->fw_mode); if (ret) { ath12k_err(ab, "failed to start firmware: %d\n", ret); return ret; @@ -1089,7 +1097,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) mutex_lock(&ag->mutex); mutex_lock(&ab->core_lock); - ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL); + ret = ath12k_core_start(ab); if (ret) { ath12k_err(ab, "failed to start core: %d\n", ret); goto err_dp_free; @@ -1239,7 +1247,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) for (i = 0; i < ag->num_hw; i++) { ah = ath12k_ag_to_ah(ag, i); - if (!ah || ah->state == ATH12K_HW_STATE_OFF) + if (!ah || ah->state == ATH12K_HW_STATE_OFF || + ah->state == ATH12K_HW_STATE_TM) continue; ieee80211_stop_queues(ah->hw); @@ -1309,6 +1318,9 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) ath12k_warn(ab, "device is wedged, will not restart hw %d\n", i); break; + case ATH12K_HW_STATE_TM: + ath12k_warn(ab, "fw mode reset done radio %d\n", i); + break; } mutex_unlock(&ah->hw_mutex); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ee595794a7ae..86a1eeec64a6 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -235,6 +235,7 @@ enum ath12k_dev_flags { ATH12K_FLAG_CE_IRQ_ENABLED, ATH12K_FLAG_EXT_IRQ_ENABLED, ATH12K_FLAG_QMI_FW_READY_COMPLETE, + ATH12K_FLAG_FTM_SEGMENTED, }; struct ath12k_tx_conf { @@ -534,12 +535,19 @@ enum ath12k_hw_state { ATH12K_HW_STATE_RESTARTING, ATH12K_HW_STATE_RESTARTED, ATH12K_HW_STATE_WEDGED, + ATH12K_HW_STATE_TM, /* Add other states as required */ }; /* Antenna noise floor */ #define ATH12K_DEFAULT_NOISE_FLOOR -95 +struct ath12k_ftm_event_obj { + u32 data_pos; + u32 expected_seq; + u8 *eventdata; +}; + struct ath12k_fw_stats { u32 pdev_id; u32 stats_id; @@ -714,6 +722,7 @@ struct ath12k { struct completion mlo_setup_done; u32 mlo_setup_status; + u8 ftm_msgref; }; struct ath12k_hw { @@ -1052,6 +1061,8 @@ struct ath12k_base { struct ath12k_hw_group *ag; struct ath12k_wsi_info wsi_info; + enum ath12k_firmware_mode fw_mode; + struct ath12k_ftm_event_obj ftm_event_obj; /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index 90e801136bc6..0aa7c8ccb14c 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUG_H_ @@ -37,6 +37,7 @@ __printf(2, 3) void __ath12k_warn(struct device *dev, const char *fmt, ...); #define ath12k_hw_warn(ah, fmt, ...) __ath12k_warn((ah)->dev, fmt, ##__VA_ARGS__) extern unsigned int ath12k_debug_mask; +extern bool ath12k_ftm_mode; #ifdef CONFIG_ATH12K_DEBUG __printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab, diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 9e5a4e75f2f6..b1f27c3ac723 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1315,6 +1315,9 @@ void ath12k_dp_cc_config(struct ath12k_base *ab) u32 wbm_base = HAL_SEQ_WCSS_UMAC_WBM_REG; u32 val = 0; + if (ath12k_ftm_mode) + return; + ath12k_hif_write32(ab, reo_base + HAL_REO1_SW_COOKIE_CFG0(ab), cmem_base); val |= u32_encode_bits(ATH12K_CMEM_ADDR_MSB, diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2d062b5904a8..329c05003721 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -15,6 +15,7 @@ #include "hw.h" #include "dp_tx.h" #include "dp_rx.h" +#include "testmode.h" #include "peer.h" #include "debugfs.h" #include "hif.h" @@ -675,7 +676,10 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) return NULL; for (i = 0; i < ab->num_radios; i++) { - pdev = rcu_dereference(ab->pdevs_active[i]); + if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM) + pdev = &ab->pdevs[i]; + else + pdev = rcu_dereference(ab->pdevs_active[i]); if (pdev && pdev->pdev_id == pdev_id) return (pdev->ar ? pdev->ar : NULL); @@ -7363,9 +7367,14 @@ err: static void ath12k_drain_tx(struct ath12k_hw *ah) { - struct ath12k *ar; + struct ath12k *ar = ah->radio; int i; + if (ath12k_ftm_mode) { + ath12k_err(ar->ab, "fail to start mac operations in ftm mode\n"); + return; + } + lockdep_assert_wiphy(ah->hw->wiphy); for_each_ar(ah, ar, i) @@ -7394,6 +7403,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: case ATH12K_HW_STATE_ON: + case ATH12K_HW_STATE_TM: ah->state = ATH12K_HW_STATE_OFF; WARN_ON(1); @@ -10299,6 +10309,7 @@ static const struct ieee80211_ops ath12k_ops = { .resume = ath12k_wow_op_resume, .set_wakeup = ath12k_wow_op_set_wakeup, #endif + CFG80211_TESTMODE_CMD(ath12k_tm_cmd) }; static void ath12k_mac_update_ch_list(struct ath12k *ar, diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 06cff3849ab8..4a50d7118b3e 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1561,6 +1561,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab_pci->ab = ab; ab_pci->pdev = pdev; ab->hif.ops = &ath12k_pci_hif_ops; + ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL; pci_set_drvdata(pdev, ab); spin_lock_init(&ab_pci->window_lock); diff --git a/drivers/net/wireless/ath/ath12k/testmode.c b/drivers/net/wireless/ath/ath12k/testmode.c new file mode 100644 index 000000000000..18d56a976dc7 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "testmode.h" +#include +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "core.h" +#include "hif.h" +#include "../testmode_i.h" + +#define ATH12K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) +#define ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) + +static const struct nla_policy ath12k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +}; + +static struct ath12k *ath12k_tm_get_ar(struct ath12k_base *ab) +{ + struct ath12k_pdev *pdev; + struct ath12k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + if (ar && ar->ah->state == ATH12K_HW_STATE_TM) + return ar; + } + + return NULL; +} + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb length %d\n", + cmd_id, skb->len); + + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + + ar = ath12k_tm_get_ar(ab); + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(skb->len), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for unsegmented testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { + ath12k_warn(ab, "failed to populate testmode unsegmented event\n"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + u32 data_pos, pdev_id; + u16 datalen; + u8 total_segments, current_seq; + u8 const *buf_pos; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d ftm event msg %pK datalen %d\n", + cmd_id, ftm_msg, length); + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", ftm_msg, length); + pdev_id = DP_HW2SW_MACID(le32_to_cpu(ftm_msg->seg_hdr.pdev_id)); + + if (pdev_id >= ab->num_radios) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev id\n"); + return; + } + + ar = ab->pdevs[pdev_id].ar; + + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to absence of pdev\n"); + return; + } + + current_seq = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + total_segments = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS); + datalen = length - (sizeof(struct ath12k_wmi_ftm_seg_hdr_params)); + buf_pos = ftm_msg->data; + + if (current_seq == 0) { + ab->ftm_event_obj.expected_seq = 0; + ab->ftm_event_obj.data_pos = 0; + } + + data_pos = ab->ftm_event_obj.data_pos; + + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { + ath12k_warn(ab, + "Invalid event length date_pos[%d] datalen[%d]\n", + data_pos, datalen); + return; + } + + memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen); + data_pos += datalen; + + if (++ab->ftm_event_obj.expected_seq != total_segments) { + ab->ftm_event_obj.data_pos = data_pos; + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "partial data received current_seq[%d], total_seg[%d]\n", + current_seq, total_segments); + return; + } + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "total data length[%d] = [%d]\n", + data_pos, ftm_msg->seg_hdr.len); + + spin_lock_bh(&ar->data_lock); + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(data_pos), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, + &ab->ftm_event_obj.eventdata[0])) { + ath12k_warn(ab, "failed to populate testmode event"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +static int ath12k_tm_cmd_get_version(struct ath12k *ar, struct nlattr *tb[]) +{ + struct sk_buff *skb; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd get version_major %d version_minor %d\n", + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); + + spin_lock_bh(&ar->data_lock); + skb = cfg80211_testmode_alloc_reply_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32))); + spin_unlock_bh(&ar->data_lock); + + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR) || + nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR)) { + kfree_skb(skb); + return -ENOBUFS; + } + + return cfg80211_testmode_reply(skb); +} + +static int ath12k_tm_cmd_process_ftm(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct ath12k_wmi_ftm_cmd *ftm_cmd; + int ret = 0; + void *buf; + size_t aligned_len; + u32 cmd_id, buf_len; + u16 chunk_len, total_bytes, num_segments; + u8 segnumber = 0, *bufpos; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, "ah->state %d\n", ar->ah->state); + if (ar->ah->state != ATH12K_HW_STATE_TM) + return -ENETDOWN; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + cmd_id = WMI_PDEV_UTF_CMDID; + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", + cmd_id, buf, buf_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + bufpos = buf; + total_bytes = buf_len; + num_segments = total_bytes / MAX_WMI_UTF_LEN; + + if (buf_len - (num_segments * MAX_WMI_UTF_LEN)) + num_segments++; + + while (buf_len) { + if (buf_len > MAX_WMI_UTF_LEN) + chunk_len = MAX_WMI_UTF_LEN; /* MAX message */ + else + chunk_len = buf_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len + + sizeof(struct ath12k_wmi_ftm_cmd))); + + if (!skb) + return -ENOMEM; + + ftm_cmd = (struct ath12k_wmi_ftm_cmd *)skb->data; + aligned_len = chunk_len + sizeof(struct ath12k_wmi_ftm_seg_hdr_params); + ftm_cmd->tlv_header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len); + ftm_cmd->seg_hdr.len = cpu_to_le32(total_bytes); + ftm_cmd->seg_hdr.msgref = cpu_to_le32(ar->ftm_msgref); + ftm_cmd->seg_hdr.segmentinfo = + le32_encode_bits(num_segments, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS) | + le32_encode_bits(segnumber, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + ftm_cmd->seg_hdr.pdev_id = cpu_to_le32(ar->pdev->pdev_id); + segnumber++; + memcpy(&ftm_cmd->data, bufpos, chunk_len); + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + + if (ret) { + ath12k_warn(ar->ab, "ftm wmi command fail: %d\n", ret); + kfree_skb(skb); + return ret; + } + + buf_len -= chunk_len; + bufpos += chunk_len; + } + + ++ar->ftm_msgref; + return ret; +} + +static int ath12k_tm_cmd_testmode_start(struct ath12k *ar, struct nlattr *tb[]) +{ + if (ar->ah->state == ATH12K_HW_STATE_TM) + return -EALREADY; + + if (ar->ah->state != ATH12K_HW_STATE_OFF) + return -EBUSY; + + ar->ab->ftm_event_obj.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, + GFP_KERNEL); + + if (!ar->ab->ftm_event_obj.eventdata) + return -ENOMEM; + + ar->ah->state = ATH12K_HW_STATE_TM; + ar->ftm_msgref = 0; + return 0; +} + +static int ath12k_tm_cmd_wmi(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_pdev_set_param_cmd *cmd; + int ret = 0, tag; + void *buf; + u32 cmd_id, buf_len; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + if (!tb[ATH_TM_ATTR_WMI_CMDID]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + + if (!buf_len) { + ath12k_warn(ar->ab, "No data present in testmode command\n"); + return -EINVAL; + } + + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); + + cmd = buf; + tag = le32_get_bits(cmd->tlv_header, WMI_TLV_TAG); + + if (tag == WMI_TAG_PDEV_SET_PARAM_CMD) + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf length %d\n", + cmd_id, buf_len); + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, buf_len); + + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + dev_kfree_skb(skb); + ath12k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", + ret); + } + + return ret; +} + +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = NULL; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; + struct ath12k_base *ab; + struct wiphy *wiphy = hw->wiphy; + int ret; + + lockdep_assert_held(&wiphy->mtx); + + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath12k_tm_policy, + NULL); + if (ret) + return ret; + + if (!tb[ATH_TM_ATTR_CMD]) + return -EINVAL; + + /* TODO: have to handle ar for MLO case */ + if (ah->num_radio) + ar = ah->radio; + + if (!ar) + return -EINVAL; + + ab = ar->ab; + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_WMI: + return ath12k_tm_cmd_wmi(ar, tb); + case ATH_TM_CMD_TESTMODE_START: + return ath12k_tm_cmd_testmode_start(ar, tb); + case ATH_TM_CMD_GET_VERSION: + return ath12k_tm_cmd_get_version(ar, tb); + case ATH_TM_CMD_WMI_FTM: + set_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags); + return ath12k_tm_cmd_process_ftm(ar, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h new file mode 100644 index 000000000000..ef6ab21d19b8 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "hif.h" + +#ifdef CONFIG_NL80211_TESTMODE + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb); +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length); +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#else + +static inline void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ +} + +static inline void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *msg, + u16 length) +{ +} + +static inline int ath12k_tm_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index fdb63001e38f..296238c7ee0f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -20,6 +20,7 @@ #include "hw.h" #include "peer.h" #include "p2p.h" +#include "testmode.h" struct ath12k_wmi_svc_ready_parse { bool wmi_svc_bitmap_done; @@ -7026,6 +7027,35 @@ exit: kfree(tb); } +static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + const struct ath12k_wmi_ftm_event *ev; + const void **tb; + int ret; + u16 length; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse ftm event tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev) { + ath12k_warn(ab, "failed to fetch ftm msg\n"); + kfree(tb); + return; + } + + length = skb->len - TLV_HDR_SIZE; + ath12k_tm_process_event(ab, cmd_id, ev, length); + kfree(tb); + tb = NULL; +} + static void ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, struct sk_buff *skb) @@ -7584,7 +7614,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: /* debug might flood hence silently ignore (no-op) */ break; - /* TODO: Add remaining events */ + case WMI_PDEV_UTF_EVENTID: + if (test_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags)) + ath12k_tm_wmi_event_segmented(ab, id, skb); + else + ath12k_tm_wmi_event_unsegmented(ab, id, skb); + break; default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); break; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 30ba3f892da5..81248253a368 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -3624,6 +3624,26 @@ struct ath12k_wmi_p2p_noa_info { struct ath12k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; } __packed; +#define MAX_WMI_UTF_LEN 252 + +struct ath12k_wmi_ftm_seg_hdr_params { + __le32 len; + __le32 msgref; + __le32 segmentinfo; + __le32 pdev_id; +} __packed; + +struct ath12k_wmi_ftm_cmd { + __le32 tlv_header; + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + +struct ath12k_wmi_ftm_event { + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 #define WMI_EMA_BEACON_CNT GENMASK(7, 0) diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 9e1c0bfd212f..dce9bd0bcaef 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -990,6 +990,7 @@ exit: case ATH12K_HW_STATE_RESTARTING: case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: + case ATH12K_HW_STATE_TM: ath12k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", ah->state); ret = -EIO; From 1ea89291cb993b1b9dfa04999caff442c1f239d1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 018/465] wifi: ath12k: Disable MLO in Factory Test Mode JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a93185b708f259ace63414b8529e9e15c6323b69 Author: Aaradhana Sahu Date: Sun Jan 19 14:06:57 2025 +0530 wifi: ath12k: Disable MLO in Factory Test Mode Factory test mode(FTM) is supported only in non-MLO(multi-link operation) mode. Therefore, disable MLO when driver boots in FTM mode. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250119083657.1937557-5-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 20c8a7bed3db..2dd0666959cd 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1614,6 +1614,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a lockdep_assert_held(&ath12k_hw_group_mutex); + if (ath12k_ftm_mode) + goto invalid_group; + /* The grouping of multiple devices will be done based on device tree file. * The platforms that do not have any valid group information would have * each device to be part of its own invalid group. @@ -1801,6 +1804,9 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) struct ath12k_base *ab; int i; + if (ath12k_ftm_mode) + return; + lockdep_assert_held(&ag->mutex); /* If more than one devices are grouped, then inter MLO From ca89620328d2d8a8ca76b1ebeeed5353f9a7fadb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 019/465] wifi: ath12k: fix skb_ext_desc leak in ath12k_dp_tx() error path JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 28a9972e0f0693cd4d08f431c992fa6be39c788c Author: Nicolas Escande Date: Wed Jan 22 17:01:12 2025 +0100 wifi: ath12k: fix skb_ext_desc leak in ath12k_dp_tx() error path When vlan support was added, we missed that when ath12k_dp_prepare_htt_metadata() returns an error we also need to free the skb holding the metadata before going on with the cleanup process. Compile tested only. Fixes: 26dd8ccdba4d ("wifi: ath12k: dynamic VLAN support") Signed-off-by: Nicolas Escande Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250122160112.3234558-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index a8d341a6df01..e0b85f959cd4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -398,6 +398,7 @@ map: if (ret < 0) { ath12k_dbg(ab, ATH12K_DBG_DP_TX, "Failed to add HTT meta data, dropping packet\n"); + kfree_skb(skb_ext_desc); goto fail_unmap_dma; } } From 2ca01b719760898360705b570b8d3c5a83bb0adc Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 020/465] wifi: ath12k: report station mode transmit rate JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8a9c06b40882ebea45563059ddc5d57acdec9dda Author: Lingbo Kong Date: Wed Jan 15 14:35:35 2025 +0800 wifi: ath12k: report station mode transmit rate Currently, the transmit rate of "iw dev xxx station dump" command always show an invalid value. To address this issue, ath12k parse the info of transmit complete report from firmware and indicate the transmit rate to mac80211. This patch affects the station mode of WCN7850 and QCN9274. After that, "iw dev xxx station dump" show the correct transmit rate. Such as: Station 00:03:7f:12:03:03 (on wlo1) inactive time: 872 ms rx bytes: 219111 rx packets: 1133 tx bytes: 53767 tx packets: 462 tx retries: 51 tx failed: 0 beacon loss: 0 beacon rx: 403 rx drop misc: 74 signal: -95 dBm beacon signal avg: -18 dBm tx bitrate: 1441.1 MBit/s 80MHz EHT-MCS 13 EHT-NSS 2 EHT-GI 0 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219715 Link: https://patch.msgid.link/20250115063537.35797-2-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 2 + drivers/net/wireless/ath/ath12k/dp_rx.h | 5 +- drivers/net/wireless/ath/ath12k/dp_tx.c | 139 +++++++++++++++++++++- drivers/net/wireless/ath/ath12k/hal_rx.h | 5 +- drivers/net/wireless/ath/ath12k/hal_tx.h | 10 +- drivers/net/wireless/ath/ath12k/mac.c | 79 ++++++++++++ drivers/net/wireless/ath/ath12k/mac.h | 5 +- drivers/net/wireless/ath/ath12k/rx_desc.h | 3 +- 8 files changed, 238 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 86a1eeec64a6..95c2ff6a9ae2 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -87,6 +87,7 @@ enum wme_ac { #define ATH12K_HT_MCS_MAX 7 #define ATH12K_VHT_MCS_MAX 9 #define ATH12K_HE_MCS_MAX 11 +#define ATH12K_EHT_MCS_MAX 15 enum ath12k_crypt_mode { /* Only use hardware crypto engine */ @@ -501,6 +502,7 @@ struct ath12k_link_sta { struct ath12k_rx_peer_stats *rx_stats; struct ath12k_wbm_tx_stats *wbm_tx_stats; u32 bw_prev; + u32 peer_nss; /* For now the assoc link will be considered primary */ bool is_assoc_link; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 1ce82088c954..c0aa965f47e7 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_RX_H #define ATH12K_DP_RX_H @@ -79,6 +79,9 @@ static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) case RX_MSDU_START_SGI_3_2_US: ret = NL80211_RATE_INFO_HE_GI_3_2; break; + default: + ret = NL80211_RATE_INFO_HE_GI_0_8; + break; } return ret; diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index e0b85f959cd4..de8bb9c1533e 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -8,6 +8,8 @@ #include "dp_tx.h" #include "debug.h" #include "hw.h" +#include "peer.h" +#include "mac.h" static enum hal_tcl_encap_type ath12k_dp_tx_get_encap_type(struct ath12k_link_vif *arvif, struct sk_buff *skb) @@ -581,6 +583,124 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, } } +static void ath12k_dp_tx_update_txcompl(struct ath12k *ar, struct hal_tx_status *ts) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_peer *peer; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + struct rate_info txrate = {0}; + u16 rate, ru_tones; + u8 rate_idx = 0; + int ret; + + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DP_TX, + "failed to find the peer by id %u\n", ts->peer_id); + spin_unlock_bh(&ab->base_lock); + return; + } + sta = peer->sta; + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + + /* This is to prefer choose the real NSS value arsta->last_txrate.nss, + * if it is invalid, then choose the NSS value while assoc. + */ + if (arsta->last_txrate.nss) + txrate.nss = arsta->last_txrate.nss; + else + txrate.nss = arsta->peer_nss; + spin_unlock_bh(&ab->base_lock); + + switch (ts->pkt_type) { + case HAL_TX_RATE_STATS_PKT_TYPE_11A: + case HAL_TX_RATE_STATS_PKT_TYPE_11B: + ret = ath12k_mac_hw_ratecode_to_legacy_rate(ts->mcs, + ts->pkt_type, + &rate_idx, + &rate); + if (ret < 0) { + ath12k_warn(ab, "Invalid tx legacy rate %d\n", ret); + return; + } + + txrate.legacy = rate; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11N: + if (ts->mcs > ATH12K_HT_MCS_MAX) { + ath12k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs); + return; + } + + if (txrate.nss != 0) + txrate.mcs = ts->mcs + 8 * (txrate.nss - 1); + + txrate.flags = RATE_INFO_FLAGS_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AC: + if (ts->mcs > ATH12K_VHT_MCS_MAX) { + ath12k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AX: + if (ts->mcs > ATH12K_HE_MCS_MAX) { + ath12k_warn(ab, "Invalid HE mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_HE_MCS; + txrate.he_gi = ath12k_he_gi_to_nl80211_he_gi(ts->sgi); + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11BE: + if (ts->mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ab, "Invalid EHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_EHT_MCS; + txrate.eht_gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(ts->sgi); + break; + default: + ath12k_warn(ab, "Invalid tx pkt type: %d\n", ts->pkt_type); + return; + } + + txrate.bw = ath12k_mac_bw_to_mac80211_bw(ts->bw); + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + txrate.bw = RATE_INFO_BW_HE_RU; + ru_tones = ath12k_mac_he_convert_tones_to_ru_tones(ts->tones); + txrate.he_ru_alloc = + ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); + } + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11BE) { + txrate.bw = RATE_INFO_BW_EHT_RU; + txrate.eht_ru_alloc = + ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(ts->tones); + } + + spin_lock_bh(&ab->base_lock); + arsta->txrate = txrate; + spin_unlock_bh(&ab->base_lock); +} + static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, struct sk_buff *msdu, struct hal_tx_status *ts) @@ -659,6 +779,8 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, * Might end up reporting it out-of-band from HTT stats. */ + ath12k_dp_tx_update_txcompl(ar, ts); + ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu); exit: @@ -669,6 +791,8 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, struct hal_wbm_completion_ring_tx *desc, struct hal_tx_status *ts) { + u32 info0 = le32_to_cpu(desc->rate_stats.info0); + ts->buf_rel_source = le32_get_bits(desc->info0, HAL_WBM_COMPL_TX_INFO0_REL_SRC_MODULE); if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && @@ -683,10 +807,17 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, ts->ppdu_id = le32_get_bits(desc->info1, HAL_WBM_COMPL_TX_INFO1_TQM_STATUS_NUMBER); - if (le32_to_cpu(desc->rate_stats.info0) & HAL_TX_RATE_STATS_INFO0_VALID) - ts->rate_stats = le32_to_cpu(desc->rate_stats.info0); - else - ts->rate_stats = 0; + + ts->peer_id = le32_get_bits(desc->info3, HAL_WBM_COMPL_TX_INFO3_PEER_ID); + + if (info0 & HAL_TX_RATE_STATS_INFO0_VALID) { + ts->pkt_type = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_PKT_TYPE); + ts->mcs = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_MCS); + ts->sgi = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_SGI); + ts->bw = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_BW); + ts->tones = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_TONES_IN_RU); + ts->ofdma = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_OFDMA_TX); + } } void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index b08aa2e79f41..04e9f5f8e83a 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HAL_RX_H @@ -662,6 +662,9 @@ enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) case RU_996: ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; break; + case RU_2X996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; case RU_26: fallthrough; default: diff --git a/drivers/net/wireless/ath/ath12k/hal_tx.h b/drivers/net/wireless/ath/ath12k/hal_tx.h index 3cf5973771d7..eb065a79f6c6 100644 --- a/drivers/net/wireless/ath/ath12k/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/hal_tx.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. + * All rights reserved. */ #ifndef ATH12K_HAL_TX_H @@ -63,7 +64,12 @@ struct hal_tx_status { u8 try_cnt; u8 tid; u16 peer_id; - u32 rate_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum ath12k_supported_bw bw; + u8 mcs; + u16 tones; + u8 ofdma; }; #define HAL_TX_PHY_DESC_INFO0_BF_TYPE GENMASK(17, 16) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 329c05003721..26c5b0284429 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -338,6 +338,82 @@ static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode) return ""; } +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones) +{ + switch (tones) { + case 26: + return RU_26; + case 52: + return RU_52; + case 106: + return RU_106; + case 242: + return RU_242; + case 484: + return RU_484; + case 996: + return RU_996; + case (996 * 2): + return RU_2X996; + default: + return RU_26; + } +} + +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi) +{ + switch (sgi) { + case RX_MSDU_START_SGI_0_8_US: + return NL80211_RATE_INFO_EHT_GI_0_8; + case RX_MSDU_START_SGI_1_6_US: + return NL80211_RATE_INFO_EHT_GI_1_6; + case RX_MSDU_START_SGI_3_2_US: + return NL80211_RATE_INFO_EHT_GI_3_2; + default: + return NL80211_RATE_INFO_EHT_GI_0_8; + } +} + +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones) +{ + switch (ru_tones) { + case 26: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + case 52: + return NL80211_RATE_INFO_EHT_RU_ALLOC_52; + case (52 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + case 106: + return NL80211_RATE_INFO_EHT_RU_ALLOC_106; + case (106 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + case 242: + return NL80211_RATE_INFO_EHT_RU_ALLOC_242; + case 484: + return NL80211_RATE_INFO_EHT_RU_ALLOC_484; + case (484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + case 996: + return NL80211_RATE_INFO_EHT_RU_ALLOC_996; + case (996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + case (996 + 484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + case (2 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + case (2 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + case (3 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + case (3 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + case (4 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + default: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + } +} + enum rate_info_bw ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw) { @@ -3120,6 +3196,7 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar, ath12k_peer_assoc_h_smps(arsta, arg); ath12k_peer_assoc_h_mlo(arsta, arg); + arsta->peer_nss = arg->peer_nss; /* TODO: amsdu_disable req? */ } @@ -10057,6 +10134,8 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->txrate.he_gi = arsta->txrate.he_gi; sinfo->txrate.he_dcm = arsta->txrate.he_dcm; sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + sinfo->txrate.eht_gi = arsta->txrate.eht_gi; + sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_ru_alloc; } sinfo->txrate.flags = arsta->txrate.flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 3594729b6397..1acaf3f68292 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_MAC_H @@ -108,5 +108,8 @@ int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif); void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf, void *data); +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones); +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones); +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi); #endif diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 10366bbe9999..777506a67d65 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_RX_DESC_H #define ATH12K_RX_DESC_H @@ -1546,5 +1546,6 @@ struct hal_rx_desc { #define RU_242 9 #define RU_484 18 #define RU_996 37 +#define RU_2X996 74 #endif /* ATH12K_RX_DESC_H */ From e5fed1271499fccf08fd28dde2a3a72dacd73949 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 021/465] wifi: ath12k: report station mode receive rate for IEEE 802.11be JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5e73276c814fc1a5a1bce6be743e1a07baa6d4bc Author: Lingbo Kong Date: Wed Jan 15 14:35:36 2025 +0800 wifi: ath12k: report station mode receive rate for IEEE 802.11be Currently, the receive rate of EHT of "iw dev xxx station dump" command always show an invalid value. This is because ath12k does not pass information about the rx_status of EHT to mac80211. So, mac80211 not calculate the receive rate. To address this issue, add logic for handling rx_status of EHT to the ath12k_dp_rx_h_rate() function. After that, "iw dev xxx station dump" show the correct receive rate. Such as: Station 00:03:7f:12:03:03 (on wlo1) inactive time: 48 ms rx bytes: 59226 rx packets: 320 tx bytes: 26556 tx packets: 191 tx retries: 99 tx failed: 0 beacon loss: 0 beacon rx: 79 rx drop misc: 68 signal: -95 dBm beacon signal avg: -20 dBm tx bitrate: 688.2 MBit/s 40MHz EHT-MCS 13 EHT-NSS 2 EHT-GI 0 tx duration: 0 us rx bitrate: 619.5 MBit/s 40MHz EHT-MCS 8 EHT-NSS 3 EHT-GI 0 This patch affects the station mode of WCN7850 and QCN9274. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219715 Link: https://patch.msgid.link/20250115063537.35797-3-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_rx.c | 22 ++++++++++++++++++++-- drivers/net/wireless/ath/ath12k/rx_desc.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index a7fd83699656..a0d67ee3a5dc 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2392,6 +2392,23 @@ static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc, rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); break; + case RX_MSDU_START_PKT_TYPE_11BE: + rx_status->rate_idx = rate_mcs; + + if (rate_mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in EHT mode %d\n", + rate_mcs); + break; + } + + rx_status->encoding = RX_ENC_EHT; + rx_status->nss = nss; + rx_status->eht.gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi); + rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); + break; + default: + break; } } @@ -2486,7 +2503,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap spin_unlock_bh(&ab->base_lock); ath12k_dbg(ab, ATH12K_DBG_DATA, - "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", msdu, msdu->len, peer ? peer->addr : NULL, @@ -2497,6 +2514,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap (status->encoding == RX_ENC_HT) ? "ht" : "", (status->encoding == RX_ENC_VHT) ? "vht" : "", (status->encoding == RX_ENC_HE) ? "he" : "", + (status->encoding == RX_ENC_EHT) ? "eht" : "", (status->bw == RATE_INFO_BW_40) ? "40" : "", (status->bw == RATE_INFO_BW_80) ? "80" : "", (status->bw == RATE_INFO_BW_160) ? "160" : "", diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 777506a67d65..7367935ee68b 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -637,6 +637,8 @@ enum rx_msdu_start_pkt_type { RX_MSDU_START_PKT_TYPE_11N, RX_MSDU_START_PKT_TYPE_11AC, RX_MSDU_START_PKT_TYPE_11AX, + RX_MSDU_START_PKT_TYPE_11BA, + RX_MSDU_START_PKT_TYPE_11BE, }; enum rx_msdu_start_sgi { From 66133337e507545e9a056b6dea2bf760f03bf78a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 022/465] wifi: ath12k: report station mode signal strength JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 79e7b04b5388c13290ae6d64f930b096c2f465c9 Author: Lingbo Kong Date: Wed Jan 15 14:35:37 2025 +0800 wifi: ath12k: report station mode signal strength Currently, the signal strength of "iw dev xxx station dump" always show an invalid value. This is because signal strength is only set in ath12k_mgmt_rx_event() function, and not set for received data packet. So, change to get signal from firmware and report to mac80211. After that, "iw dev xxx station dump" show the correct signal strength. Such as: Station 00:03:7f:12:03:03 (on wlo1) inactive time: 36 ms rx bytes: 61571 rx packets: 336 tx bytes: 28204 tx packets: 205 tx retries: 49 tx failed: 0 beacon loss: 0 beacon rx: 83 rx drop misc: 66 signal: -24 dBm beacon signal avg: -22 dBm For WCN7850, the firmware supports db2dbm, so not need to add noise floor. For QCN9274, the firmware not support db2dbm, so need to add noise floor. This patch affects the station mode of WCN7850 and QCN9274. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Link: https://patch.msgid.link/20250115063537.35797-4-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 3 + drivers/net/wireless/ath/ath12k/mac.c | 59 ++++++++++- drivers/net/wireless/ath/ath12k/wmi.c | 132 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 48 +++++++++ 4 files changed, 240 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 95c2ff6a9ae2..e036f2e00eb7 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -503,6 +503,7 @@ struct ath12k_link_sta { struct ath12k_wbm_tx_stats *wbm_tx_stats; u32 bw_prev; u32 peer_nss; + s8 rssi_beacon; /* For now the assoc link will be considered primary */ bool is_assoc_link; @@ -722,6 +723,8 @@ struct ath12k { bool nlo_enabled; + struct completion fw_stats_complete; + struct completion mlo_setup_done; u32 mlo_setup_status; u8 ftm_msgref; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 26c5b0284429..a245eb6293fb 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10104,6 +10104,40 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } +static int ath12k_mac_get_fw_stats(struct ath12k *ar, u32 pdev_id, + u32 vdev_id, u32 stats_id) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + unsigned long time_left; + int ret; + + guard(mutex)(&ah->hw_mutex); + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + reinit_completion(&ar->fw_stats_complete); + + ret = ath12k_wmi_send_stats_request_cmd(ar, stats_id, vdev_id, pdev_id); + + if (ret) { + ath12k_warn(ab, "failed to request fw stats: %d\n", ret); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "get fw stat pdev id %d vdev id %d stats id 0x%x\n", + pdev_id, vdev_id, stats_id); + + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); + + if (!time_left) + ath12k_warn(ab, "time out while waiting for get fw stats\n"); + + return ret; +} + static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -10111,10 +10145,19 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, { struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); struct ath12k_link_sta *arsta; + struct ath12k *ar; + s8 signal; + bool db2dbm; lockdep_assert_wiphy(hw->wiphy); arsta = &ahsta->deflink; + ar = ath12k_get_ar_by_vif(hw, vif, arsta->link_id); + if (!ar) + return; + + db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ar->ab->wmi_ab.svc_map); sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); @@ -10141,8 +10184,18 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); /* TODO: Use real NF instead of default one. */ - sinfo->signal = arsta->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + signal = arsta->rssi_comb; + + if (!signal && + ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA && + !(ath12k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_VDEV_STAT))) + signal = arsta->rssi_beacon; + + if (signal) { + sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } } static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, @@ -11123,6 +11176,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_debugfs_register(ar); } + init_completion(&ar->fw_stats_complete); + return 0; err_unregister_hw: diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 296238c7ee0f..a944bfed763f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -26,6 +26,10 @@ struct ath12k_wmi_svc_ready_parse { bool wmi_svc_bitmap_done; }; +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; +}; + struct ath12k_wmi_dma_ring_caps_parse { struct ath12k_wmi_dma_ring_caps_params *dma_ring_caps; u32 n_dma_ring_caps; @@ -815,6 +819,39 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, return ret; } +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_REQUEST_STATS_CMD, + sizeof(*cmd)); + + cmd->stats_id = cpu_to_le32(stats_id); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pdev_id = cpu_to_le32(pdev_id); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n"); + dev_kfree_skb(skb); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI request stats 0x%x vdev id %d pdev id %d\n", + stats_id, vdev_id, pdev_id); + + return ret; +} + int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, struct ath12k_wmi_vdev_create_arg *args) { @@ -6843,8 +6880,103 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff rcu_read_unlock(); } +static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, + struct wmi_tlv_fw_stats_parse *parse, + const void *ptr, + u16 len) +{ + const struct wmi_stats_event *ev = parse->ev; + struct ath12k *ar; + struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + int i, ret = 0; + const void *data = ptr; + + if (!ev) { + ath12k_warn(ab, "failed to fetch update stats ev"); + return -EPROTO; + } + + rcu_read_lock(); + + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id)); + if (!ar) { + ath12k_warn(ab, "invalid pdev id %d in update stats event\n", + le32_to_cpu(ev->pdev_id)); + ret = -EPROTO; + goto exit; + } + + for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) { + const struct wmi_vdev_stats_params *src; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + arvif = ath12k_mac_get_arvif(ar, le32_to_cpu(src->vdev_id)); + if (arvif) { + sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), + arvif->bssid, + NULL); + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + arsta->rssi_beacon = le32_to_cpu(src->beacon_snr); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "wmi stats vdev id %d snr %d\n", + src->vdev_id, src->beacon_snr); + } else { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "not found station bssid %pM for vdev stat\n", + arvif->bssid); + } + } + + data += sizeof(*src); + len -= sizeof(*src); + } + + complete(&ar->fw_stats_complete); +exit: + rcu_read_unlock(); + return ret; +} + +static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_fw_stats_parse *parse = data; + int ret = 0; + + switch (tag) { + case WMI_TAG_STATS_EVENT: + parse->ev = ptr; + break; + case WMI_TAG_ARRAY_BYTE: + ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); + break; + default: + break; + } + return ret; +} + static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb) { + int ret; + struct wmi_tlv_fw_stats_parse parse = {}; + + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_fw_stats_parse, + &parse); + if (ret) + ath12k_warn(ab, "failed to parse fw stats %d\n", ret); } /* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 81248253a368..afa2832ba282 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5648,6 +5648,52 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +struct wmi_stats_event { + __le32 stats_id; + __le32 num_pdev_stats; + __le32 num_vdev_stats; + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + __le32 num_chan_stats; + __le32 num_mib_stats; + __le32 pdev_id; + __le32 num_bcn_stats; + __le32 num_peer_extd_stats; + __le32 num_peer_extd2_stats; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_VDEV_STAT = BIT(3), +}; + +struct wmi_request_stats_cmd { + __le32 tlv_header; + __le32 stats_id; + __le32 vdev_id; + struct ath12k_wmi_mac_addr_params peer_macaddr; + __le32 pdev_id; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats_params { + __le32 vdev_id; + __le32 beacon_snr; + __le32 data_snr; + __le32 num_tx_frames[WLAN_MAX_AC]; + __le32 num_rx_frames; + __le32 num_tx_frames_retries[WLAN_MAX_AC]; + __le32 num_tx_frames_failures[WLAN_MAX_AC]; + __le32 num_rts_fail; + __le32 num_rts_success; + __le32 num_rx_err; + __le32 num_rx_discard; + __le32 num_tx_not_acked; + __le32 tx_rate_history[MAX_TX_RATE_VALUES]; + __le32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5773,6 +5819,8 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, const u8 *buf, size_t buf_len); int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id); __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); static inline u32 From a298fa5dd0627a946187d436b5239a7680b7d322 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 023/465] wifi: ath11k: fix memory leak in ath11k_xxx_remove() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit efb24b1f0d29537714dd3cc46fb335ac27855251 Author: Miaoqing Pan Date: Thu Jan 23 16:49:48 2025 +0800 wifi: ath11k: fix memory leak in ath11k_xxx_remove() The firmware memory was allocated in ath11k_pci_probe() or ath11k_ahb_probe(), but not freed in ath11k_xxx_remove() in case ATH11K_FLAG_QMI_FAIL bit is set. So call ath11k_fw_destroy() to free the memory. Found while fixing the same problem in ath12k: https://lore.kernel.org/linux-wireless/20240314012746.2729101-1-quic_miaoqing@quicinc.com Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04546-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Signed-off-by: Miaoqing Pan Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250123084948.1124357-1-quic_miaoqing@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/ahb.c | 4 +++- drivers/net/wireless/ath/ath11k/core.c | 3 +-- drivers/net/wireless/ath/ath11k/fw.c | 3 ++- drivers/net/wireless/ath/ath11k/pci.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 916402ad06b8..f22b6297c6ef 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1290,6 +1290,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev) ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } @@ -1309,6 +1310,7 @@ static void ath11k_ahb_shutdown(struct platform_device *pdev) ath11k_core_deinit(ab); free_resources: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index c576bbba52bf..85077247b025 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2346,7 +2346,6 @@ void ath11k_core_deinit(struct ath11k_base *ab) ath11k_hif_power_down(ab); ath11k_mac_destroy(ab); ath11k_core_soc_destroy(ab); - ath11k_fw_destroy(ab); } EXPORT_SYMBOL(ath11k_core_deinit); diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c index 4e36292a79db..cbbd8e57119f 100644 --- a/drivers/net/wireless/ath/ath11k/fw.c +++ b/drivers/net/wireless/ath/ath11k/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -166,3 +166,4 @@ void ath11k_fw_destroy(struct ath11k_base *ab) { release_firmware(ab->fw.fw); } +EXPORT_SYMBOL(ath11k_fw_destroy); diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index b93f04973ad7..f1121e331719 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -984,6 +984,7 @@ static void ath11k_pci_remove(struct pci_dev *pdev) ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_mhi_unregister(ab_pci); ath11k_pcic_free_irq(ab); From be1ee435ef0d54b37266a860335698fc876237b2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:24 +0200 Subject: [PATCH 024/465] wifi: ath12k: encode max Tx power in scan channel list command JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 07c34cad10ab0ac8b06ede8a7fbc55ecf2efa3e6 Author: Sathishkumar Muruganandam Date: Tue Jan 7 09:31:39 2025 +0530 wifi: ath12k: encode max Tx power in scan channel list command Currently, when sending the scan channel list command to the firmware, the maximum Tx power is not encoded in the reg2 member. This omission causes the firmware to be unaware of the host's maximum Tx power, leading to incorrect Tx power derivation at firmware level. To resolve this issue, encode the maximum Tx power in the scan channel list command before sending it to firmware. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250107-add_max_reg_pwr_in_scan_ch_list_cmd-v1-1-70d9963a21e4@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/wmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index a944bfed763f..ba6f5afd81e3 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -2832,6 +2832,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar, WMI_CHAN_REG_INFO1_REG_CLS); *reg2 |= le32_encode_bits(channel_arg->antennamax, WMI_CHAN_REG_INFO2_ANT_MAX); + *reg2 |= le32_encode_bits(channel_arg->maxregpower, + WMI_CHAN_REG_INFO2_MAX_TX_PWR); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n", From e78789295ac0f575958d00f982fa99446c4e2d2b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 025/465] wifi: ath12k: fix memory leak in ath12k_pci_remove() JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37744 commit 1b24394ed5c8a8d8f7b9e3aa9044c31495d46f2e Author: Miaoqing Pan Date: Thu Jan 23 16:02:26 2025 +0800 wifi: ath12k: fix memory leak in ath12k_pci_remove() Kmemleak reported this error: unreferenced object 0xffff1c165cec3060 (size 32): comm "insmod", pid 560, jiffies 4296964570 (age 235.596s) backtrace: [<000000005434db68>] __kmem_cache_alloc_node+0x1f4/0x2c0 [<000000001203b155>] kmalloc_trace+0x40/0x88 [<0000000028adc9c8>] _request_firmware+0xb8/0x608 [<00000000cad1aef7>] firmware_request_nowarn+0x50/0x80 [<000000005011a682>] local_pci_probe+0x48/0xd0 [<00000000077cd295>] pci_device_probe+0xb4/0x200 [<0000000087184c94>] really_probe+0x150/0x2c0 The firmware memory was allocated in ath12k_pci_probe(), but not freed in ath12k_pci_remove() in case ATH12K_FLAG_QMI_FAIL bit is set. So call ath12k_fw_unmap() to free the memory. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.2.0-02280-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Signed-off-by: Miaoqing Pan Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250123080226.1116479-1-quic_miaoqing@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 4a50d7118b3e..6c6a3cabab32 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1735,9 +1735,9 @@ static void ath12k_pci_remove(struct pci_dev *pdev) cancel_work_sync(&ab->reset_work); cancel_work_sync(&ab->dump_work); ath12k_core_deinit(ab); - ath12k_fw_unmap(ab); qmi_fail: + ath12k_fw_unmap(ab); ath12k_mhi_unregister(ab_pci); ath12k_pci_free_irq(ab); From 5c2a6e04381844ab63c4e81ced8982d4d57813ac Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 026/465] wifi: ath12k: Request vdev stats from firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e367c924768b11d28727efbf2a6426c7aef66f0f Author: Ramya Gnanasekar Date: Sat Jan 25 00:23:28 2025 +0530 wifi: ath12k: Request vdev stats from firmware Add support to request and print vdev stats from firmware through WMI. Sample output: ------------- cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/fw_stats/vdev_stats ath12k VDEV stats ================= VDEV ID 0 VDEV MAC address 00:03:7f:6c:9c:1a beacon snr 96 data snr 255 num rx frames 0 num rts fail 0 num rts success 0 num rx err 0 num rx discard 0 num tx not acked 0 num tx frames [00] 0 num tx frames [01] 0 num tx frames [02] 0 num tx frames [03] 2 num tx frames retries [00] 0 num tx frames retries [01] 0 num tx frames retries [02] 0 num tx frames retries [03] 0 num tx frames failures [00] 0 num tx frames failures [01] 0 num tx frames failures [02] 0 num tx frames failures [03] 0 tx rate history [00] 0x00000000 tx rate history [01] 0x00000000 tx rate history [02] 0x00000000 tx rate history [03] 0x00000000 tx rate history [04] 0x00000000 tx rate history [05] 0x00000000 tx rate history [06] 0x00000000 tx rate history [07] 0x00000000 tx rate history [08] 0x00000000 tx rate history [09] 0x00000000 beacon rssi history [00] 0 beacon rssi history [01] 0 beacon rssi history [02] 0 beacon rssi history [03] 0 beacon rssi history [04] 0 beacon rssi history [05] 0 beacon rssi history [06] 0 beacon rssi history [07] 0 beacon rssi history [08] 0 beacon rssi history [09] 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Ramya Gnanasekar Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124185330.1244585-2-ramya.gnanasekar@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 21 +++ drivers/net/wireless/ath/ath12k/debugfs.c | 197 +++++++++++++++++++++- drivers/net/wireless/ath/ath12k/debugfs.h | 13 +- drivers/net/wireless/ath/ath12k/wmi.c | 155 +++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 10 ++ 5 files changed, 394 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index e036f2e00eb7..97939ddb3cd7 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -557,6 +557,7 @@ struct ath12k_fw_stats { struct list_head pdevs; struct list_head vdevs; struct list_head bcn; + bool fw_stats_done; }; struct ath12k_dbg_htt_stats { @@ -728,6 +729,7 @@ struct ath12k { struct completion mlo_setup_done; u32 mlo_setup_status; u8 ftm_msgref; + struct ath12k_fw_stats fw_stats; }; struct ath12k_hw { @@ -1078,6 +1080,25 @@ struct ath12k_pdev_map { u8 pdev_idx; }; +struct ath12k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index d4b32d1a431c..1ee9a3e00514 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" +#include "debug.h" #include "debugfs.h" #include "debugfs_htt_stats.h" @@ -68,6 +69,199 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) */ } +static void ath12k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) +{ + spin_lock_bh(&ar->data_lock); + ar->fw_stats.fw_stats_done = false; + ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); + spin_unlock_bh(&ar->data_lock); +} + +static int ath12k_debugfs_fw_stats_request(struct ath12k *ar, + struct ath12k_fw_stats_req_params *param) +{ + struct ath12k_base *ab = ar->ab; + unsigned long timeout, time_left; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + timeout = jiffies + msecs_to_jiffies(3 * 1000); + + ath12k_debugfs_fw_stats_reset(ar); + + reinit_completion(&ar->fw_stats_complete); + + ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, + param->vdev_id, param->pdev_id); + + if (ret) { + ath12k_warn(ab, "could not request fw stats (%d)\n", + ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, + 1 * HZ); + /* If the wait timed out, return -ETIMEDOUT */ + if (!time_left) + return -ETIMEDOUT; + + /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back + * when stats data buffer limit is reached. fw_stats_complete + * is completed once host receives first event from firmware, but + * still end might not be marked in the TLV. + * Below loop is to confirm that firmware completed sending all the event + * and fw_stats_done is marked true when end is marked in the TLV + */ + for (;;) { + if (time_after(jiffies, timeout)) + break; + + spin_lock_bh(&ar->data_lock); + if (ar->fw_stats.fw_stats_done) { + spin_unlock_bh(&ar->data_lock); + break; + } + spin_unlock_bh(&ar->data_lock); + } + return 0; +} + +void +ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_pdev *pdev; + bool is_end; + static unsigned int num_vdev; + size_t total_vdevs_started = 0; + int i; + + if (stats->stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats->vdevs)) { + ath12k_warn(ab, "empty vdev stats"); + return; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + rcu_read_lock(); + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += pdev->ar->num_started_vdevs; + } + rcu_read_unlock(); + + is_end = ((++num_vdev) == total_vdevs_started); + + list_splice_tail_init(&stats->vdevs, + &ar->fw_stats.vdevs); + + if (is_end) { + ar->fw_stats.fw_stats_done = true; + num_vdev = 0; + } + return; + } +} + +static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (!ah) + return -ENETDOWN; + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + /* VDEV stats is always sent for all active VDEVs from FW */ + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath12k_debugfs_fw_stats_request(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_vdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath12k_open_vdev_stats, + .release = ath12k_release_vdev_stats, + .read = ath12k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static +void ath12k_debugfs_fw_stats_register(struct ath12k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + + INIT_LIST_HEAD(&ar->fw_stats.vdevs); + init_completion(&ar->fw_stats_complete); +} + void ath12k_debugfs_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -92,6 +286,7 @@ void ath12k_debugfs_register(struct ath12k *ar) } ath12k_debugfs_htt_stats_register(ar); + ath12k_debugfs_fw_stats_register(ar); } void ath12k_debugfs_unregister(struct ath12k *ar) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 8d64ba03aa9a..1c30745ee415 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUGFS_H_ @@ -12,6 +12,9 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab); void ath12k_debugfs_soc_destroy(struct ath12k_base *ab); void ath12k_debugfs_register(struct ath12k *ar); void ath12k_debugfs_unregister(struct ath12k *ar); +void ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats); +void ath12k_debugfs_fw_stats_reset(struct ath12k *ar); #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { @@ -29,6 +32,14 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar) { } +static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats) +{ +} + +static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) +{ +} #endif /* CONFIG_ATH12K_DEBUGFS */ #endif /* _ATH12K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index ba6f5afd81e3..b91b4767515a 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -15,6 +15,7 @@ #include #include #include "core.h" +#include "debugfs.h" #include "debug.h" #include "mac.h" #include "hw.h" @@ -6882,12 +6883,156 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff rcu_read_unlock(); } +static void +ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_vdev *vdev; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + u8 *vif_macaddr; + int i; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k VDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + arvif = ath12k_mac_get_arvif(ar, vdev->vdev_id); + if (!arvif) + continue; + vif_macaddr = arvif->ahvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vif_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + u32 stats_id, char *buf) +{ + u32 len = 0; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + spin_lock_bh(&ar->data_lock); + + switch (stats_id) { + case WMI_REQUEST_VDEV_STAT: + ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len); + break; + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; + + ath12k_debugfs_fw_stats_reset(ar); +} + +static void +ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src, + struct ath12k_fw_stats_vdev *dst) +{ + int i; + + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->beacon_snr = le32_to_cpu(src->beacon_snr); + dst->data_snr = le32_to_cpu(src->data_snr); + dst->num_rx_frames = le32_to_cpu(src->num_rx_frames); + dst->num_rts_fail = le32_to_cpu(src->num_rts_fail); + dst->num_rts_success = le32_to_cpu(src->num_rts_success); + dst->num_rx_err = le32_to_cpu(src->num_rx_err); + dst->num_rx_discard = le32_to_cpu(src->num_rx_discard); + dst->num_tx_not_acked = le32_to_cpu(src->num_tx_not_acked); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames[i] = + le32_to_cpu(src->num_tx_frames[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_retries[i] = + le32_to_cpu(src->num_tx_frames_retries[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_failures[i] = + le32_to_cpu(src->num_tx_frames_failures[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->tx_rate_history[i] = + le32_to_cpu(src->tx_rate_history[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->beacon_rssi_history[i] = + le32_to_cpu(src->beacon_rssi_history[i]); +} + static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, struct wmi_tlv_fw_stats_parse *parse, const void *ptr, u16 len) { const struct wmi_stats_event *ev = parse->ev; + struct ath12k_fw_stats stats = {0}; struct ath12k *ar; struct ath12k_link_vif *arvif; struct ieee80211_sta *sta; @@ -6896,6 +7041,8 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, int i, ret = 0; const void *data = ptr; + INIT_LIST_HEAD(&stats.vdevs); + if (!ev) { ath12k_warn(ab, "failed to fetch update stats ev"); return -EPROTO; @@ -6913,6 +7060,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) { const struct wmi_vdev_stats_params *src; + struct ath12k_fw_stats_vdev *dst; src = data; if (len < sizeof(*src)) { @@ -6941,9 +7089,16 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, data += sizeof(*src); len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_vdev_stats(src, dst); + stats.stats_id = WMI_REQUEST_VDEV_STAT; + list_add_tail(&dst->list, &stats.vdevs); } complete(&ar->fw_stats_complete); + ath12k_debugfs_fw_stats_process(ar, &stats); exit: rcu_read_unlock(); return ret; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index afa2832ba282..1c9500594f1d 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -25,6 +25,7 @@ struct ath12k_base; struct ath12k; struct ath12k_link_vif; +struct ath12k_fw_stats; /* There is no signed version of __le32, so for a temporary solution come * up with our own version. The idea is from fs/ntfs/endian.h. @@ -5694,6 +5695,12 @@ struct wmi_vdev_stats_params { __le32 beacon_rssi_history[MAX_TX_RATE_VALUES]; } __packed; +struct ath12k_fw_stats_req_params { + u32 stats_id; + u32 vdev_id; + u32 pdev_id; +}; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5875,5 +5882,8 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params); int ath12k_wmi_mlo_ready(struct ath12k *ar); int ath12k_wmi_mlo_teardown(struct ath12k *ar); +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, u32 stats_id, + char *buf); #endif From e065934d8fda1decb171511a13ef7f49632a1023 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 027/465] wifi: ath12k: Request beacon stats from firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9fe4669ae9193b27d1d4add9a26c82dca9d59da0 Author: Ramya Gnanasekar Date: Sat Jan 25 00:23:29 2025 +0530 wifi: ath12k: Request beacon stats from firmware Add support to request and dump beacon statistics from firmware Sample output: ------------- cat /sys/kernel/debug/ath12k/pci-0000:06:00.0/mac0/fw_stats/beacon_stats ath12k Beacon stats (1) =================== VDEV ID 0 VDEV MAC address 00:03:7f:04:37:58 ================ Num of beacon tx success 20 Num of beacon tx failures 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Ramya Gnanasekar Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124185330.1244585-3-ramya.gnanasekar@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 8 ++ drivers/net/wireless/ath/ath12k/debugfs.c | 106 +++++++++++++++++++++- drivers/net/wireless/ath/ath12k/wmi.c | 71 +++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 7 ++ 4 files changed, 191 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 97939ddb3cd7..7159343113d6 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1099,6 +1099,14 @@ struct ath12k_fw_stats_vdev { u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; }; +struct ath12k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 1ee9a3e00514..3cb2bb9fa424 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -69,6 +69,16 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) */ } +static void ath12k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath12k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + static void ath12k_fw_stats_vdevs_free(struct list_head *head) { struct ath12k_fw_stats_vdev *i, *tmp; @@ -84,6 +94,7 @@ void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) spin_lock_bh(&ar->data_lock); ar->fw_stats.fw_stats_done = false; ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); + ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn); spin_unlock_bh(&ar->data_lock); } @@ -150,7 +161,7 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar, struct ath12k_base *ab = ar->ab; struct ath12k_pdev *pdev; bool is_end; - static unsigned int num_vdev; + static unsigned int num_vdev, num_bcn; size_t total_vdevs_started = 0; int i; @@ -181,6 +192,24 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar, } return; } + if (stats->stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats->bcn)) { + ath12k_warn(ab, "empty beacon stats"); + return; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + is_end = ((++num_bcn) == ar->num_started_vdevs); + + list_splice_tail_init(&stats->bcn, + &ar->fw_stats.bcn); + + if (is_end) { + ar->fw_stats.fw_stats_done = true; + num_bcn = 0; + } + } } static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) @@ -246,6 +275,78 @@ static const struct file_operations fops_vdev_stats = { .llseek = default_llseek, }; +static int ath12k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_link_vif *arvif; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.stats_id = WMI_REQUEST_BCN_STAT; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + param.vdev_id = arvif->vdev_id; + ret = ath12k_debugfs_fw_stats_request(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + return ret; + } + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_bcn_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath12k_open_bcn_stats, + .release = ath12k_release_bcn_stats, + .read = ath12k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static void ath12k_debugfs_fw_stats_register(struct ath12k *ar) { @@ -257,8 +358,11 @@ void ath12k_debugfs_fw_stats_register(struct ath12k *ar) */ debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); INIT_LIST_HEAD(&ar->fw_stats.vdevs); + INIT_LIST_HEAD(&ar->fw_stats.bcn); init_completion(&ar->fw_stats_complete); } diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b91b4767515a..f84102852438 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -6962,6 +6962,45 @@ ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar, } } +static void +ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_bcn *bcn; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + size_t num_bcn; + + num_bcn = list_count_nodes(&fw_stats->bcn); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath12k Beacon stats", num_bcn); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "==================="); + + list_for_each_entry(bcn, &fw_stats->bcn, list) { + arvif = ath12k_mac_get_arvif(ar, bcn->vdev_id); + if (!arvif) + continue; + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", bcn->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", arvif->ahvif->vif->addr); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================"); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx success", bcn->tx_bcn_succ_cnt); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx failures", bcn->tx_bcn_outage_cnt); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + void ath12k_wmi_fw_stats_dump(struct ath12k *ar, struct ath12k_fw_stats *fw_stats, u32 stats_id, char *buf) @@ -6975,6 +7014,9 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar, case WMI_REQUEST_VDEV_STAT: ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len); break; + case WMI_REQUEST_BCN_STAT: + ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len); + break; default: break; } @@ -7026,6 +7068,15 @@ ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src, le32_to_cpu(src->beacon_rssi_history[i]); } +static void +ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src, + struct ath12k_fw_stats_bcn *dst) +{ + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->tx_bcn_succ_cnt = le32_to_cpu(src->tx_bcn_succ_cnt); + dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt); +} + static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, struct wmi_tlv_fw_stats_parse *parse, const void *ptr, @@ -7042,6 +7093,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, const void *data = ptr; INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); if (!ev) { ath12k_warn(ab, "failed to fetch update stats ev"); @@ -7096,6 +7148,25 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, stats.stats_id = WMI_REQUEST_VDEV_STAT; list_add_tail(&dst->list, &stats.vdevs); } + for (i = 0; i < le32_to_cpu(ev->num_bcn_stats); i++) { + const struct ath12k_wmi_bcn_stats_params *src; + struct ath12k_fw_stats_bcn *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + data += sizeof(*src); + len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_bcn_stats(src, dst); + stats.stats_id = WMI_REQUEST_BCN_STAT; + list_add_tail(&dst->list, &stats.bcn); + } complete(&ar->fw_stats_complete); ath12k_debugfs_fw_stats_process(ar, &stats); diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 1c9500594f1d..ec2664c62e53 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5665,6 +5665,7 @@ struct wmi_stats_event { enum wmi_stats_id { WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCN_STAT = BIT(11), }; struct wmi_request_stats_cmd { @@ -5695,6 +5696,12 @@ struct wmi_vdev_stats_params { __le32 beacon_rssi_history[MAX_TX_RATE_VALUES]; } __packed; +struct ath12k_wmi_bcn_stats_params { + __le32 vdev_id; + __le32 tx_bcn_succ_cnt; + __le32 tx_bcn_outage_cnt; +} __packed; + struct ath12k_fw_stats_req_params { u32 stats_id; u32 vdev_id; From d46bb655f609541692b4d9369febe0079bae56d5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 028/465] wifi: ath12k: Request pdev stats from firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b826ad94d89615e222a3682b02adcbe45ecbde0e Author: Ramya Gnanasekar Date: Sat Jan 25 00:23:30 2025 +0530 wifi: ath12k: Request pdev stats from firmware Add support to request pdev stats from firmware through WMI and print the information Sample Output: ------------- cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/fw_stats/pdev_stats ath12k PDEV stats ================= Channel noise floor -85 Channel TX power 126 TX frame count 0 RX frame count 8637 RX clear count 37424 Cycle count 4372024 PHY error count 0 soc drop count 0 ath12k PDEV TX stats ==================== HTT cookies queued 0 HTT cookies disp. 0 MSDU queued 0 MPDU queued 0 MSDUs dropped 0 Local enqued 0 Local freed 0 HW queued 0 PPDUs reaped 0 Num underruns 0 PPDUs cleaned 0 MPDUs requeued 0 Excessive retries 0 HW rate 0 Sched self triggers 0 Dropped due to SW retries 0 Illegal rate phy errors 0 PDEV continuous xretry 0 TX timeout 9 PDEV resets 0 Stateless TIDs alloc failures 0 PHY underrun 0 MPDU is more than txop limit 0 ath12k PDEV RX stats ==================== Mid PPDU route change 0 Tot. number of statuses 0 Extra frags on rings 0 0 Extra frags on rings 1 0 Extra frags on rings 2 0 Extra frags on rings 3 0 MSDUs delivered to HTT 0 MPDUs delivered to HTT 0 MSDUs delivered to stack 0 MPDUs delivered to stack 0 Oversized AMSUs 0 PHY errors 0 PHY errors drops 0 MPDU errors (FCS, MIC, ENC) 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Ramya Gnanasekar Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124185330.1244585-4-ramya.gnanasekar@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 60 +++++ drivers/net/wireless/ath/ath12k/debugfs.c | 79 +++++++ drivers/net/wireless/ath/ath12k/wmi.c | 256 ++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 60 +++++ 4 files changed, 455 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 7159343113d6..3d20a6b3b473 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1107,6 +1107,66 @@ struct ath12k_fw_stats_bcn { u32 tx_bcn_outage_cnt; }; +struct ath12k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + u32 tx_frame_count; + u32 rx_frame_count; + u32 rx_clear_count; + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + s32 comp_queued; + s32 comp_delivered; + s32 msdu_enqued; + s32 mpdu_enqued; + s32 wmm_drop; + s32 local_enqued; + s32 local_freed; + s32 hw_queued; + s32 hw_reaped; + s32 underrun; + s32 tx_abort; + s32 mpdus_requed; + u32 tx_ko; + u32 data_rc; + u32 self_triggers; + u32 sw_retry_failure; + u32 illgl_rate_phy_err; + u32 pdev_cont_xretry; + u32 pdev_tx_timeout; + u32 pdev_resets; + u32 stateless_tid_alloc_failure; + u32 phy_underrun; + u32 txop_ovf; + + /* PDEV RX stats */ + s32 mid_ppdu_route_change; + s32 status_rcvd; + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + s32 htt_msdus; + s32 htt_mpdus; + s32 loc_msdus; + s32 loc_mpdus; + s32 oversize_amsdu; + s32 phy_errs; + s32 phy_err_drop; + s32 mpdu_errs; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 3cb2bb9fa424..6d6708486d14 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -69,6 +69,16 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) */ } +static void ath12k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + static void ath12k_fw_stats_bcn_free(struct list_head *head) { struct ath12k_fw_stats_bcn *i, *tmp; @@ -95,6 +105,7 @@ void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) ar->fw_stats.fw_stats_done = false; ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn); + ath12k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); spin_unlock_bh(&ar->data_lock); } @@ -210,6 +221,10 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar, num_bcn = 0; } } + if (stats->stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats->pdevs, &ar->fw_stats.pdevs); + ar->fw_stats.fw_stats_done = true; + } } static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) @@ -347,6 +362,66 @@ static const struct file_operations fops_bcn_stats = { .llseek = default_llseek, }; +static int ath12k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct ath12k_base *ab = ar->ab; + struct ath12k_fw_stats_req_params param; + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath12k_debugfs_fw_stats_request(ar, ¶m); + if (ret) { + ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_pdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath12k_open_pdev_stats, + .release = ath12k_release_pdev_stats, + .read = ath12k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static void ath12k_debugfs_fw_stats_register(struct ath12k *ar) { @@ -360,9 +435,13 @@ void ath12k_debugfs_fw_stats_register(struct ath12k *ar) &fops_vdev_stats); debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, &fops_bcn_stats); + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); INIT_LIST_HEAD(&ar->fw_stats.vdevs); INIT_LIST_HEAD(&ar->fw_stats.bcn); + INIT_LIST_HEAD(&ar->fw_stats.pdevs); + init_completion(&ar->fw_stats_complete); } diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f84102852438..61aa5f509338 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7001,6 +7001,170 @@ ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar, } } +static void +ath12k_wmi_fw_pdev_base_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length, u64 fw_soc_drop_cnt) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len = scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10llu\n", + "soc drop count", fw_soc_drop_cnt); + + *length = len; +} + +static void +ath12k_wmi_fw_pdev_tx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requeued", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Sched self triggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Stateless TIDs alloc failures", + pdev->stateless_tid_alloc_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_rx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_pdev *pdev; + u32 len = *length; + + pdev = list_first_entry_or_null(&fw_stats->pdevs, + struct ath12k_fw_stats_pdev, list); + if (!pdev) { + ath12k_warn(ar->ab, "failed to get pdev stats\n"); + return; + } + + ath12k_wmi_fw_pdev_base_stats_dump(pdev, buf, &len, + ar->ab->fw_soc_drop_count); + ath12k_wmi_fw_pdev_tx_stats_dump(pdev, buf, &len); + ath12k_wmi_fw_pdev_rx_stats_dump(pdev, buf, &len); + + *length = len; +} + void ath12k_wmi_fw_stats_dump(struct ath12k *ar, struct ath12k_fw_stats *fw_stats, u32 stats_id, char *buf) @@ -7017,6 +7181,9 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar, case WMI_REQUEST_BCN_STAT: ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len); break; + case WMI_REQUEST_PDEV_STAT: + ath12k_wmi_fw_pdev_stats_dump(ar, fw_stats, buf, &len); + break; default: break; } @@ -7077,6 +7244,70 @@ ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src, dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt); } +static void +ath12k_wmi_pull_pdev_stats_base(const struct ath12k_wmi_pdev_base_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->ch_noise_floor = a_sle32_to_cpu(src->chan_nf); + dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count); + dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count); + dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count); + dst->cycle_count = __le32_to_cpu(src->cycle_count); + dst->phy_err_count = __le32_to_cpu(src->phy_err_count); + dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr); +} + +static void +ath12k_wmi_pull_pdev_stats_tx(const struct ath12k_wmi_pdev_tx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->comp_queued = a_sle32_to_cpu(src->comp_queued); + dst->comp_delivered = a_sle32_to_cpu(src->comp_delivered); + dst->msdu_enqued = a_sle32_to_cpu(src->msdu_enqued); + dst->mpdu_enqued = a_sle32_to_cpu(src->mpdu_enqued); + dst->wmm_drop = a_sle32_to_cpu(src->wmm_drop); + dst->local_enqued = a_sle32_to_cpu(src->local_enqued); + dst->local_freed = a_sle32_to_cpu(src->local_freed); + dst->hw_queued = a_sle32_to_cpu(src->hw_queued); + dst->hw_reaped = a_sle32_to_cpu(src->hw_reaped); + dst->underrun = a_sle32_to_cpu(src->underrun); + dst->tx_abort = a_sle32_to_cpu(src->tx_abort); + dst->mpdus_requed = a_sle32_to_cpu(src->mpdus_requed); + dst->tx_ko = __le32_to_cpu(src->tx_ko); + dst->data_rc = __le32_to_cpu(src->data_rc); + dst->self_triggers = __le32_to_cpu(src->self_triggers); + dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure); + dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err); + dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry); + dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout); + dst->pdev_resets = __le32_to_cpu(src->pdev_resets); + dst->stateless_tid_alloc_failure = + __le32_to_cpu(src->stateless_tid_alloc_failure); + dst->phy_underrun = __le32_to_cpu(src->phy_underrun); + dst->txop_ovf = __le32_to_cpu(src->txop_ovf); +} + +static void +ath12k_wmi_pull_pdev_stats_rx(const struct ath12k_wmi_pdev_rx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = + a_sle32_to_cpu(src->mid_ppdu_route_change); + dst->status_rcvd = a_sle32_to_cpu(src->status_rcvd); + dst->r0_frags = a_sle32_to_cpu(src->r0_frags); + dst->r1_frags = a_sle32_to_cpu(src->r1_frags); + dst->r2_frags = a_sle32_to_cpu(src->r2_frags); + dst->r3_frags = a_sle32_to_cpu(src->r3_frags); + dst->htt_msdus = a_sle32_to_cpu(src->htt_msdus); + dst->htt_mpdus = a_sle32_to_cpu(src->htt_mpdus); + dst->loc_msdus = a_sle32_to_cpu(src->loc_msdus); + dst->loc_mpdus = a_sle32_to_cpu(src->loc_mpdus); + dst->oversize_amsdu = a_sle32_to_cpu(src->oversize_amsdu); + dst->phy_errs = a_sle32_to_cpu(src->phy_errs); + dst->phy_err_drop = a_sle32_to_cpu(src->phy_err_drop); + dst->mpdu_errs = a_sle32_to_cpu(src->mpdu_errs); +} + static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, struct wmi_tlv_fw_stats_parse *parse, const void *ptr, @@ -7094,6 +7325,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, INIT_LIST_HEAD(&stats.vdevs); INIT_LIST_HEAD(&stats.bcn); + INIT_LIST_HEAD(&stats.pdevs); if (!ev) { ath12k_warn(ab, "failed to fetch update stats ev"); @@ -7167,6 +7399,30 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, stats.stats_id = WMI_REQUEST_BCN_STAT; list_add_tail(&dst->list, &stats.bcn); } + for (i = 0; i < le32_to_cpu(ev->num_pdev_stats); i++) { + const struct ath12k_wmi_pdev_stats_params *src; + struct ath12k_fw_stats_pdev *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + stats.stats_id = WMI_REQUEST_PDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath12k_wmi_pull_pdev_stats_base(&src->base, dst); + ath12k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath12k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats.pdevs); + } complete(&ar->fw_stats_complete); ath12k_debugfs_fw_stats_process(ar, &stats); diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index ec2664c62e53..2934d9589007 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5664,6 +5664,7 @@ struct wmi_stats_event { } __packed; enum wmi_stats_id { + WMI_REQUEST_PDEV_STAT = BIT(2), WMI_REQUEST_VDEV_STAT = BIT(3), WMI_REQUEST_BCN_STAT = BIT(11), }; @@ -5702,6 +5703,65 @@ struct ath12k_wmi_bcn_stats_params { __le32 tx_bcn_outage_cnt; } __packed; +struct ath12k_wmi_pdev_base_stats_params { + a_sle32 chan_nf; + __le32 tx_frame_count; /* Cycles spent transmitting frames */ + __le32 rx_frame_count; /* Cycles spent receiving frames */ + __le32 rx_clear_count; /* Total channel busy time, evidently */ + __le32 cycle_count; /* Total on-channel time */ + __le32 phy_err_count; + __le32 chan_tx_pwr; +} __packed; + +struct ath12k_wmi_pdev_tx_stats_params { + a_sle32 comp_queued; + a_sle32 comp_delivered; + a_sle32 msdu_enqued; + a_sle32 mpdu_enqued; + a_sle32 wmm_drop; + a_sle32 local_enqued; + a_sle32 local_freed; + a_sle32 hw_queued; + a_sle32 hw_reaped; + a_sle32 underrun; + a_sle32 tx_abort; + a_sle32 mpdus_requed; + __le32 tx_ko; + __le32 data_rc; + __le32 self_triggers; + __le32 sw_retry_failure; + __le32 illgl_rate_phy_err; + __le32 pdev_cont_xretry; + __le32 pdev_tx_timeout; + __le32 pdev_resets; + __le32 stateless_tid_alloc_failure; + __le32 phy_underrun; + __le32 txop_ovf; +} __packed; + +struct ath12k_wmi_pdev_rx_stats_params { + a_sle32 mid_ppdu_route_change; + a_sle32 status_rcvd; + a_sle32 r0_frags; + a_sle32 r1_frags; + a_sle32 r2_frags; + a_sle32 r3_frags; + a_sle32 htt_msdus; + a_sle32 htt_mpdus; + a_sle32 loc_msdus; + a_sle32 loc_mpdus; + a_sle32 oversize_amsdu; + a_sle32 phy_errs; + a_sle32 phy_err_drop; + a_sle32 mpdu_errs; +} __packed; + +struct ath12k_wmi_pdev_stats_params { + struct ath12k_wmi_pdev_base_stats_params base; + struct ath12k_wmi_pdev_tx_stats_params tx; + struct ath12k_wmi_pdev_rx_stats_params rx; +} __packed; + struct ath12k_fw_stats_req_params { u32 stats_id; u32 vdev_id; From 65fc6250c653b976ff710bbcbd9ad51ca87d4454 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 029/465] wifi: ath12k: Fix pdev lookup in WBM error processing JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4e635b81db9d69bbb836afae8cb402978ff966a4 Author: Rameshkumar Sundaram Date: Thu Jan 2 10:00:48 2025 +0530 wifi: ath12k: Fix pdev lookup in WBM error processing Currently in ath12k_dp_rx_process_wbm_err(), when processing packets received on the WBM error ring, pdev validation is done based upon the hw_link_id. But hw_link_id corresponds to link id of a given partner pdev in a MLO hardware group, and is not the correct index to use to lookup a pdev in an SoC(ab). As a result, pdev validation fails, and the reaped packets are dropped instead of being processed. The correct index to use is the pdev_id, which is already derived in the function. So update the logic to validate the pdev based upon the pdev_id instead of the hw_link_id. This matches the logic used in other Rx ring processing functions. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: 1a73acb5fba4 ("wifi: ath12k: move to HW link id based receive handling") Signed-off-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20250102043048.2596791-1-quic_ramess@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index a0d67ee3a5dc..ff6a709b5042 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4088,7 +4088,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, hw_links[hw_link_id].pdev_idx); ar = partner_ab->pdevs[pdev_id].ar; - if (!ar || !rcu_dereference(ar->ab->pdevs_active[hw_link_id])) { + if (!ar || !rcu_dereference(ar->ab->pdevs_active[pdev_id])) { dev_kfree_skb_any(msdu); continue; } From 5740b80eabbc7e61deb5e6a3f10ce4f2fb6858b1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 030/465] wifi: ath12k: Add HTT source ring ID for monitor rings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dbb73909eea3e89437296d75675e84fcd76a418c Author: P Praneesh Date: Mon Dec 23 11:31:19 2024 +0530 wifi: ath12k: Add HTT source ring ID for monitor rings Add source buffer ring and destination buffer ring ID for monitor rings. These IDs are used for ring configuration during initial ring setup. Since monitor rings are enabled based on the rxdma1_enable flag, enable it in the hardware param for the QCN9274 version 2 hardware and increase the destination ring size to handle MSDU data buffers. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-2-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp.h | 5 ++++- drivers/net/wireless/ath/ath12k/dp_tx.c | 4 ++-- drivers/net/wireless/ath/ath12k/hw.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 7ac3143de016..1099ec19260b 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -176,7 +176,7 @@ struct ath12k_pdev_dp { #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 #define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 -#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 8092 #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 #define DP_TX_MONITOR_BUF_RING_SIZE 4096 #define DP_TX_MONITOR_DEST_RING_SIZE 2048 @@ -434,8 +434,11 @@ enum htt_srng_ring_id { HTT_HOST1_TO_FW_RXBUF_RING, HTT_HOST2_TO_FW_RXBUF_RING, HTT_RXDMA_NON_MONITOR_DEST_RING, + HTT_RXDMA_HOST_BUF_RING2, HTT_TX_MON_HOST2MON_BUF_RING, HTT_TX_MON_MON2HOST_DEST_RING, + HTT_RX_MON_HOST2MON_BUF_RING, + HTT_RX_MON_MON2HOST_DEST_RING, }; /* host -> target HTT_SRING_SETUP message diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index de8bb9c1533e..133a496bebb9 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -946,7 +946,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_BUF: - *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_id = HTT_RX_MON_HOST2MON_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_STATUS: @@ -954,7 +954,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_DST: - *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_id = HTT_RX_MON_MON2HOST_DEST_RING; *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_DESC: diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index b7b583fadb5a..14bbd446ad37 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1035,7 +1035,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_params = &ath12k_hw_hal_params_qcn9274, - .rxdma1_enable = false, + .rxdma1_enable = true, .num_rxdma_per_pdev = 1, .num_rxdma_dst_ring = 0, .rx_mac_buf_ring = false, From b98e48d1b32a78f3bd69f3fc99797acea7096078 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 031/465] wifi: ath12k: Enable filter config for monitor destination ring JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9b0d8fb3326b32a11981a3ac6fa4ddf97248b8a6 Author: P Praneesh Date: Mon Dec 23 11:31:20 2024 +0530 wifi: ath12k: Enable filter config for monitor destination ring Add provision to configure monitor filter for the destination ring. These filters are used for requesting statistics or monitor mode through the monitor destination ring. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-3-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp.h | 49 +++++++++++++++- drivers/net/wireless/ath/ath12k/dp_tx.c | 75 ++++++++++++++++++++----- 2 files changed, 109 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 1099ec19260b..6946fb4f5c25 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -770,8 +770,22 @@ enum htt_stats_internal_ppdu_frametype { #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) -#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) -#define HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL BIT(27) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON BIT(28) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT GENMASK(18, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL GENMASK(21, 19) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA GENMASK(24, 22) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD GENMASK(9, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE BIT(17) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE BIT(18) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE BIT(19) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET BIT(0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET GENMASK(14, 1) #define HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET GENMASK(15, 0) #define HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET GENMASK(31, 16) @@ -800,6 +814,7 @@ enum htt_rx_filter_tlv_flags { HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO = BIT(13), }; enum htt_rx_mgmt_pkt_filter_tlv_flags0 { @@ -1088,6 +1103,21 @@ enum htt_rx_data_pkt_filter_tlv_flasg3 { HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ HTT_RX_FILTER_TLV_FLAGS_ATTENTION) +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO) + /* msdu start. mpdu end, attention, rx hdr tlv's are not subscribed */ #define HTT_RX_TLV_FLAGS_RXDMA_RING \ (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ @@ -1116,6 +1146,10 @@ struct htt_rx_ring_selection_cfg_cmd { __le32 info3; } __packed; +#define HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE 32 +#define HTT_RX_RING_DEFAULT_DMA_LENGTH 0x7 +#define HTT_RX_RING_PKT_TLV_OFFSET 0x1 + struct htt_rx_ring_tlv_filter { u32 rx_filter; /* see htt_rx_filter_tlv_flags */ u32 pkt_filter_flags0; /* MGMT */ @@ -1133,6 +1167,17 @@ struct htt_rx_ring_tlv_filter { u16 rx_mpdu_start_wmask; u16 rx_mpdu_end_wmask; u32 rx_msdu_end_wmask; + u32 conf_len_ctrl; + u32 conf_len_mgmt; + u32 conf_len_data; + u16 rx_drop_threshold; + bool enable_log_mgmt_type; + bool enable_log_ctrl_type; + bool enable_log_data_type; + bool enable_rx_tlv_offset; + u16 rx_tlv_offset; + bool drop_threshold_valid; + bool rxmon_disable; }; #define HTT_STATS_FRAME_CTRL_TYPE_MGMT 0x0 diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 133a496bebb9..aa8058dd2da6 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1209,15 +1209,46 @@ int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_base *ab, u32 ring_id, cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS); cmd->info0 |= le32_encode_bits(tlv_filter->offset_valid, - HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID); + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID); + cmd->info0 |= + le32_encode_bits(tlv_filter->drop_threshold_valid, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL); + cmd->info0 |= le32_encode_bits(!tlv_filter->rxmon_disable, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON); + cmd->info1 = le32_encode_bits(rx_buf_size, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_mgmt, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_ctrl, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_data, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA); cmd->pkt_type_en_flags0 = cpu_to_le32(tlv_filter->pkt_filter_flags0); cmd->pkt_type_en_flags1 = cpu_to_le32(tlv_filter->pkt_filter_flags1); cmd->pkt_type_en_flags2 = cpu_to_le32(tlv_filter->pkt_filter_flags2); cmd->pkt_type_en_flags3 = cpu_to_le32(tlv_filter->pkt_filter_flags3); cmd->rx_filter_tlv = cpu_to_le32(tlv_filter->rx_filter); + cmd->info2 = le32_encode_bits(tlv_filter->rx_drop_threshold, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_mgmt_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_ctrl_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_data_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE); + + cmd->info3 = + le32_encode_bits(tlv_filter->enable_rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET); + cmd->info3 |= + le32_encode_bits(tlv_filter->rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET); + if (tlv_filter->offset_valid) { cmd->rx_packet_offset = le32_encode_bits(tlv_filter->rx_packet_offset, @@ -1342,15 +1373,28 @@ int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset) int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) { struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; struct htt_rx_ring_tlv_filter tlv_filter = {0}; - int ret, ring_id; + int ret, ring_id, i; - ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; tlv_filter.offset_valid = false; if (!reset) { - tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING; + + tlv_filter.drop_threshold_valid = true; + tlv_filter.rx_drop_threshold = HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE; + + tlv_filter.enable_log_mgmt_type = true; + tlv_filter.enable_log_ctrl_type = true; + tlv_filter.enable_log_data_type = true; + + tlv_filter.conf_len_ctrl = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_mgmt = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_data = HTT_RX_RING_DEFAULT_DMA_LENGTH; + + tlv_filter.enable_rx_tlv_offset = true; + tlv_filter.rx_tlv_offset = HTT_RX_RING_PKT_TLV_OFFSET; + tlv_filter.pkt_filter_flags0 = HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; @@ -1368,14 +1412,19 @@ int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) } if (ab->hw_params->rxdma1_enable) { - ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, 0, - HAL_RXDMA_MONITOR_BUF, - DP_RXDMA_REFILL_RING_SIZE, - &tlv_filter); - if (ret) { - ath12k_err(ab, - "failed to setup filter for monitor buf %d\n", ret); - return ret; + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + return ret; + } } } From 5f9e70923ad7269033136d07d76705cb3a12de0a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 032/465] wifi: ath12k: Avoid multiple times configuring monitor filter JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6e8c9ba7f0308ba2a88270cec6ce7e8ef621df1f Author: P Praneesh Date: Mon Dec 23 11:31:21 2024 +0530 wifi: ath12k: Avoid multiple times configuring monitor filter ath12k_mac_op_configure_filter() gets called multiple times during interface bringup. Applying filter configuration from this function leads to writing same filter configurations multiple times. Resolve this issue by relocating the filter configuration to ath12k_mac_config_mon_status_default(), which is invoked by both ath12k_mac_op_start() and ath12k_mac_op_stop(). Additionally, set the rxmon_disable flag to true when called from ath12k_mac_op_stop() to disable the monitor destination ring. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-4-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 57 +++++++++++++++------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a245eb6293fb..4fb7e235be66 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7336,8 +7336,36 @@ void ath12k_mac_drain_tx(struct ath12k *ar) static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) { - return -EOPNOTSUPP; - /* TODO: Need to support new monitor mode */ + struct htt_rx_ring_tlv_filter tlv_filter = {}; + struct ath12k_base *ab = ar->ab; + u32 ring_id, i; + int ret = 0; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (!ab->hw_params->rxdma1_enable) + return ret; + + if (enable) + tlv_filter = ath12k_mac_mon_status_filter_default; + else + tlv_filter.rxmon_disable = true; + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + } + } + + return ret; } static int ath12k_mac_start(struct ath12k *ar) @@ -8468,29 +8496,6 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, FIF_PROBE_REQ | \ FIF_FCSFAIL) -static void ath12k_mac_configure_filter(struct ath12k *ar, - unsigned int total_flags) -{ - bool reset_flag; - int ret; - - lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - - ar->filter_flags = total_flags; - - /* For monitor mode */ - reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); - - ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); - if (ret) - ath12k_warn(ar->ab, - "fail to set monitor filter: %d\n", ret); - - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "total_flags:0x%x, reset_flag:%d\n", - total_flags, reset_flag); -} - static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -8504,7 +8509,7 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, ar = ath12k_ah_to_ar(ah, 0); *total_flags &= SUPPORTED_FILTERS; - ath12k_mac_configure_filter(ar, *total_flags); + ar->filter_flags = *total_flags; } static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) From a34124a50b270e511fb2f9a0f902bf478d70ffa8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 033/465] wifi: ath12k: Avoid code duplication in monitor ring processing JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 39f1d751d2ae7050ad9b88560fc2042515dc88d5 Author: P Praneesh Date: Mon Dec 23 11:31:22 2024 +0530 wifi: ath12k: Avoid code duplication in monitor ring processing The current implementation processes the monitor destination ring using two separate functions, ath12k_dp_mon_srng_process() for standalone monitor mode and ath12k_dp_mon_rx_process_stats() for statistics. However, both functions contain same code which performs monitor ring reaping and skb data processing. To eliminate redundancy, remove the duplicate code and use a single function to handle both the cases. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-5-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 126 ++--------------------- drivers/net/wireless/ath/ath12k/dp_mon.h | 6 +- 2 files changed, 9 insertions(+), 123 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 53f8e8f8959a..08f92e74fede 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2000,116 +2000,6 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, return tlv_status; } -int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, - enum dp_monitor_mode monitor_mode, - struct napi_struct *napi) -{ - struct hal_mon_dest_desc *mon_dst_desc; - struct ath12k_pdev_dp *pdev_dp = &ar->dp; - struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&pdev_dp->mon_data; - struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; - struct sk_buff *skb; - struct ath12k_skb_rxcb *rxcb; - struct dp_srng *mon_dst_ring; - struct hal_srng *srng; - struct dp_rxdma_mon_ring *buf_ring; - u64 cookie; - u32 ppdu_id; - int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - struct hal_rx_mon_ppdu_info *ppdu_info; - struct ath12k_peer *peer = NULL; - u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); - - ppdu_info = &pmon->mon_ppdu_info; - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; - - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) { - mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; - buf_ring = &dp->rxdma_mon_buf_ring; - } else { - return 0; - } - - srng = &ab->hal.srng_list[mon_dst_ring->ring_id]; - - spin_lock_bh(&srng->lock); - ath12k_hal_srng_access_begin(ab, srng); - - while (likely(*budget)) { - *budget -= 1; - mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); - if (unlikely(!mon_dst_desc)) - break; - - cookie = le32_to_cpu(mon_dst_desc->cookie); - buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); - - spin_lock_bh(&buf_ring->idr_lock); - skb = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!skb)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - goto move_next; - } - - rxcb = ATH12K_SKB_RXCB(skb); - dma_unmap_single(ab->dev, rxcb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - ppdu_id = le32_to_cpu(mon_dst_desc->ppdu_id); - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) - continue; - - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) - ath12k_dp_mon_rx_parse_mon_status(ar, pmon, - skb, napi); - else - ath12k_dp_mon_tx_parse_mon_status(ar, pmon, - skb, napi, ppdu_id); - - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - dev_kfree_skb_any(skb); - continue; - } - - dev_kfree_skb_any(skb); - pmon->dest_skb_q[i] = NULL; - } - - dest_idx = 0; -move_next: - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); - num_buffs_reaped++; - } - - ath12k_hal_srng_access_end(ab, srng); - spin_unlock_bh(&srng->lock); - - return num_buffs_reaped; -} - static void ath12k_dp_mon_rx_update_peer_rate_table_stats(struct ath12k_rx_peer_stats *rx_stats, struct hal_rx_mon_ppdu_info *ppdu_info, @@ -2415,8 +2305,8 @@ ath12k_dp_mon_rx_update_peer_mu_stats(struct ath12k *ar, ath12k_dp_mon_rx_update_user_stats(ar, ppdu_info, i); } -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget) +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, + struct napi_struct *napi) { struct ath12k_base *ab = ar->ab; struct ath12k_pdev_dp *pdev_dp = &ar->dp; @@ -2437,8 +2327,9 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, u8 dest_idx = 0, i; bool end_of_ppdu; u32 hal_status; + u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; buf_ring = &dp->rxdma_mon_buf_ring; @@ -2535,11 +2426,10 @@ int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct ath12k *ar = ath12k_ab_to_ar(ab, mac_id); int num_buffs_reaped = 0; - if (!ar->monitor_started) - ath12k_dp_mon_rx_process_stats(ar, mac_id, napi, &budget); - else - num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, - monitor_mode, napi); + if (ab->hw_params->rxdma1_enable) { + if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) + num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, napi); + } return num_buffs_reaped; } diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index 64c959c36459..e4368eb42aca 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -82,9 +82,6 @@ ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab, struct dp_rxdma_mon_ring *buf_ring, int req_entries); -int ath12k_dp_mon_srng_process(struct ath12k *ar, - int *budget, enum dp_monitor_mode monitor_mode, - struct napi_struct *napi); int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct napi_struct *napi, int budget, enum dp_monitor_mode monitor_mode); @@ -100,6 +97,5 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct napi_struct *napi, u32 ppdu_id); void ath12k_dp_mon_rx_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info); -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget); +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct napi_struct *napi); #endif From 8cb85549a406f6931fec306c03515835a57d69ca Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:25 +0200 Subject: [PATCH 034/465] wifi: ath12k: Restructure the code for monitor ring processing JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cf544270c7392af1dc3a3bf405b902f3b9d3f925 Author: P Praneesh Date: Mon Dec 23 11:31:23 2024 +0530 wifi: ath12k: Restructure the code for monitor ring processing Currently, monitor ring reaping and processing occur in the same loop, which requires holding ring locks until skb processing is complete. However, only the ring reaping part requires the ring lock; the skb processing part does not need it. This approach is problematic because it unnecessarily extends the duration for which the ring locks are held, leading to increased contention and potential backpressure issues. Fix it by holding ring locks only during the reaping phase, as skb processing does not require them. First, reap the monitor destination ring with the ring lock and queue the skbs into an skb list. Then, process the skbs in this list in a separate loop without holding the ring lock. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-6-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp.h | 1 - drivers/net/wireless/ath/ath12k/dp_mon.c | 116 +++++++++++++---------- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 6946fb4f5c25..f68bb78d4a11 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -125,7 +125,6 @@ struct ath12k_mon_data { struct sk_buff_head rx_status_q; struct dp_mon_mpdu *mon_mpdu; struct list_head dp_rx_mon_mpdu_list; - struct sk_buff *dest_skb_q[DP_MON_MAX_STATUS_BUF]; struct dp_mon_tx_ppdu_info *tx_prot_ppdu_info; struct dp_mon_tx_ppdu_info *tx_data_ppdu_info; }; diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 08f92e74fede..f9fd28ce801a 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1188,14 +1188,11 @@ static enum hal_rx_mon_status ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct sk_buff *skb) { - struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct hal_tlv_64_hdr *tlv; enum hal_rx_mon_status hal_status; u16 tlv_tag, tlv_len; u8 *ptr = skb->data; - memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info)); - do { tlv = (struct hal_tlv_64_hdr *)ptr; tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); @@ -2305,6 +2302,13 @@ ath12k_dp_mon_rx_update_peer_mu_stats(struct ath12k *ar, ath12k_dp_mon_rx_update_user_stats(ar, ppdu_info, i); } +static void +ath12k_dp_mon_rx_memset_ppdu_info(struct hal_rx_mon_ppdu_info *ppdu_info) +{ + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; +} + int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct napi_struct *napi) { @@ -2322,13 +2326,13 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct ath12k_sta *ahsta = NULL; struct ath12k_link_sta *arsta; struct ath12k_peer *peer; + struct sk_buff_head skb_list; u64 cookie; int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - u32 hal_status; + u32 hal_status, end_offset, info0; u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); + __skb_queue_head_init(&skb_list); srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; buf_ring = &dp->rxdma_mon_buf_ring; @@ -2342,6 +2346,7 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); if (unlikely(!mon_dst_desc)) break; + cookie = le32_to_cpu(mon_dst_desc->cookie); buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); @@ -2359,55 +2364,19 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, dma_unmap_single(ab->dev, rxcb->paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) - continue; - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); - - if (ppdu_info->peer_id == HAL_INVALID_PEERID || - hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { - dev_kfree_skb_any(skb); - continue; - } - - rcu_read_lock(); - spin_lock_bh(&ab->base_lock); - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - dev_kfree_skb_any(skb); - continue; - } - - if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { - ahsta = ath12k_sta_to_ahsta(peer->sta); - arsta = &ahsta->deflink; - ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, - ppdu_info); - } else if ((ppdu_info->fc_valid) && - (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { - ath12k_dp_mon_rx_process_ulofdma(ppdu_info); - ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); - } - - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - dev_kfree_skb_any(skb); - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; + end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); + if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { + skb_put(skb, end_offset); + } else { + ath12k_warn(ab, + "invalid offset on mon stats destination %u\n", + end_offset); + skb_put(skb, DP_RX_BUFFER_SIZE); } - dest_idx = 0; + __skb_queue_tail(&skb_list, skb); + move_next: ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); ath12k_hal_srng_src_get_next_entry(ab, srng); @@ -2416,6 +2385,49 @@ move_next: ath12k_hal_srng_access_end(ab, srng); spin_unlock_bh(&srng->lock); + + if (!num_buffs_reaped) + return 0; + + while ((skb = __skb_dequeue(&skb_list))) { + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); + if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + dev_kfree_skb_any(skb); + continue; + } + + if (ppdu_info->peer_id == HAL_INVALID_PEERID) + goto free_skb; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "failed to find the peer with monitor peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { + ahsta = ath12k_sta_to_ahsta(peer->sta); + arsta = &ahsta->deflink; + ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, + ppdu_info); + } else if ((ppdu_info->fc_valid) && + (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { + ath12k_dp_mon_rx_process_ulofdma(ppdu_info); + ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); + } + +next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +free_skb: + dev_kfree_skb_any(skb); + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + } + return num_buffs_reaped; } From 75d6395c63886bbfb2adea16fd3ad1f6dd710a35 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 035/465] wifi: ath12k: Fix invalid entry fetch in ath12k_dp_mon_srng_process JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37944 commit 63fdc4509bcf483e79548de6bc08bf3c8e504bb3 Author: P Praneesh Date: Mon Dec 23 11:31:24 2024 +0530 wifi: ath12k: Fix invalid entry fetch in ath12k_dp_mon_srng_process Currently, ath12k_dp_mon_srng_process uses ath12k_hal_srng_src_get_next_entry to fetch the next entry from the destination ring. This is incorrect because ath12k_hal_srng_src_get_next_entry is intended for source rings, not destination rings. This leads to invalid entry fetches, causing potential data corruption or crashes due to accessing incorrect memory locations. This happens because the source ring and destination ring have different handling mechanisms and using the wrong function results in incorrect pointer arithmetic and ring management. To fix this issue, replace the call to ath12k_hal_srng_src_get_next_entry with ath12k_hal_srng_dst_get_next_entry in ath12k_dp_mon_srng_process. This ensures that the correct function is used for fetching entries from the destination ring, preventing invalid memory accesses. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-7-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index f9fd28ce801a..236456b02198 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2379,7 +2379,7 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, move_next: ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); + ath12k_hal_srng_dst_get_next_entry(ab, srng); num_buffs_reaped++; } From 5c3571b07d4c8359d71164a307ba75040b9fa72b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 036/465] wifi: ath12k: Fix end offset bit definition in monitor ring descriptor JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6788a666000d600bd8f2e9f991cad9cc805e7f01 Author: P Praneesh Date: Mon Dec 23 11:31:25 2024 +0530 wifi: ath12k: Fix end offset bit definition in monitor ring descriptor End offset for the monitor destination ring descriptor is defined as 16 bits, while the firmware definition specifies only 12 bits. The remaining bits (bit 12 to bit 15) are reserved and may contain junk values, leading to invalid information retrieval. Fix this issue by updating the correct genmask values. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-8-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/hal_desc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 7b0403d245e5..a102d27e5785 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -2968,7 +2968,7 @@ struct hal_mon_buf_ring { #define HAL_MON_DEST_COOKIE_BUF_ID GENMASK(17, 0) -#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(15, 0) +#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(11, 0) #define HAL_MON_DEST_INFO0_FLUSH_DETECTED BIT(16) #define HAL_MON_DEST_INFO0_END_OF_PPDU BIT(17) #define HAL_MON_DEST_INFO0_INITIATOR BIT(18) From babdb6dafcd33c728fecad5680da872d866bb94d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 037/465] wifi: ath12k: Add drop descriptor handling for monitor ring JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 51ad34a47e9f261d03894b49a734174c170b326f Author: P Praneesh Date: Mon Dec 23 11:31:26 2024 +0530 wifi: ath12k: Add drop descriptor handling for monitor ring When monitor block in Hardware experiences internal backpressure, a ring entry with the EMPTY_DESC reason is received in the monitor destination ring descriptor. The cookie field for this corresponding entry is invalid. Currently driver attempts to process this cookie resulting in invalid buf_id warning logs flooding the console. To fix this, skip processing the destination descriptor when the EMPTY_DESCRIPTOR bit is set to true in ring descriptor, thereby avoiding the processing of junk cookies. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-9-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 236456b02198..bf43ef2b6b39 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2347,6 +2347,14 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, if (unlikely(!mon_dst_desc)) break; + /* In case of empty descriptor, the cookie in the ring descriptor + * is invalid. Therefore, this entry is skipped, and ring processing + * continues. + */ + info0 = le32_to_cpu(mon_dst_desc->info0); + if (u32_get_bits(info0, HAL_MON_DEST_INFO0_EMPTY_DESC)) + goto move_next; + cookie = le32_to_cpu(mon_dst_desc->cookie); buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); From 963837952cc437ed287151af04910cdd1cf560d2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 038/465] wifi: ath12k: Handle end reason for the monitor destination ring JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8520ba9bb8f43ce9d540d9be6a5711ceeb1651cf Author: P Praneesh Date: Mon Dec 23 11:31:27 2024 +0530 wifi: ath12k: Handle end reason for the monitor destination ring Currently, the monitor destination ring's descriptor includes a 2-bit field for the end reason. Out of all the end reason values, hardware uses HAL_MON_FLUSH_DETECTED and HAL_MON_PPDU_TRUNCATED to indicate buffers that should not be processed due to system level errors. Driver should not process entries with these end reasons, as they contain junk values. However, the current code lacks end reason-specific checks for the monitor destination ring, leading to the processing of invalid buffers. Fix this by adding checks for these two end reasons during the reaping phase. Free the skb if either HAL_MON_FLUSH_DETECTED or HAL_MON_PPDU_TRUNCATED is detected, preventing the driver from processing invalid entries. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-10-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 17 ++++++++++++++++- drivers/net/wireless/ath/ath12k/hal_desc.h | 3 +-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index bf43ef2b6b39..dbf5afd88ad5 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2329,7 +2329,7 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct sk_buff_head skb_list; u64 cookie; int num_buffs_reaped = 0, srng_id, buf_id; - u32 hal_status, end_offset, info0; + u32 hal_status, end_offset, info0, end_reason; u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); __skb_queue_head_init(&skb_list); @@ -2373,6 +2373,21 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + end_reason = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_REASON); + + /* HAL_MON_FLUSH_DETECTED implies that an rx flush received at the end of + * rx PPDU and HAL_MON_PPDU_TRUNCATED implies that the PPDU got + * truncated due to a system level error. In both the cases, buffer data + * can be discarded + */ + if ((end_reason == HAL_MON_FLUSH_DETECTED) || + (end_reason == HAL_MON_PPDU_TRUNCATED)) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "Monitor dest descriptor end reason %d", end_reason); + dev_kfree_skb_any(skb); + goto move_next; + } + end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { skb_put(skb, end_offset); diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index a102d27e5785..3e8983b85de8 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -2969,8 +2969,7 @@ struct hal_mon_buf_ring { #define HAL_MON_DEST_COOKIE_BUF_ID GENMASK(17, 0) #define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(11, 0) -#define HAL_MON_DEST_INFO0_FLUSH_DETECTED BIT(16) -#define HAL_MON_DEST_INFO0_END_OF_PPDU BIT(17) +#define HAL_MON_DEST_INFO0_END_REASON GENMASK(17, 16) #define HAL_MON_DEST_INFO0_INITIATOR BIT(18) #define HAL_MON_DEST_INFO0_EMPTY_DESC BIT(19) #define HAL_MON_DEST_INFO0_RING_ID GENMASK(27, 20) From 52cfdc359431ec1bedbc577de3119095848b0250 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 039/465] wifi: ath12k: Optimize NAPI budget by adjusting PPDU processing JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 394a3fa7c538060ee3cb134d52b4e9ec1f680cac Author: P Praneesh Date: Mon Dec 23 11:31:28 2024 +0530 wifi: ath12k: Optimize NAPI budget by adjusting PPDU processing In the current implementation, when PPDU spans multiple ring descriptors, leading to inefficient use of the NAPI budget. The budget counter is decremented for each ring descriptor, causing rapid depletion of the budget even though the processing of a single PPDU might not be complete. To address this issue, modify the code to decrement the budget counter only when the driver receives HAL_MON_END_OF_PPDU as the end reason. This change ensures that the budget is decremented only once per PPDU, resulting in more efficient utilization of the NAPI budget and better handling of monitor destination ring. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-11-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index dbf5afd88ad5..a11b39ae3774 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2388,6 +2388,13 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, goto move_next; } + /* Calculate the budget when the ring descriptor with the + * HAL_MON_END_OF_PPDU to ensure that one PPDU worth of data is always + * reaped. This helps to efficiently utilize the NAPI budget. + */ + if (end_reason == HAL_MON_END_OF_PPDU) + *budget -= 1; + end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { skb_put(skb, end_offset); From 8b54974a393392d048709f4da051136a0bb1e07d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 040/465] wifi: ath12k: Handle PPDU spread across multiple buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 67434640e52256efd14260cb8edbea41c1faef0b Author: P Praneesh Date: Mon Dec 23 11:31:29 2024 +0530 wifi: ath12k: Handle PPDU spread across multiple buffers Each PPDU contains numerous TLV tags. HAL_RX_PPDU_START marks the start of the PPDU, and HAL_RX_PPDU_END_STATUS_DONE marks the end. From the monitor destination rings, the driver retrieves skb containing these TLV tags and their corresponding data. Sometimes, one PPDU’s information spreads across multiple skbs. The current parsing logic uses memset on struct hal_rx_mon_ppdu_info after parsing each skb, leading to information loss if a PPDU spans multiple skbs. Fix this by setting the ppdu_continuation flag when the driver fails to get HAL_RX_PPDU_END_STATUS_DONE, and do memset on struct hal_rx_mon_ppdu_info only when the ppdu_continuation flag is not set. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-12-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 8 ++++++++ drivers/net/wireless/ath/ath12k/hal_rx.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index a11b39ae3774..4b35dfcbdfe1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2419,9 +2419,17 @@ move_next: if (!num_buffs_reaped) return 0; + /* In some cases, one PPDU worth of data can be spread across multiple NAPI + * schedules, To avoid losing existing parsed ppdu_info information, skip + * the memset of the ppdu_info structure and continue processing it. + */ + if (!ppdu_info->ppdu_continuation) + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + while ((skb = __skb_dequeue(&skb_list))) { hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + ppdu_info->ppdu_continuation = true; dev_kfree_skb_any(skb); continue; } diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 04e9f5f8e83a..b60c172d131b 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -231,6 +231,7 @@ struct hal_rx_mon_ppdu_info { bool first_msdu_in_mpdu; bool is_ampdu; u8 medium_prot_type; + bool ppdu_continuation; }; #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) From 1d67a646718556ec4fac67396846db493b0bae7f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 041/465] wifi: ath12k: Avoid memory leak while enabling statistics JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37743 commit ecfc131389923405be8e7a6f4408fd9321e4d19b Author: P Praneesh Date: Mon Dec 23 11:31:30 2024 +0530 wifi: ath12k: Avoid memory leak while enabling statistics Driver uses monitor destination rings for extended statistics mode and standalone monitor mode. In extended statistics mode, TLVs are parsed from the buffer received from the monitor destination ring and assigned to the ppdu_info structure to update per-packet statistics. In standalone monitor mode, along with per-packet statistics, the packet data (payload) is captured, and the driver updates per MSDU to mac80211. When the AP interface is enabled, only extended statistics mode is activated. As part of enabling monitor rings for collecting statistics, the driver subscribes to HAL_RX_MPDU_START TLV in the filter configuration. This TLV is received from the monitor destination ring, and kzalloc for the mon_mpdu object occurs, which is not freed, leading to a memory leak. The kzalloc for the mon_mpdu object is only required while enabling the standalone monitor interface. This causes a memory leak while enabling extended statistics mode in the driver. Fix this memory leak by removing the kzalloc for the mon_mpdu object in the HAL_RX_MPDU_START TLV handling. Additionally, remove the standalone monitor mode handlings in the HAL_MON_BUF_ADDR and HAL_RX_MSDU_END TLVs. These TLV tags will be handled properly when enabling standalone monitor mode in the future. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-13-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 65 ++++-------------------- drivers/net/wireless/ath/ath12k/hal_rx.h | 3 ++ 2 files changed, 12 insertions(+), 56 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 4b35dfcbdfe1..23b1a41c6fd2 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -567,7 +567,6 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, const struct hal_tlv_64_hdr *tlv) { - struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; const void *tlv_data = tlv->value; u32 info[7], userid; @@ -748,7 +747,6 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, } case HAL_RX_MPDU_START: { const struct hal_rx_mpdu_start *mpdu_start = tlv_data; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; u16 peer_id; info[1] = __le32_to_cpu(mpdu_start->info1); @@ -765,65 +763,17 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, u32_get_bits(info[0], HAL_RX_MPDU_START_INFO1_PEERID); } - mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC); - if (!mon_mpdu) - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; - break; } case HAL_RX_MSDU_START: /* TODO: add msdu start parsing logic */ break; - case HAL_MON_BUF_ADDR: { - struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring; - const struct dp_mon_packet_info *packet_info = tlv_data; - int buf_id = u32_get_bits(packet_info->cookie, - DP_RXDMA_BUF_COOKIE_BUF_ID); - struct sk_buff *msdu; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; - struct ath12k_skb_rxcb *rxcb; - - spin_lock_bh(&buf_ring->idr_lock); - msdu = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!msdu)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; - } - - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - - if (mon_mpdu->tail) - mon_mpdu->tail->next = msdu; - else - mon_mpdu->tail = msdu; - - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - - break; - } - case HAL_RX_MSDU_END: { - const struct rx_msdu_end_qcn9274 *msdu_end = tlv_data; - bool is_first_msdu_in_mpdu; - u16 msdu_end_info; - - msdu_end_info = __le16_to_cpu(msdu_end->info5); - is_first_msdu_in_mpdu = u32_get_bits(msdu_end_info, - RX_MSDU_END_INFO5_FIRST_MSDU); - if (is_first_msdu_in_mpdu) { - pmon->mon_mpdu->head = pmon->mon_mpdu->tail; - pmon->mon_mpdu->tail = NULL; - } - break; - } + case HAL_MON_BUF_ADDR: + return HAL_RX_MON_STATUS_BUF_ADDR; + case HAL_RX_MSDU_END: + return HAL_RX_MON_STATUS_MSDU_END; case HAL_RX_MPDU_END: - list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list); - break; + return HAL_RX_MON_STATUS_MPDU_END; case HAL_DUMMY: return HAL_RX_MON_STATUS_BUF_DONE; case HAL_RX_PPDU_END_STATUS_DONE: @@ -1215,7 +1165,10 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) break; - } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE); + } while ((hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE) || + (hal_status == HAL_RX_MON_STATUS_BUF_ADDR) || + (hal_status == HAL_RX_MON_STATUS_MPDU_END) || + (hal_status == HAL_RX_MON_STATUS_MSDU_END)); return hal_status; } diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index b60c172d131b..96954b2f7ca6 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -108,6 +108,9 @@ enum hal_rx_mon_status { HAL_RX_MON_STATUS_PPDU_NOT_DONE, HAL_RX_MON_STATUS_PPDU_DONE, HAL_RX_MON_STATUS_BUF_DONE, + HAL_RX_MON_STATUS_BUF_ADDR, + HAL_RX_MON_STATUS_MPDU_END, + HAL_RX_MON_STATUS_MSDU_END, }; #define HAL_RX_MAX_MPDU 256 From 3e522d2bab531b40edc9439e78cef2deeaad3017 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 042/465] wifi: ath12k: Handle monitor drop TLVs scenario JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3bcc4e830e05ed604bbba89c5415926df4ccabae Author: P Praneesh Date: Mon Dec 23 11:31:31 2024 +0530 wifi: ath12k: Handle monitor drop TLVs scenario During monitor destination ring back-pressure, hardware failed to send HAL_RX_PPDU_END_STATUS_DONE TLV. But driver uses this TLV as a delimiter to complete one PPDU worth of data parsing. This causes driver to overwrite the existing PPDU information with the new PPDU information. Fix it by recording the end reason which is provided under each buffer's descriptor in skb->cb and uses it while parsing TLV tags to mark the PPDU end delimiter. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-14-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp_mon.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 3d20a6b3b473..28db100cfac0 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -142,6 +142,7 @@ struct ath12k_skb_rxcb { u8 is_frag; u8 tid; u16 peer_id; + bool is_end_of_ppdu; }; enum ath12k_hw_rev { diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 23b1a41c6fd2..4e9a60181c06 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1139,6 +1139,7 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct sk_buff *skb) { struct hal_tlv_64_hdr *tlv; + struct ath12k_skb_rxcb *rxcb; enum hal_rx_mon_status hal_status; u16 tlv_tag, tlv_len; u8 *ptr = skb->data; @@ -1170,6 +1171,10 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, (hal_status == HAL_RX_MON_STATUS_MPDU_END) || (hal_status == HAL_RX_MON_STATUS_MSDU_END)); + rxcb = ATH12K_SKB_RXCB(skb); + if (rxcb->is_end_of_ppdu) + hal_status = HAL_RX_MON_STATUS_PPDU_DONE; + return hal_status; } @@ -2345,8 +2350,10 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, * HAL_MON_END_OF_PPDU to ensure that one PPDU worth of data is always * reaped. This helps to efficiently utilize the NAPI budget. */ - if (end_reason == HAL_MON_END_OF_PPDU) + if (end_reason == HAL_MON_END_OF_PPDU) { *budget -= 1; + rxcb->is_end_of_ppdu = true; + } end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { From 85518545959c4abf450e191d4cd126819a36015d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:26 +0200 Subject: [PATCH 043/465] wifi: ath12k: Enable monitor ring mask for QCN9274 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 58b976e4f4b4d727eea27c0e40830853ef7ecf0e Author: P Praneesh Date: Mon Dec 23 11:31:32 2024 +0530 wifi: ath12k: Enable monitor ring mask for QCN9274 QCN9274's monitor related rings are initialized properly. Hence enabling the corresponding ring mask in the hardware params. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20241223060132.3506372-15-quic_ppranees@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/hw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 14bbd446ad37..a106ebed7870 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -543,7 +543,11 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { ATH12K_TX_RING_MASK_3, }, .rx_mon_dest = { - 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + ATH12K_RX_MON_RING_MASK_0, + ATH12K_RX_MON_RING_MASK_1, + ATH12K_RX_MON_RING_MASK_2, }, .rx = { 0, 0, 0, 0, From 6d3fba1687edd85ba257587ecc0836f0fbf89097 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 044/465] wifi: mwifiex: Constify struct mwifiex_if_ops JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e50e30fa966e7f1ef407d5cdda22de629d64e82b Author: Christophe JAILLET Date: Sun Jan 19 18:48:39 2025 +0100 wifi: mwifiex: Constify struct mwifiex_if_ops 'struct mwifiex_if_ops' are not modified in these drivers. Constifying these structures moves some data to a read-only section, so increase overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 61439 4367 32 65838 1012e drivers/net/wireless/marvell/mwifiex/pcie.o After: ===== text data bss dec hex filename 61699 4127 32 65858 10142 drivers/net/wireless/marvell/mwifiex/pcie.o Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo Link: https://patch.msgid.link/03d524b72f20a0302e4de5e0ebdc20ab69469dec.1737308889.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/main.c | 4 ++-- drivers/net/wireless/marvell/mwifiex/main.h | 2 +- drivers/net/wireless/marvell/mwifiex/pcie.c | 4 ++-- drivers/net/wireless/marvell/mwifiex/sdio.c | 4 ++-- drivers/net/wireless/marvell/mwifiex/usb.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 855019fe5485..45eecb5f643b 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -54,7 +54,7 @@ const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; * proper cleanup before exiting. */ static int mwifiex_register(void *card, struct device *dev, - struct mwifiex_if_ops *if_ops, void **padapter) + const struct mwifiex_if_ops *if_ops, void **padapter) { struct mwifiex_adapter *adapter; int i; @@ -1713,7 +1713,7 @@ err_exit: */ int mwifiex_add_card(void *card, struct completion *fw_done, - struct mwifiex_if_ops *if_ops, u8 iface_type, + const struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev) { struct mwifiex_adapter *adapter; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 0674dcf7a537..fb15831201f7 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1470,7 +1470,7 @@ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *card, struct completion *fw_done, - struct mwifiex_if_ops *if_ops, u8 iface_type, + const struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev); int mwifiex_remove_card(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 5f997becdbaa..e11458fd4d50 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -21,7 +21,7 @@ #define PCIE_VERSION "1.0" #define DRV_NAME "Marvell mwifiex PCIe" -static struct mwifiex_if_ops pcie_ops; +static const struct mwifiex_if_ops pcie_ops; static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { .cmd_addr_lo = PCIE_SCRATCH_0_REG, @@ -3240,7 +3240,7 @@ static void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter) mwifiex_pcie_free_buffers(adapter); } -static struct mwifiex_if_ops pcie_ops = { +static const struct mwifiex_if_ops pcie_ops = { .init_if = mwifiex_init_pcie, .cleanup_if = mwifiex_cleanup_pcie, .check_fw_status = mwifiex_check_fw_status, diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 490ffd981164..c1fe48448839 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -21,7 +21,7 @@ static void mwifiex_sdio_work(struct work_struct *work); -static struct mwifiex_if_ops sdio_ops; +static const struct mwifiex_if_ops sdio_ops; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { .start_rd_port = 1, @@ -3167,7 +3167,7 @@ static void mwifiex_sdio_up_dev(struct mwifiex_adapter *adapter) dev_err(&card->func->dev, "error enabling SDIO port\n"); } -static struct mwifiex_if_ops sdio_ops = { +static const struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, .check_fw_status = mwifiex_check_fw_status, diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 6085cd50970d..3034c4405cb5 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -10,7 +10,7 @@ #define USB_VERSION "1.0" -static struct mwifiex_if_ops usb_ops; +static const struct mwifiex_if_ops usb_ops; static const struct usb_device_id mwifiex_usb_table[] = { /* 8766 */ @@ -1585,7 +1585,7 @@ mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) return 0; } -static struct mwifiex_if_ops usb_ops = { +static const struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, .wakeup = mwifiex_pm_wakeup_card, From febb7115c9f72dfc30af1e71ab750ceaf21f38f6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 045/465] wifi: ath12k: Fetch regdb.bin file from board-2.bin JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 24f587572acf7509127dbdfcbf1b681ef84eeba0 Author: Aaradhana Sahu Date: Thu Jan 16 08:58:35 2025 +0530 wifi: ath12k: Fetch regdb.bin file from board-2.bin Currently, ath12k_core_fetch_regdb() finds regdb.bin file through board id's but in board-2.bin file regdb.bin file is present with default board id because of which regdb.bin is not fetched. Add support to fetch regdb.bin file from board-2.bin through default board id. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250116032835.118397-1-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 2dd0666959cd..60c077b016b4 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -177,7 +177,7 @@ EXPORT_SYMBOL(ath12k_core_resume); static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len, bool with_variant, - bool bus_type_mode) + bool bus_type_mode, bool with_default) { /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; @@ -208,7 +208,9 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", ath12k_bus_str(ab->hif.bus), ab->qmi.target.chip_id, - ab->qmi.target.board_id, variant); + with_default ? + ATH12K_BOARD_ID_DEFAULT : ab->qmi.target.board_id, + variant); break; } @@ -220,19 +222,19 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, true, false); + return __ath12k_core_create_board_name(ab, name, name_len, true, false, false); } static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, false); + return __ath12k_core_create_board_name(ab, name, name_len, false, false, true); } static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, true); + return __ath12k_core_create_board_name(ab, name, name_len, false, true, true); } const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, From 52a8f9c2bf70ed5b52228d039821dcb1b2be2080 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 046/465] wifi: ath9k: return by of_get_mac_address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dfffb317519f88534bb82797f055f0a2fd867e7b Author: Rosen Penev Date: Tue Nov 5 14:23:26 2024 -0800 wifi: ath9k: return by of_get_mac_address When using nvmem, ath9k could potentially be loaded before nvmem, which loads after mtd. This is an issue if DT contains an nvmem mac address. If nvmem is not ready in time for ath9k, -EPROBE_DEFER is returned. Pass it to _probe so that ath9k can properly grab a potentially present MAC address. Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20241105222326.194417-1-rosenp@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath9k/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f9e77c4624d9..01e0dffbf57e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -647,7 +647,9 @@ static int ath9k_of_init(struct ath_softc *sc) ah->ah_flags |= AH_NO_EEP_SWAP; } - of_get_mac_address(np, common->macaddr); + ret = of_get_mac_address(np, common->macaddr); + if (ret == -EPROBE_DEFER) + return ret; return 0; } From 79e2fc51d7432cd7e65b2d92e07bbf6ad7002d23 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 047/465] wifi: ath9k: do not submit zero bytes to the entropy pool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0f2b59a98027a781eee1cbd48c7c8fdf87cb73f6 Author: Dmitry Antipov Date: Thu Jan 23 17:10:58 2025 +0300 wifi: ath9k: do not submit zero bytes to the entropy pool In 'ath_cmn_process_fft()', it doesn't make too much sense to add zero bytes in attempt to improve randomness. So swap calls to 'memset()' and 'add_device_randomness()' to feed the pool with actual FFT results rather than zeroes. Compile tested only. Signed-off-by: Dmitry Antipov Fixes: 2aa56cca3571 ("ath9k: Mix the received FFT bins to the random pool") Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20250123141058.1696502-1-dmantipov@yandex.ru Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath9k/common-spectral.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 628eeec4b82f..300d178830ad 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -628,12 +628,12 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h else RX_STAT_INC(sc, rx_spectral_sample_err); - memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); - /* Mix the received bins to the /dev/random * pool */ add_device_randomness(sample_buf, num_bins); + + memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); } /* Process a normal frame */ From 3eb9215e4eacd3e09304e810faf194e1a6de36f8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 048/465] wifi: ath11k: remove peer extra rssi update JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bee577165a0a375a1d47d8ddef1c321e0f7dd6c8 Author: Nicolas Escande Date: Fri Jan 17 19:19:53 2025 +0100 wifi: ath11k: remove peer extra rssi update Commit b205ce4c266c ("ath11k: support avg signal in station dump") added an extra assignment of arsta->rssi_comb in ath11k_dp_rx_update_peer_stats() when it added the average rssi support. So let's keep only one by removing the legacy assignment so the two statements about rssi stay next to each other. Compile tested only. Signed-off-by: Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250117181953.3375273-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/dp_rx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 5e71e5d9ecb7..640d617a585a 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2822,8 +2822,6 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, rx_stats->dcm_count += ppdu_info->dcm; rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; - arsta->rssi_comb = ppdu_info->rssi_comb; - BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); From b9bcd29ed69d75cea73634703c6b484f1b84ab25 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 049/465] wifi: ath12k: fix ath12k_hal_tx_cmd_ext_desc_setup() info1 override JIRA: https://issues.redhat.com/browse/RHEL-89168 commit df11edfba49e5fb69f4c9e7cb76082b89c417f78 Author: Nicolas Escande Date: Mon Jan 27 08:13:06 2025 +0100 wifi: ath12k: fix ath12k_hal_tx_cmd_ext_desc_setup() info1 override Since inception there is an obvious typo laying around in ath12k_hal_tx_cmd_ext_desc_setup(). Instead of initializing + adding flags to tcl_ext_cmd->info1, we initialize + override. This will be needed in the future to make broadcast frames work with ethernet encapsulation. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250127071306.1454699-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index aa8058dd2da6..94310bcc620c 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -119,7 +119,7 @@ static void ath12k_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->data_len, HAL_TX_MSDU_EXT_INFO1_BUF_LEN); - tcl_ext_cmd->info1 = le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | + tcl_ext_cmd->info1 |= le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | le32_encode_bits(ti->encap_type, HAL_TX_MSDU_EXT_INFO1_ENCAP_TYPE) | le32_encode_bits(ti->encrypt_type, From 6cc0b753a24ba743ad25032172e6a94df00870e1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 050/465] wifi: ath12k: remove return for empty tx bitrate in mac_op_sta_statistics JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8a5ad319f2e6f0462dbcb1bd3a6ba5097f629a5b Author: Remi Pommarel Date: Wed Jan 29 17:55:17 2025 +0100 wifi: ath12k: remove return for empty tx bitrate in mac_op_sta_statistics Currently in ath12k_mac_op_sta_statistics() there is the following logic: if (!arsta->txrate.legacy && !arsta->txrate.nss) return; Because ath12k_sta_statistics is used to report many info to iw wlan0 link, if it return for empty legacy and nss of arsta->txrate, then the other stats after it will not be set. To address this issue remove the return and instead invert the logic to set the txrate logic if (arsta->txrate.legacy || arsta->txrate.nss). The same was done also in both ath10k with commit 1cd6ba8ae33e ("ath10k: remove return for NL80211_STA_INFO_TX_BITRATE") and ath11k as well with commit 1d795645e1ee ("ath11k: remove return for empty tx bitrate in mac_op_sta_statistics"). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Remi Pommarel Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/38c2a7c4f7eaf57b9306bb95a9e6c42b7d987e05.1738169458.git.repk@triplefau.lt Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 4fb7e235be66..e9663c6ac72c 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10170,23 +10170,22 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_duration = arsta->tx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); - if (!arsta->txrate.legacy && !arsta->txrate.nss) - return; - - if (arsta->txrate.legacy) { - sinfo->txrate.legacy = arsta->txrate.legacy; - } else { - sinfo->txrate.mcs = arsta->txrate.mcs; - sinfo->txrate.nss = arsta->txrate.nss; - sinfo->txrate.bw = arsta->txrate.bw; - sinfo->txrate.he_gi = arsta->txrate.he_gi; - sinfo->txrate.he_dcm = arsta->txrate.he_dcm; - sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; - sinfo->txrate.eht_gi = arsta->txrate.eht_gi; - sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_ru_alloc; + if (arsta->txrate.legacy || arsta->txrate.nss) { + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + sinfo->txrate.he_gi = arsta->txrate.he_gi; + sinfo->txrate.he_dcm = arsta->txrate.he_dcm; + sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + sinfo->txrate.eht_gi = arsta->txrate.eht_gi; + sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_ru_alloc; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - sinfo->txrate.flags = arsta->txrate.flags; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); /* TODO: Use real NF instead of default one. */ signal = arsta->rssi_comb; From b85ae10c9c2a629b9ad58fa3baf08d453b136c1d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 051/465] wifi: rtw89: phy: rename to RTW89_PHY_NUM as proper naming JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f0dc53a7b77f900aa3d13309f84912b324ccca35 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:21 2025 +0800 wifi: rtw89: phy: rename to RTW89_PHY_NUM as proper naming The meaning is number of PHY, not maximum ID of PHY. Change to proper naming. No change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 38 ++++++++++----------- drivers/net/wireless/realtek/rtw89/core.c | 4 +-- drivers/net/wireless/realtek/rtw89/core.h | 36 +++++++++---------- drivers/net/wireless/realtek/rtw89/fw.h | 14 ++++---- drivers/net/wireless/realtek/rtw89/mac.c | 2 +- drivers/net/wireless/realtek/rtw89/mac_be.c | 2 +- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 7e43ba0c4098..755c157c815d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1602,7 +1602,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v4.wl_fw_info.fw_ver); dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i], sizeof(dm->gnt.band[i])); @@ -1634,7 +1634,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v5.rpt_info.fw_ver); dm->wl_fw_cx_offload = 0; - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1661,7 +1661,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v105.rpt_info.fw_ver); dm->wl_fw_cx_offload = 0; - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1687,7 +1687,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw_coex = le32_to_cpu(prpt->v7.rpt_info.cx_ver); wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1719,7 +1719,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw_coex = le32_to_cpu(prpt->v8.rpt_info.cx_ver); wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -2706,7 +2706,7 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st if (phy_map > BTC_PHY_ALL) return; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (!(phy_map & BIT(i))) continue; @@ -2755,7 +2755,7 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map, if (phy_map > BTC_PHY_ALL) return; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (!(phy_map & BIT(i))) continue; @@ -2955,7 +2955,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) if (ver->fwlrole == 0) { link_mode = wl->role_info.link_mode; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G) dbcc_2g_phy = i; } @@ -4240,7 +4240,7 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, case BTC_ANT_W2G: rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); if (rtwdev->dbcc_en) { - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G); gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; @@ -4896,9 +4896,9 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) if (rtwdev->dbcc_en) { if (ver->fwlrole == 0) { - wl_rinfo.dbcc_2g_phy = RTW89_PHY_MAX; + wl_rinfo.dbcc_2g_phy = RTW89_PHY_NUM; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (wl_dinfo->real_band[i] == RTW89_BAND_2G) wl_rinfo.dbcc_2g_phy = i; } @@ -5790,7 +5790,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; /* check dbcc role */ - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -5940,7 +5940,7 @@ static void _update_wl_info_v1(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -6090,7 +6090,7 @@ static void _update_wl_info_v2(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -6381,7 +6381,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; struct rtw89_btc_wl_active_role_v7 *act_role = NULL; - u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_MAX, phy_dbcc; + u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc; bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false; u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; @@ -6392,7 +6392,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) memset(wl_rinfo, 0, sizeof(*wl_rinfo)); for (i = 0; i < RTW89_PORT_NUM; i++) { - if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_MAX) + if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM) continue; act_role = &wl_rinfo->active_role[i]; @@ -6486,7 +6486,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, &dbcc_2g_phy); /* correct 2G-located PHY band for gnt ctrl */ - if (dbcc_2g_phy < RTW89_PHY_MAX) + if (dbcc_2g_phy < RTW89_PHY_NUM) wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G; } else if (b2g && b5g && cnt == 2) { mode = BTC_WLINK_25G_MCC; @@ -7179,7 +7179,7 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band) "[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); - if (phy_idx >= RTW89_PHY_MAX) + if (phy_idx >= RTW89_PHY_NUM) return; btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++; @@ -7229,7 +7229,7 @@ void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band) "[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); - if (phy_idx >= RTW89_PHY_MAX) + if (phy_idx >= RTW89_PHY_NUM) return; btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++; diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 85f739f1173d..b7987c6b1cd5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4432,9 +4432,9 @@ static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev) { int i; - for (i = 0; i < RTW89_PHY_MAX; i++) + for (i = 0; i < RTW89_PHY_NUM; i++) skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]); - for (i = 0; i < RTW89_PHY_MAX; i++) + for (i = 0; i < RTW89_PHY_NUM; i++) rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0f2a46e36b04..4e5949516a75 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -826,7 +826,7 @@ enum rtw89_mac_idx { enum rtw89_phy_idx { RTW89_PHY_0 = 0, RTW89_PHY_1 = 1, - RTW89_PHY_MAX + RTW89_PHY_NUM, }; #define __RTW89_MLD_MAX_LINK_NUM 2 @@ -1540,16 +1540,16 @@ struct rtw89_btc_u8_sta_chg { }; struct rtw89_btc_wl_scan_info { - u8 band[RTW89_PHY_MAX]; + u8 band[RTW89_PHY_NUM]; u8 phy_map; u8 rsvd; }; struct rtw89_btc_wl_dbcc_info { - u8 op_band[RTW89_PHY_MAX]; /* op band in each phy */ - u8 scan_band[RTW89_PHY_MAX]; /* scan band in each phy */ - u8 real_band[RTW89_PHY_MAX]; - u8 role[RTW89_PHY_MAX]; /* role in each phy */ + u8 op_band[RTW89_PHY_NUM]; /* op band in each phy */ + u8 scan_band[RTW89_PHY_NUM]; /* scan band in each phy */ + u8 real_band[RTW89_PHY_NUM]; + u8 role[RTW89_PHY_NUM]; /* role in each phy */ }; struct rtw89_btc_wl_active_role { @@ -1899,7 +1899,7 @@ struct rtw89_btc_wl_info { u8 cn_report; u8 coex_mode; u8 pta_req_mac; - u8 bt_polut_type[RTW89_PHY_MAX]; /* BT polluted WL-Tx type for phy0/1 */ + u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1 */ bool is_5g_hi_channel; bool pta_reg_mac_chg; @@ -2231,7 +2231,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v4 { struct rtw89_btc_fbtc_rpt_ctrl_wl_fw_info wl_fw_info; struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; __le32 bt_cnt[BTC_BCNT_STA_MAX]; - struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_MAX]; + struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_NUM]; } __packed; struct rtw89_btc_fbtc_rpt_ctrl_v5 { @@ -2239,7 +2239,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v5 { u8 rsvd; __le16 rsvd1; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX]; struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2251,7 +2251,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v105 { u8 rsvd; __le16 rsvd1; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2264,7 +2264,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v7 { u8 rsvd1; u8 rsvd2; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -2277,7 +2277,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 { u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */ u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */ - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -4763,7 +4763,7 @@ struct rtw89_hal { struct rtw89_chanctx chanctx[NUM_OF_RTW89_CHANCTX]; struct cfg80211_chan_def roc_chandef; - bool entity_active[RTW89_PHY_MAX]; + bool entity_active[RTW89_PHY_NUM]; bool entity_pause; enum rtw89_entity_mode entity_mode; struct rtw89_entity_mgnt entity_mgnt; @@ -4974,7 +4974,7 @@ struct rtw89_dpk_bkup_para { struct rtw89_dpk_info { bool is_dpk_enable; bool is_dpk_reload_en; - u8 dpk_gs[RTW89_PHY_MAX]; + u8 dpk_gs[RTW89_PHY_NUM]; u16 dc_i[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u16 dc_q[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u8 corr_val[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; @@ -5300,8 +5300,8 @@ struct rtw89_lps_parm { }; struct rtw89_ppdu_sts_info { - struct sk_buff_head rx_queue[RTW89_PHY_MAX]; - u8 curr_rx_ppdu_cnt[RTW89_PHY_MAX]; + struct sk_buff_head rx_queue[RTW89_PHY_NUM]; + u8 curr_rx_ppdu_cnt[RTW89_PHY_NUM]; }; struct rtw89_early_h2c { @@ -5420,8 +5420,8 @@ struct rtw89_phy_efuse_gain { bool offset_valid; bool comp_valid; s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ - s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */ - s8 rssi_base[RTW89_PHY_MAX]; /* S(8, 4) */ + s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */ + s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */ s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 2026bc2fd2ac..3cc514de073a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1803,14 +1803,14 @@ struct rtw89_h2c_lps_ml_cmn_info { u8 fmt_id; u8 rsvd0[3]; __le32 mlo_dbcc_mode; - u8 central_ch[RTW89_PHY_MAX]; - u8 pri_ch[RTW89_PHY_MAX]; - u8 bw[RTW89_PHY_MAX]; - u8 band[RTW89_PHY_MAX]; - u8 bcn_rate_type[RTW89_PHY_MAX]; + u8 central_ch[RTW89_PHY_NUM]; + u8 pri_ch[RTW89_PHY_NUM]; + u8 bw[RTW89_PHY_NUM]; + u8 band[RTW89_PHY_NUM]; + u8 bcn_rate_type[RTW89_PHY_NUM]; u8 rsvd1[2]; - __le16 tia_gain[RTW89_PHY_MAX][TIA_GAIN_NUM]; - u8 lna_gain[RTW89_PHY_MAX][LNA_GAIN_NUM]; + __le16 tia_gain[RTW89_PHY_NUM][TIA_GAIN_NUM]; + u8 lna_gain[RTW89_PHY_NUM][LNA_GAIN_NUM]; u8 rsvd2[2]; } __packed; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index a37c6d525d6f..f698807790ff 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6031,7 +6031,7 @@ int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl) if (wl) return 0; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { g[i].gnt_bt_sw_en = 1; g[i].gnt_bt = 1; g[i].gnt_wl_sw_en = 1; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 2dbdeae904ad..16ee378c5418 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1865,7 +1865,7 @@ int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl) if (wl) return 0; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { g[i].gnt_bt_sw_en = 1; g[i].gnt_bt = 1; g[i].gnt_wl_sw_en = 1; From c2e5a030964c30d091191d2de04b2fcdbbd1c16d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:27 +0200 Subject: [PATCH 052/465] wifi: rtw89: phy: add PHY context array to support functions per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 57a6cdf2feaf864903b88ae03fe9e0d56cdbd0ee Author: Ping-Ke Shih Date: Fri Jan 17 15:28:22 2025 +0800 wifi: rtw89: phy: add PHY context array to support functions per PHY To support MLO working on PHY 0 and PHY 1, extend existing PHY dynamic mechanism from PHY 0 to PHY 0/1. Implement a 2 elements array for two PHY instances, and pass pointer of each element as argument to existing functions. Then existing algorithm is kept, unchanged. Also provide iterate and get function to access PHY context. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++ drivers/net/wireless/realtek/rtw89/core.h | 46 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b7987c6b1cd5..42815a58a49a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4867,6 +4867,9 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtwdev->mlo_dbcc_mode = MLO_2_PLUS_0_1RF; } + rtwdev->bbs[RTW89_PHY_0].phy_idx = RTW89_PHY_0; + rtwdev->bbs[RTW89_PHY_1].phy_idx = RTW89_PHY_1; + INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4e5949516a75..d15d8c4046c1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5694,6 +5694,10 @@ struct rtw89_dev { struct rtw89_phy_ul_tb_info ul_tb_info; struct rtw89_antdiv_info antdiv; + struct rtw89_bb_ctx { + enum rtw89_phy_idx phy_idx; + } bbs[RTW89_PHY_NUM]; + struct delayed_work track_work; struct delayed_work chanctx_work; struct delayed_work coex_act1_work; @@ -6979,6 +6983,48 @@ static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev) } } +static inline u8 rtw89_get_active_phy_bitmap(struct rtw89_dev *rtwdev) +{ + if (!rtwdev->dbcc_en) + return BIT(RTW89_PHY_0); + + switch (rtwdev->mlo_dbcc_mode) { + case MLO_0_PLUS_2_1RF: + case MLO_0_PLUS_2_2RF: + return BIT(RTW89_PHY_1); + case MLO_1_PLUS_1_1RF: + case MLO_1_PLUS_1_2RF: + case MLO_2_PLUS_2_2RF: + case DBCC_LEGACY: + return BIT(RTW89_PHY_0) | BIT(RTW89_PHY_1); + case MLO_2_PLUS_0_1RF: + case MLO_2_PLUS_0_2RF: + default: + return BIT(RTW89_PHY_0); + } +} + +#define rtw89_for_each_active_bb(rtwdev, bb) \ + for (u8 __active_bb_bitmap = rtw89_get_active_phy_bitmap(rtwdev), \ + __phy_idx = 0; __phy_idx < RTW89_PHY_NUM; __phy_idx++) \ + if (__active_bb_bitmap & BIT(__phy_idx) && \ + (bb = &rtwdev->bbs[__phy_idx])) + +#define rtw89_for_each_capab_bb(rtwdev, bb) \ + for (u8 __phy_idx_max = rtwdev->dbcc_en ? RTW89_PHY_1 : RTW89_PHY_0, \ + __phy_idx = 0; __phy_idx <= __phy_idx_max; __phy_idx++) \ + if ((bb = &rtwdev->bbs[__phy_idx])) + +static inline +struct rtw89_bb_ctx *rtw89_get_bb_ctx(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + if (phy_idx >= RTW89_PHY_NUM) + return &rtwdev->bbs[RTW89_PHY_0]; + + return &rtwdev->bbs[phy_idx]; +} + static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev) { enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; From 8774c31ef84fd3d340bde76012b03c1e228d6d8e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 053/465] wifi: rtw89: phy: support env_monitor per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 11a625160a32243001b6c773a671dcacdd56c038 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:23 2025 +0800 wifi: rtw89: phy: support env_monitor per PHY The env_monitor is to collect information of current operating channel as helper to make decision with better hardware parameters to adopt current environment. Use PHY context as argument to control PHY accordingly, and use rtw89_phy_{write32,read32}_idx with phy_idx to access registers. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/phy.c | 304 +++++++++++++--------- drivers/net/wireless/realtek/rtw89/sar.c | 3 +- 3 files changed, 179 insertions(+), 130 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d15d8c4046c1..2f1bb51f6a0c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5683,7 +5683,6 @@ struct rtw89_dev { struct rtw89_power_trim_info pwr_trim; struct rtw89_cfo_tracking_info cfo_tracking; - struct rtw89_env_monitor_info env_monitor; struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; union { @@ -5696,6 +5695,7 @@ struct rtw89_dev { struct rtw89_bb_ctx { enum rtw89_phy_idx phy_idx; + struct rtw89_env_monitor_info env_monitor; } bbs[RTW89_PHY_NUM]; struct delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index c7c05f7fda1d..e266ddae0c31 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5199,24 +5199,27 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); } -static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, u32 time_us) +static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u32 time_us) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; return time_us >> (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx); } -static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, u16 idx) +static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u16 idx) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; return idx << (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx); } -static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; env->ccx_manual_ctrl = false; @@ -5225,17 +5228,20 @@ static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev) env->ccx_period = 0; env->ccx_unit_idx = RTW89_CCX_32_US; - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->en_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, - RTW89_CCX_EDCCA_BW20_0); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->en_mask, 1, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, + RTW89_CCX_EDCCA_BW20_0, bb->phy_idx); } -static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, u16 report, - u16 score) +static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, + u16 report, u16 score) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; u32 numer = 0; u16 ret = 0; @@ -5275,9 +5281,10 @@ static void rtw89_phy_ccx_ms_to_period_unit(struct rtw89_dev *rtwdev, *period, *unit_idx); } -static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "lv:(%d)->(0)\n", env->ccx_rac_lv); @@ -5288,9 +5295,10 @@ static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev) } static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, struct rtw89_ccx_para_info *para) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; bool is_update = env->ifs_clm_app != para->ifs_clm_app; u8 i = 0; u16 *ifs_th_l = env->ifs_clm_th_l; @@ -5325,12 +5333,12 @@ static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev, */ ifs_th_l[IFS_CLM_TH_START_IDX] = 0; ifs_th_h_us[IFS_CLM_TH_START_IDX] = ifs_th0_us; - ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, + ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th0_us); for (i = 1; i < RTW89_IFS_CLM_NUM; i++) { ifs_th_l[i] = ifs_th_h[i - 1] + 1; ifs_th_h_us[i] = ifs_th_h_us[i - 1] * ifs_th_times; - ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, ifs_th_h_us[i]); + ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th_h_us[i]); } ifs_update_finished: @@ -5341,30 +5349,31 @@ ifs_update_finished: return is_update; } -static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u8 i = 0; - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, - env->ifs_clm_th_l[0]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, - env->ifs_clm_th_l[1]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, - env->ifs_clm_th_l[2]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, - env->ifs_clm_th_l[3]); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, + env->ifs_clm_th_l[0], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, + env->ifs_clm_th_l[1], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, + env->ifs_clm_th_l[2], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, + env->ifs_clm_th_l[3], bb->phy_idx); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, - env->ifs_clm_th_h[0]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, - env->ifs_clm_th_h[1]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, - env->ifs_clm_th_h[2]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, - env->ifs_clm_th_h[3]); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, + env->ifs_clm_th_h[0], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, + env->ifs_clm_th_h[1], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, + env->ifs_clm_th_h[2], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, + env->ifs_clm_th_h[3], bb->phy_idx); for (i = 0; i < RTW89_IFS_CLM_NUM; i++) rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, @@ -5372,31 +5381,38 @@ static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev) i + 1, env->ifs_clm_th_l[i], env->ifs_clm_th_h[i]); } -static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; - struct rtw89_ccx_para_info para = {0}; + struct rtw89_ccx_para_info para = {}; env->ifs_clm_app = RTW89_IFS_CLM_BACKGROUND; env->ifs_clm_mntr_time = 0; para.ifs_clm_app = RTW89_IFS_CLM_INIT; - if (rtw89_phy_ifs_clm_th_update_check(rtwdev, ¶)) - rtw89_phy_ifs_clm_set_th_reg(rtwdev); + if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, ¶)) + rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true, + bb->phy_idx); } static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, enum rtw89_env_racing_lv level) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; int ret = 0; if (level >= RTW89_RAC_MAX_NUM) { @@ -5425,56 +5441,62 @@ static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev, return ret; } -static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, + bb->phy_idx); env->ccx_ongoing = true; } -static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; u8 i = 0; u32 res = 0; env->ifs_clm_tx_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_tx, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_tx, PERCENT); env->ifs_clm_edcca_excl_cca_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_edcca_excl_cca, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_edcca_excl_cca, PERCENT); env->ifs_clm_cck_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERCENT); env->ifs_clm_ofdm_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERCENT); env->ifs_clm_cck_cca_excl_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckcca_excl_fa, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckcca_excl_fa, PERCENT); env->ifs_clm_ofdm_cca_excl_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmcca_excl_fa, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmcca_excl_fa, PERCENT); env->ifs_clm_cck_fa_permil = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERMIL); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERMIL); env->ifs_clm_ofdm_fa_permil = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERMIL); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERMIL); for (i = 0; i < RTW89_IFS_CLM_NUM; i++) { if (env->ifs_clm_his[i] > ENV_MNTR_IFSCLM_HIS_MAX) { env->ifs_clm_ifs_avg[i] = ENV_MNTR_FAIL_DWORD; } else { env->ifs_clm_ifs_avg[i] = - rtw89_phy_ccx_idx_to_us(rtwdev, + rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_avg[i]); } - res = rtw89_phy_ccx_idx_to_us(rtwdev, env->ifs_clm_cca[i]); + res = rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_cca[i]); res += env->ifs_clm_his[i] >> 1; if (env->ifs_clm_his[i]) res /= env->ifs_clm_his[i]; @@ -5504,81 +5526,82 @@ static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev) env->ifs_clm_cca_avg[i]); } -static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev) +static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u8 i = 0; - if (rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, - ccx->ifs_cnt_done_mask) == 0) { + if (rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, + ccx->ifs_cnt_done_mask, bb->phy_idx) == 0) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Get IFS_CLM report Fail\n"); return false; } env->ifs_clm_tx = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, - ccx->ifs_clm_tx_cnt_msk); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, + ccx->ifs_clm_tx_cnt_msk, bb->phy_idx); env->ifs_clm_edcca_excl_cca = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, - ccx->ifs_clm_edcca_excl_cca_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, + ccx->ifs_clm_edcca_excl_cca_fa_mask, bb->phy_idx); env->ifs_clm_cckcca_excl_fa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, - ccx->ifs_clm_cckcca_excl_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, + ccx->ifs_clm_cckcca_excl_fa_mask, bb->phy_idx); env->ifs_clm_ofdmcca_excl_fa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, - ccx->ifs_clm_ofdmcca_excl_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, + ccx->ifs_clm_ofdmcca_excl_fa_mask, bb->phy_idx); env->ifs_clm_cckfa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, - ccx->ifs_clm_cck_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, + ccx->ifs_clm_cck_fa_mask, bb->phy_idx); env->ifs_clm_ofdmfa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, - ccx->ifs_clm_ofdm_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, + ccx->ifs_clm_ofdm_fa_mask, bb->phy_idx); env->ifs_clm_his[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t1_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t1_his_mask, bb->phy_idx); env->ifs_clm_his[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t2_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t2_his_mask, bb->phy_idx); env->ifs_clm_his[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t3_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t3_his_mask, bb->phy_idx); env->ifs_clm_his[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t4_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t4_his_mask, bb->phy_idx); env->ifs_clm_avg[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, - ccx->ifs_t1_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, + ccx->ifs_t1_avg_mask, bb->phy_idx); env->ifs_clm_avg[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, - ccx->ifs_t2_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, + ccx->ifs_t2_avg_mask, bb->phy_idx); env->ifs_clm_avg[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, - ccx->ifs_t3_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, + ccx->ifs_t3_avg_mask, bb->phy_idx); env->ifs_clm_avg[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, - ccx->ifs_t4_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, + ccx->ifs_t4_avg_mask, bb->phy_idx); env->ifs_clm_cca[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, - ccx->ifs_t1_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, + ccx->ifs_t1_cca_mask, bb->phy_idx); env->ifs_clm_cca[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, - ccx->ifs_t2_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, + ccx->ifs_t2_cca_mask, bb->phy_idx); env->ifs_clm_cca[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, - ccx->ifs_t3_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, + ccx->ifs_t3_cca_mask, bb->phy_idx); env->ifs_clm_cca[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, - ccx->ifs_t4_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, + ccx->ifs_t4_cca_mask, bb->phy_idx); env->ifs_clm_total_ifs = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, - ccx->ifs_total_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, + ccx->ifs_total_mask, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "IFS-CLM total_ifs = %d\n", env->ifs_clm_total_ifs); @@ -5598,16 +5621,17 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev) "T%d:[%d, %d, %d]\n", i + 1, env->ifs_clm_his[i], env->ifs_clm_avg[i], env->ifs_clm_cca[i]); - rtw89_phy_ifs_clm_get_utility(rtwdev); + rtw89_phy_ifs_clm_get_utility(rtwdev, bb); return true; } static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, struct rtw89_ccx_para_info *para) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u32 period = 0; u32 unit_idx = 0; @@ -5618,17 +5642,17 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, return -EINVAL; } - if (rtw89_phy_ccx_racing_ctrl(rtwdev, para->rac_lv)) + if (rtw89_phy_ccx_racing_ctrl(rtwdev, bb, para->rac_lv)) return -EINVAL; if (para->mntr_time != env->ifs_clm_mntr_time) { rtw89_phy_ccx_ms_to_period_unit(rtwdev, para->mntr_time, &period, &unit_idx); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, - ccx->ifs_clm_period_mask, period); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, - ccx->ifs_clm_cnt_unit_mask, - unit_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, + ccx->ifs_clm_period_mask, period, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, + ccx->ifs_clm_cnt_unit_mask, + unit_idx, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Update IFS-CLM time ((%d)) -> ((%d))\n", @@ -5639,18 +5663,19 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, env->ccx_unit_idx = (u8)unit_idx; } - if (rtw89_phy_ifs_clm_th_update_check(rtwdev, para)) { + if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, para)) { env->ifs_clm_app = para->ifs_clm_app; - rtw89_phy_ifs_clm_set_th_reg(rtwdev); + rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb); } return 0; } -void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; - struct rtw89_ccx_para_info para = {0}; + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_ccx_para_info para = {}; u8 chk_result = RTW89_PHY_ENV_MON_CCX_FAIL; env->ccx_watchdog_result = RTW89_PHY_ENV_MON_CCX_FAIL; @@ -5660,25 +5685,36 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) return; } + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "BB-%d env_monitor track\n", bb->phy_idx); + /* only ifs_clm for now */ - if (rtw89_phy_ifs_clm_get_result(rtwdev)) + if (rtw89_phy_ifs_clm_get_result(rtwdev, bb)) env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; - rtw89_phy_ccx_racing_release(rtwdev); + rtw89_phy_ccx_racing_release(rtwdev, bb); para.mntr_time = 1900; para.rac_lv = RTW89_RAC_LV_1; para.ifs_clm_app = RTW89_IFS_CLM_BACKGROUND; - if (rtw89_phy_ifs_clm_set(rtwdev, ¶) == 0) + if (rtw89_phy_ifs_clm_set(rtwdev, bb, ¶) == 0) chk_result |= RTW89_PHY_ENV_MON_IFS_CLM; if (chk_result) - rtw89_phy_ccx_trigger(rtwdev); + rtw89_phy_ccx_trigger(rtwdev, bb); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "get_result=0x%x, chk_result:0x%x\n", env->ccx_watchdog_result, chk_result); } +void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_env_monitor_track(rtwdev, bb); +} + static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page) { if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM || @@ -6020,7 +6056,7 @@ static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) { struct rtw89_dig_info *dig = &rtwdev->dig; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &rtwdev->bbs[0].env_monitor; enum rtw89_dig_noisy_level noisy_lv; u8 igi_offset = dig->fa_rssi_ofst; u16 fa_ratio = 0; @@ -6486,10 +6522,22 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0); } +static void __rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "BB-%d env_monitor init\n", bb->phy_idx); + + rtw89_phy_ccx_top_setting_init(rtwdev, bb); + rtw89_phy_ifs_clm_setting_init(rtwdev, bb); +} + static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) { - rtw89_phy_ccx_top_setting_init(rtwdev); - rtw89_phy_ifs_clm_setting_init(rtwdev); + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_env_monitor_init(rtwdev, bb); } static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 871f45a6508c..94db695e78e6 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -411,7 +411,8 @@ static const struct rtw89_reg_def txpwr_regs[] = { void rtw89_tas_track(struct rtw89_dev *rtwdev) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0); + struct rtw89_env_monitor_info *env = &bb->env_monitor; const enum rtw89_sar_sources src = rtwdev->sar.src; u8 max_nss_num = rtwdev->chip->rf_path_num; struct rtw89_tas_info *tas = &rtwdev->tas; From d9c21e1a59dd67a423f2baa29da6ec67e0a9f1ce Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 054/465] wifi: rtw89: phy: support DIG per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dc0ac60f2a92cc8a44636cbee3d6c32d0197cbe6 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:24 2025 +0800 wifi: rtw89: phy: support DIG per PHY DIG standing for dynamic initial gain is to define RX coverage. Adjust this value dynamically to gain good RX performance. Use PHY context to extend DIG algorithm to support two PHY. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 4 +- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/phy.c | 226 +++++++++++++--------- drivers/net/wireless/realtek/rtw89/phy.h | 2 +- 4 files changed, 137 insertions(+), 97 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 42815a58a49a..e39fb696ac13 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4925,6 +4925,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool hw_scan) { struct ieee80211_bss_conf *bss_conf; + struct rtw89_bb_ctx *bb; if (!rtwvif_link) return; @@ -4943,7 +4944,8 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rtw89_phy_config_edcca(rtwdev, false); rtwdev->scanning = false; - rtwdev->dig.bypass_dig = true; + rtw89_for_each_active_bb(rtwdev, bb) + bb->dig.bypass_dig = true; if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2f1bb51f6a0c..42632d1c981f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5683,7 +5683,6 @@ struct rtw89_dev { struct rtw89_power_trim_info pwr_trim; struct rtw89_cfo_tracking_info cfo_tracking; - struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; union { struct rtw89_phy_bb_gain_info ax; @@ -5696,6 +5695,7 @@ struct rtw89_dev { struct rtw89_bb_ctx { enum rtw89_phy_idx phy_idx; struct rtw89_env_monitor_info env_monitor; + struct rtw89_dig_info dig; } bbs[RTW89_PHY_NUM]; struct delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e266ddae0c31..bae8d8643490 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5835,11 +5835,12 @@ static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) __rtw89_physts_parsing_init(rtwdev, RTW89_PHY_1); } -static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) +static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, int type) { const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_dig_info *dig = &rtwdev->dig; const struct rtw89_phy_dig_gain_cfg *cfg; + struct rtw89_dig_info *dig = &bb->dig; const char *msg; u8 i; s8 gain_base; @@ -5876,8 +5877,8 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) } for (i = 0; i < cfg->size; i++) { - tmp = rtw89_phy_read32_mask(rtwdev, cfg->table[i].addr, - cfg->table[i].mask); + tmp = rtw89_phy_read32_idx(rtwdev, cfg->table[i].addr, + cfg->table[i].mask, bb->phy_idx); tmp >>= DIG_GAIN_SHIFT; gain_arr[i] = sign_extend32(tmp, U4_MAX_BIT) + gain_base; gain_base += DIG_GAIN; @@ -5887,25 +5888,26 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) } } -static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u32 tmp; u8 i; if (!rtwdev->hal.support_igi) return; - tmp = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PKPW, - B_PATH0_IB_PKPW_MSK); + tmp = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PKPW, + B_PATH0_IB_PKPW_MSK, bb->phy_idx); dig->ib_pkpwr = sign_extend32(tmp >> DIG_GAIN_SHIFT, U8_MAX_BIT); - dig->ib_pbk = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PBK, - B_PATH0_IB_PBK_MSK); + dig->ib_pbk = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PBK, + B_PATH0_IB_PBK_MSK, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "ib_pkpwr=%d, ib_pbk=%d\n", dig->ib_pkpwr, dig->ib_pbk); for (i = RTW89_DIG_GAIN_LNA_G; i < RTW89_DIG_GAIN_MAX; i++) - rtw89_phy_dig_read_gain_table(rtwdev, i); + rtw89_phy_dig_read_gain_table(rtwdev, bb, i); } static const u8 rssi_nolink = 22; @@ -5914,10 +5916,11 @@ static const u16 fa_th_2g[FA_TH_NUM] = {22, 44, 66, 88}; static const u16 fa_th_5g[FA_TH_NUM] = {4, 8, 12, 16}; static const u16 fa_th_nolink[FA_TH_NUM] = {196, 352, 440, 528}; -static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; if (is_linked) { @@ -5928,10 +5931,11 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev) } } -static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; const u16 *fa_th_src = NULL; @@ -5960,9 +5964,10 @@ static const u8 pd_low_th_offset = 16, dynamic_igi_min = 0x20; static const u8 igi_max_performance_mode = 0x5a; static const u8 dynamic_pd_threshold_max; -static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; dig->cur_gaincode.lna_idx = LNA_IDX_MAX; dig->cur_gaincode.tia_idx = TIA_IDX_MAX; @@ -5978,15 +5983,27 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev) dig->is_linked_pre = false; } -static void rtw89_phy_dig_init(struct rtw89_dev *rtwdev) +static void __rtw89_phy_dig_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - rtw89_phy_dig_update_gain_para(rtwdev); - rtw89_phy_dig_reset(rtwdev); + rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig_init\n", bb->phy_idx); + + rtw89_phy_dig_update_gain_para(rtwdev, bb); + rtw89_phy_dig_reset(rtwdev, bb); } -static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static void rtw89_phy_dig_init(struct rtw89_dev *rtwdev) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_dig_init(rtwdev, bb); +} + +static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi) +{ + struct rtw89_dig_info *dig = &bb->dig; u8 lna_idx; if (rssi < dig->igi_rssi_th[0]) @@ -6005,9 +6022,10 @@ static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) return lna_idx; } -static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u8 tia_idx; if (rssi < dig->igi_rssi_th[0]) @@ -6020,10 +6038,11 @@ static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) #define IB_PBK_BASE 110 #define WB_RSSI_BASE 10 -static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi, struct rtw89_agc_gaincode_set *set) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; s8 lna_gain = dig->lna_gain[set->lna_idx]; s8 tia_gain = dig->tia_gain[set->tia_idx]; s32 wb_rssi = rssi + lna_gain + tia_gain; @@ -6039,12 +6058,13 @@ static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, return rxb_idx; } -static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi, struct rtw89_agc_gaincode_set *set) { - set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, rssi); - set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, rssi); - set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, rssi, set); + set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, bb, rssi); + set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, bb, rssi); + set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, bb, rssi, set); rtw89_debug(rtwdev, RTW89_DBG_DIG, "final_rssi=%03d, (lna,tia,rab)=(%d,%d,%02d)\n", @@ -6053,10 +6073,11 @@ static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, #define IGI_OFFSET_MAX 25 #define IGI_OFFSET_MUL 2 -static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; - struct rtw89_env_monitor_info *env = &rtwdev->bbs[0].env_monitor; + struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_env_monitor_info *env = &bb->env_monitor; enum rtw89_dig_noisy_level noisy_lv; u8 igi_offset = dig->fa_rssi_ofst; u16 fa_ratio = 0; @@ -6093,92 +6114,99 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) noisy_lv, igi_offset); } -static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, u8 lna_idx) +static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 lna_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_lna_init.addr, - dig_regs->p0_lna_init.mask, lna_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_lna_init.addr, - dig_regs->p1_lna_init.mask, lna_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_lna_init.addr, + dig_regs->p0_lna_init.mask, lna_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_lna_init.addr, + dig_regs->p1_lna_init.mask, lna_idx, bb->phy_idx); } -static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, u8 tia_idx) +static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 tia_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_tia_init.addr, - dig_regs->p0_tia_init.mask, tia_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_tia_init.addr, - dig_regs->p1_tia_init.mask, tia_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_tia_init.addr, + dig_regs->p0_tia_init.mask, tia_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_tia_init.addr, + dig_regs->p1_tia_init.mask, tia_idx, bb->phy_idx); } -static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, u8 rxb_idx) +static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rxb_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_rxb_init.addr, - dig_regs->p0_rxb_init.mask, rxb_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_rxb_init.addr, - dig_regs->p1_rxb_init.mask, rxb_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_rxb_init.addr, + dig_regs->p0_rxb_init.mask, rxb_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_rxb_init.addr, + dig_regs->p1_rxb_init.mask, rxb_idx, bb->phy_idx); } static void rtw89_phy_dig_set_igi_cr(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, const struct rtw89_agc_gaincode_set set) { if (!rtwdev->hal.support_igi) return; - rtw89_phy_dig_set_lna_idx(rtwdev, set.lna_idx); - rtw89_phy_dig_set_tia_idx(rtwdev, set.tia_idx); - rtw89_phy_dig_set_rxb_idx(rtwdev, set.rxb_idx); + rtw89_phy_dig_set_lna_idx(rtwdev, bb, set.lna_idx); + rtw89_phy_dig_set_tia_idx(rtwdev, bb, set.tia_idx); + rtw89_phy_dig_set_rxb_idx(rtwdev, bb, set.rxb_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "Set (lna,tia,rxb)=((%d,%d,%02d))\n", set.lna_idx, set.tia_idx, set.rxb_idx); } static void rtw89_phy_dig_sdagc_follow_pagc_config(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool enable) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, - dig_regs->p0_p20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, - dig_regs->p0_s20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, - dig_regs->p1_p20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, - dig_regs->p1_s20_pagcugc_en.mask, enable); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, + dig_regs->p0_p20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, + dig_regs->p0_s20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, + dig_regs->p1_p20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, + dig_regs->p1_s20_pagcugc_en.mask, enable, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "sdagc_follow_pagc=%d\n", enable); } -static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; if (!rtwdev->hal.support_igi) return; if (dig->force_gaincode_idx_en) { - rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode); rtw89_debug(rtwdev, RTW89_DBG_DIG, "Force gaincode index enabled.\n"); } else { - rtw89_phy_dig_gaincode_by_rssi(rtwdev, dig->igi_fa_rssi, + rtw89_phy_dig_gaincode_by_rssi(rtwdev, bb, dig->igi_fa_rssi, &dig->cur_gaincode); - rtw89_phy_dig_set_igi_cr(rtwdev, dig->cur_gaincode); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->cur_gaincode); } } -static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, - bool enable) +static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, + u8 rssi, bool enable) { - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; enum rtw89_bandwidth cbw = chan->band_width; - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u8 final_rssi = 0, under_region = dig->pd_low_th_ofst; u8 ofdm_cca_th; s8 cck_cca_th; @@ -6220,10 +6248,10 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, "Dynamic PD th disabled, Set PD_low_bd=0\n"); } - rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, - dig_regs->pd_lower_bound_mask, pd_val); - rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, - dig_regs->pd_spatial_reuse_en, enable); + rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, + dig_regs->pd_lower_bound_mask, pd_val, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, + dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx); if (!rtwdev->hal.support_cckpd) return; @@ -6235,29 +6263,29 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", final_rssi, cck_cca_th, under_region, pd_val); - rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_reg, - dig_regs->bmode_cca_rssi_limit_en, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_lower_bound_reg, - dig_regs->bmode_rssi_nocca_low_th_mask, pd_val); + rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg, + dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_lower_bound_reg, + dig_regs->bmode_rssi_nocca_low_th_mask, pd_val, bb->phy_idx); } -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev) +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; dig->bypass_dig = false; - rtw89_phy_dig_para_reset(rtwdev); - rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); - rtw89_phy_dig_dyn_pd_th(rtwdev, rssi_nolink, false); - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_para_reset(rtwdev, bb); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode); + rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi_nolink, false); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); + rtw89_phy_dig_update_para(rtwdev, bb); } #define IGI_RSSI_MIN 10 #define ABS_IGI_MIN 0xc -void rtw89_phy_dig(struct rtw89_dev *rtwdev) +static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; u8 igi_min; @@ -6266,20 +6294,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev) return; } - rtw89_phy_dig_update_rssi_info(rtwdev); + rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig track\n", bb->phy_idx); + + rtw89_phy_dig_update_rssi_info(rtwdev, bb); if (!dig->is_linked_pre && is_linked) { rtw89_debug(rtwdev, RTW89_DBG_DIG, "First connected\n"); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_update_para(rtwdev, bb); dig->igi_fa_rssi = dig->igi_rssi; } else if (dig->is_linked_pre && !is_linked) { rtw89_debug(rtwdev, RTW89_DBG_DIG, "First disconnected\n"); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_update_para(rtwdev, bb); dig->igi_fa_rssi = dig->igi_rssi; } dig->is_linked_pre = is_linked; - rtw89_phy_dig_igi_offset_by_env(rtwdev); + rtw89_phy_dig_igi_offset_by_env(rtwdev, bb); igi_min = max_t(int, dig->igi_rssi - IGI_RSSI_MIN, 0); dig->dyn_igi_max = min(igi_min + IGI_OFFSET_MAX, igi_max_performance_mode); @@ -6298,14 +6328,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev) dig->igi_rssi, dig->dyn_igi_max, dig->dyn_igi_min, dig->igi_fa_rssi); - rtw89_phy_dig_config_igi(rtwdev); + rtw89_phy_dig_config_igi(rtwdev, bb); - rtw89_phy_dig_dyn_pd_th(rtwdev, dig->igi_fa_rssi, dig->dyn_pd_th_en); + rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, dig->dyn_pd_th_en); if (dig->dyn_pd_th_en && dig->igi_fa_rssi > dig->dyn_pd_th_max) - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, true); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, true); else - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); +} + +void rtw89_phy_dig(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_dig(rtwdev, bb); } static void __rtw89_phy_tx_path_div_sta_iter(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 08b635c93ac3..db748c7453cd 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -985,7 +985,7 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev); void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev); void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 val); -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev); +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb); void rtw89_phy_dig(struct rtw89_dev *rtwdev); void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev, From 43ddceb4ffe6529e9f00f3d39b8428e746008984 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 055/465] wifi: rtw89: phy: support ch_info per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 786e485c61efaca1b986fc5b83b1d2132fe5b88b Author: Ping-Ke Shih Date: Fri Jan 17 15:28:25 2025 +0800 wifi: rtw89: phy: support ch_info per PHY The ch_info is to record current channel info such as minimum RSSI of connected stations, and to assist in dynamic mechanisms of DIG and EDCCA. Move the struct to PHY context, so the dynamic mechanisms can use the info of the same PHY. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-6-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 42632d1c981f..ca2719957bbf 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5696,6 +5696,7 @@ struct rtw89_dev { enum rtw89_phy_idx phy_idx; struct rtw89_env_monitor_info env_monitor; struct rtw89_dig_info dig; + struct rtw89_phy_ch_info ch_info; } bbs[RTW89_PHY_NUM]; struct delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index bae8d8643490..e07f874590d2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5115,7 +5115,6 @@ static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev) struct rtw89_phy_iter_rssi_data { struct rtw89_dev *rtwdev; - struct rtw89_phy_ch_info *ch_info; bool rssi_changed; }; @@ -5123,10 +5122,15 @@ static void __rtw89_phy_stat_rssi_update_iter(struct rtw89_sta_link *rtwsta_link, struct rtw89_phy_iter_rssi_data *rssi_data) { - struct rtw89_phy_ch_info *ch_info = rssi_data->ch_info; + struct rtw89_vif_link *rtwvif_link = rtwsta_link->rtwvif_link; + struct rtw89_dev *rtwdev = rssi_data->rtwdev; + struct rtw89_phy_ch_info *ch_info; + struct rtw89_bb_ctx *bb; unsigned long rssi_curr; rssi_curr = ewma_rssi_read(&rtwsta_link->avg_rssi); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + ch_info = &bb->ch_info; if (rssi_curr < ch_info->rssi_min) { ch_info->rssi_min = rssi_curr; @@ -5157,11 +5161,13 @@ static void rtw89_phy_stat_rssi_update_iter(void *data, static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev) { - struct rtw89_phy_iter_rssi_data rssi_data = {0}; + struct rtw89_phy_iter_rssi_data rssi_data = {}; + struct rtw89_bb_ctx *bb; rssi_data.rtwdev = rtwdev; - rssi_data.ch_info = &rtwdev->ch_info; - rssi_data.ch_info->rssi_min = U8_MAX; + rtw89_for_each_active_bb(rtwdev, bb) + bb->ch_info.rssi_min = U8_MAX; + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_phy_stat_rssi_update_iter, &rssi_data); @@ -5919,7 +5925,7 @@ static const u16 fa_th_nolink[FA_TH_NUM] = {196, 352, 440, 528}; static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; + struct rtw89_phy_ch_info *ch_info = &bb->ch_info; struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; From 6e2f6d7dac7623b186797038cc549f2751dce8a9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 056/465] wifi: rtw89: phy: support EDCCA per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit af5fa884e22feae23643515bbeec53a021e06b84 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:26 2025 +0800 wifi: rtw89: phy: support EDCCA per PHY Dynamic mechanism EDCCA (Energy Detection Clear Channel Assessment) is to dynamically adjusted to make EDCCA suitable for situations. Use PHY context to support two PHY. For the EDCCA log part, registers to report EDCCA for PHY 1 is not a simple offset from PHY 0, so add them by separate patch. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-7-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 6 +- drivers/net/wireless/realtek/rtw89/core.h | 3 +- drivers/net/wireless/realtek/rtw89/phy.c | 105 +++++++++++++--------- drivers/net/wireless/realtek/rtw89/phy.h | 5 +- 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index e39fb696ac13..b730f70c82aa 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4906,6 +4906,7 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); rtwdev->scanning = true; rtw89_leave_lps(rtwdev); @@ -4916,7 +4917,7 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv rtw89_btc_ntfy_scan_start(rtwdev, rtwvif_link->phy_idx, chan->band_type); rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true); rtw89_hci_recalc_int_mit(rtwdev); - rtw89_phy_config_edcca(rtwdev, true); + rtw89_phy_config_edcca(rtwdev, bb, true); rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr); } @@ -4941,7 +4942,8 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false); rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx); - rtw89_phy_config_edcca(rtwdev, false); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + rtw89_phy_config_edcca(rtwdev, bb, false); rtwdev->scanning = false; rtw89_for_each_active_bb(rtwdev, bb) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ca2719957bbf..c8b4809c2f36 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4768,7 +4768,6 @@ struct rtw89_hal { enum rtw89_entity_mode entity_mode; struct rtw89_entity_mgnt entity_mgnt; - struct rtw89_edcca_bak edcca_bak; u32 disabled_dm_bitmap; /* bitmap of enum rtw89_dm_type */ u8 thermal_prot_th; @@ -5683,7 +5682,6 @@ struct rtw89_dev { struct rtw89_power_trim_info pwr_trim; struct rtw89_cfo_tracking_info cfo_tracking; - struct rtw89_phy_ch_info ch_info; union { struct rtw89_phy_bb_gain_info ax; struct rtw89_phy_bb_gain_info_be be; @@ -5697,6 +5695,7 @@ struct rtw89_dev { struct rtw89_env_monitor_info env_monitor; struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; + struct rtw89_edcca_bak edcca_bak; } bbs[RTW89_PHY_NUM]; struct delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e07f874590d2..f1029da4a78e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -6584,10 +6584,13 @@ static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) __rtw89_phy_env_monitor_init(rtwdev, bb); } -static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; + + rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca init\n", bb->phy_idx); memset(edcca_bak, 0, sizeof(*edcca_bak)); @@ -6603,8 +6606,16 @@ static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) rtw89_phy_set_phy_regs(rtwdev, R_DFS_FFT_CG, B_DFS_FFT_EN, 1); } - rtw89_phy_write32_mask(rtwdev, edcca_regs->tx_collision_t2r_st, - edcca_regs->tx_collision_t2r_st_mask, 0x29); + rtw89_phy_write32_idx(rtwdev, edcca_regs->tx_collision_t2r_st, + edcca_regs->tx_collision_t2r_st_mask, 0x29, bb->phy_idx); +} + +static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_edcca_init(rtwdev, bb); } void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) @@ -6967,42 +6978,43 @@ void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, } EXPORT_SYMBOL(rtw89_decode_chan_idx); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan) +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool scan) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; if (scan) { edcca_bak->a = - rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask); + rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, bb->phy_idx); edcca_bak->p = - rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask); + rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, bb->phy_idx); edcca_bak->ppdu = - rtw89_phy_read32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask); + rtw89_phy_read32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, bb->phy_idx); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, EDCCA_MAX); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, EDCCA_MAX); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, EDCCA_MAX); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, EDCCA_MAX, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, EDCCA_MAX, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, EDCCA_MAX, bb->phy_idx); } else { - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, - edcca_bak->a); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, - edcca_bak->p); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, - edcca_bak->ppdu); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, + edcca_bak->a, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, + edcca_bak->p, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, + edcca_bak->ppdu, bb->phy_idx); } } -static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) +static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; bool flag_fb, flag_p20, flag_s20, flag_s40, flag_s80; @@ -7099,9 +7111,10 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80); } -static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev) +static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; + struct rtw89_phy_ch_info *ch_info = &bb->ch_info; bool is_linked = rtwdev->total_sta_assoc > 0; u8 rssi_min = ch_info->rssi_min >> 1; u8 edcca_thre; @@ -7117,13 +7130,13 @@ static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev) return edcca_thre; } -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev) +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; u8 th; - th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev); + th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev, bb); if (th == edcca_bak->th_old) return; @@ -7132,23 +7145,33 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "[EDCCA]: Normal Mode, EDCCA_th = %d\n", th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, th); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, th, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, th, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, th, bb->phy_idx); +} + +static +void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ + rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca track\n", bb->phy_idx); + + rtw89_phy_edcca_thre_calc(rtwdev, bb); + rtw89_phy_edcca_log(rtwdev, bb); } void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_bb_ctx *bb; if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DYNAMIC_EDCCA)) return; - rtw89_phy_edcca_thre_calc(rtwdev); - rtw89_phy_edcca_log(rtwdev); + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_edcca_track(rtwdev, bb); } enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index db748c7453cd..33d466c519e3 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -1002,9 +1002,10 @@ void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev); u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band); void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, u8 *ch, enum nl80211_band *band); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan); +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool scan); void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev); -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev); +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb); enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev, From e318f474f8efbc5074964528f402aedda4946184 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 057/465] wifi: rtw89: phy: support EDCCA log per PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0a51f04a9afe98bf60dbfcef5ccf6a120398c356 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:27 2025 +0800 wifi: rtw89: phy: support EDCCA log per PHY The registers of EDCCA log for PHY 1 isn't a simple offset, so define them accordingly. Then the function use register set to access reports according to phy_idx. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-8-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 10 ++-- drivers/net/wireless/realtek/rtw89/phy.c | 48 +++++++++++-------- drivers/net/wireless/realtek/rtw89/reg.h | 5 ++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 15 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 15 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 15 ++++-- .../net/wireless/realtek/rtw89/rtw8852bt.c | 15 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 15 ++++-- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 15 ++++-- 9 files changed, 104 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c8b4809c2f36..970730291e56 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4190,10 +4190,12 @@ struct rtw89_edcca_regs { u32 edcca_p_mask; u32 ppdu_level; u32 ppdu_mask; - u32 rpt_a; - u32 rpt_b; - u32 rpt_sel; - u32 rpt_sel_mask; + struct rtw89_edcca_p_regs { + u32 rpt_a; + u32 rpt_b; + u32 rpt_sel; + u32 rpt_sel_mask; + } p[RTW89_PHY_NUM]; u32 rpt_sel_be; u32 rpt_sel_be_mask; u32 tx_collision_t2r_st; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f1029da4a78e..a5299295b777 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -7017,6 +7017,7 @@ void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; + const struct rtw89_edcca_p_regs *edcca_p_regs; bool flag_fb, flag_p20, flag_s20, flag_s40, flag_s80; s8 pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80; u8 path, per20_bitmap; @@ -7026,13 +7027,18 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *b if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_EDCCA)) return; + if (bb->phy_idx == RTW89_PHY_1) + edcca_p_regs = &edcca_regs->p[RTW89_PHY_1]; + else + edcca_p_regs = &edcca_regs->p[RTW89_PHY_0]; + if (rtwdev->chip->chip_id == RTL8922A) rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 0); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 0); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 0); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); path = u32_get_bits(tmp, B_EDCCA_RPT_B_PATH_MASK); flag_s80 = u32_get_bits(tmp, B_EDCCA_RPT_B_S80); flag_s40 = u32_get_bits(tmp, B_EDCCA_RPT_B_S40); @@ -7043,19 +7049,19 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *b pwdb_p20 = u32_get_bits(tmp, MASKBYTE2); pwdb_fb = u32_get_bits(tmp, MASKBYTE3); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 4); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 4); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb_s80 = u32_get_bits(tmp, MASKBYTE1); pwdb_s40 = u32_get_bits(tmp, MASKBYTE2); - per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_regs->rpt_a, + per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_p_regs->rpt_a, MASKBYTE0); if (rtwdev->chip->chip_id == RTL8922A) { rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 4); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb[0] = u32_get_bits(tmp, MASKBYTE3); pwdb[1] = u32_get_bits(tmp, MASKBYTE2); pwdb[2] = u32_get_bits(tmp, MASKBYTE1); @@ -7063,33 +7069,33 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *b rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 5); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb[4] = u32_get_bits(tmp, MASKBYTE3); pwdb[5] = u32_get_bits(tmp, MASKBYTE2); pwdb[6] = u32_get_bits(tmp, MASKBYTE1); pwdb[7] = u32_get_bits(tmp, MASKBYTE0); } else { - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 0); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 0); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[0] = u32_get_bits(tmp, MASKBYTE3); pwdb[1] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 1); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 1); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[2] = u32_get_bits(tmp, MASKBYTE3); pwdb[3] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 2); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 2); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[4] = u32_get_bits(tmp, MASKBYTE3); pwdb[5] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 3); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 3); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[6] = u32_get_bits(tmp, MASKBYTE3); pwdb[7] = u32_get_bits(tmp, MASKBYTE2); } diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 10d0efa7a58e..c992835d4b63 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8157,6 +8157,8 @@ #define B_EDCCA_RPT_B_S40 BIT(4) #define B_EDCCA_RPT_B_S80 BIT(3) #define B_EDCCA_RPT_B_PATH_MASK GENMASK(2, 1) +#define R_EDCCA_RPT_P1_A 0x1740 +#define R_EDCCA_RPT_P1_B 0x1744 #define R_SWSI_V1 0x174C #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) @@ -8222,6 +8224,7 @@ #define B_TXCKEN_FORCE_ALL GENMASK(24, 0) #define R_EDCCA_RPT_SEL 0x20CC #define B_EDCCA_RPT_SEL_MSK GENMASK(2, 0) +#define B_EDCCA_RPT_SEL_P1_MSK GENMASK(5, 3) #define R_ADC_FIFO 0x20fc #define B_ADC_FIFO_RST GENMASK(31, 24) #define B_ADC_FIFO_RXK GENMASK(31, 16) @@ -8291,6 +8294,8 @@ #define B_P1_EN_SOUND_WO_NDP BIT(1) #define R_EDCCA_RPT_A_BE 0x2E38 #define R_EDCCA_RPT_B_BE 0x2E3C +#define R_EDCCA_RPT_P1_A_BE 0x2E40 +#define R_EDCCA_RPT_P1_B_BE 0x2E44 #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P1_RXCK 0x32A0 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 0487d1cbe605..82289dbad1f4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -224,10 +224,17 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 8a9a5201b2ee..2046832d021f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -522,10 +522,17 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index cbaf54f88af6..652914a36245 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -189,10 +189,17 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index c0ae5dca3049..6f15245b2f74 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -187,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 106df618bb58..ecc1ff358583 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -186,10 +186,17 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 11d66bfceb15..898a65a721dc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -205,10 +205,17 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_PPDU_LVL_BE, .ppdu_mask = B_EDCCA_LVL_MSK1, - .rpt_a = R_EDCCA_RPT_A_BE, - .rpt_b = R_EDCCA_RPT_B_BE, - .rpt_sel = R_EDCCA_RPT_SEL_BE, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A_BE, + .rpt_b = R_EDCCA_RPT_B_BE, + .rpt_sel = R_EDCCA_RPT_SEL_BE, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A_BE, + .rpt_b = R_EDCCA_RPT_P1_B_BE, + .rpt_sel = R_EDCCA_RPT_SEL_BE, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .rpt_sel_be = R_EDCCA_RPTREG_SEL_BE, .rpt_sel_be_mask = B_EDCCA_RPTREG_SEL_BE_MSK, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST_BE, From 9b47666922b56830be40ce8d199e15762881c9a0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 058/465] wifi: rtw89: phy: disable CFO track when two PHY are working simultaneously JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 076652f56ed6c5eea2e2c05b1fc805094045c7b9 Author: Ping-Ke Shih Date: Fri Jan 17 15:28:28 2025 +0800 wifi: rtw89: phy: disable CFO track when two PHY are working simultaneously To have good performance, adjust hardware XTAL to track CFO (carrier frequency offset). However, there is only one hardware XTAL, so it is not possible to track on two PHY simultaneously. It also can't track on single one PHY when two PHY are working, because the adjustment of XTAL will affect all PHY. Thus, disable CFO track for this case. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250117072828.16728-9-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index a5299295b777..5b0cf8314fb2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4600,7 +4600,7 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev) cfo->dcfo_avg = 0; rtw89_debug(rtwdev, RTW89_DBG_CFO, "CFO:total_sta_assoc=%d\n", rtwdev->total_sta_assoc); - if (rtwdev->total_sta_assoc == 0) { + if (rtwdev->total_sta_assoc == 0 || rtw89_is_mlo_1_1(rtwdev)) { rtw89_phy_cfo_reset(rtwdev); return; } From aef01a4555390b54f79c37aa2193d8acfd26b702 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 059/465] wifi: rtw89: regd: support loading regd table from fw element JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e7196b32a43de0e230b29b5682ea7af83568b903 Author: Zong-Zhe Yang Date: Mon Jan 20 11:27:21 2025 +0800 wifi: rtw89: regd: support loading regd table from fw element Regd table is a table that we use to describe how to map Realtek RF TX power settings on different countries. Originally, a common regd table for all chips is implemented in driver. However, in order to work on all chips, the common regd table might have some trade-off. So now, there are also an individual regd table for some chips. And, we support loading it from FW element. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250120032723.19042-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 14 ++++ drivers/net/wireless/realtek/rtw89/fw.c | 86 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 22 ++++++ drivers/net/wireless/realtek/rtw89/regd.c | 66 +++++++++++------ 4 files changed, 166 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 970730291e56..680e80b06f01 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -22,6 +22,7 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_debugfs; +struct rtw89_regd_data; extern const struct ieee80211_ops rtw89_ops; @@ -718,6 +719,7 @@ enum rtw89_ofdma_type { RTW89_OFDMA_NUM, }; +/* neither insert new in the middle, nor change any given definition */ enum rtw89_regulation_type { RTW89_WW = 0, RTW89_ETSI = 1, @@ -4539,6 +4541,7 @@ struct rtw89_fw_elm_info { struct rtw89_phy_table *rf_nctl; struct rtw89_fw_txpwr_track_cfg *txpwr_trk; struct rtw89_phy_rfk_log_fmt *rfk_log_fmt; + const struct rtw89_regd_data *regd; }; enum rtw89_fw_mss_dev_type { @@ -5153,11 +5156,22 @@ struct rtw89_regd { u8 txpwr_regd[RTW89_BAND_NUM]; }; +struct rtw89_regd_data { + unsigned int nr; + struct rtw89_regd map[] __counted_by(nr); +}; + +struct rtw89_regd_ctrl { + unsigned int nr; + const struct rtw89_regd *map; +}; + #define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX #define RTW89_5GHZ_UNII4_CHANNEL_NUM 3 #define RTW89_5GHZ_UNII4_START_INDEX 25 struct rtw89_regulatory_info { + struct rtw89_regd_ctrl ctrl; const struct rtw89_regd *regd; enum rtw89_reg_6ghz_power reg_6ghz_power; struct rtw89_reg_6ghz_tpe reg_6ghz_tpe; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5cc9ab78c09f..7b7994b8e1aa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1056,6 +1056,89 @@ allocated: return 0; } +static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor, + u8 cursor_size) +{ + /* fill default values if needed for backward compatibility */ + struct rtw89_fw_regd_entry entry = { + .rule_2ghz = RTW89_NA, + .rule_5ghz = RTW89_NA, + .rule_6ghz = RTW89_NA, + }; + u8 valid_size = min_t(u8, sizeof(entry), cursor_size); + + memcpy(&entry, cursor, valid_size); + memset(regd, 0, sizeof(*regd)); + + regd->alpha2[0] = entry.alpha2_0; + regd->alpha2[1] = entry.alpha2_1; + regd->alpha2[2] = '\0'; + + /* also need to consider forward compatibility */ + regd->txpwr_regd[RTW89_BAND_2G] = entry.rule_2ghz < RTW89_REGD_NUM ? + entry.rule_2ghz : RTW89_NA; + regd->txpwr_regd[RTW89_BAND_5G] = entry.rule_5ghz < RTW89_REGD_NUM ? + entry.rule_5ghz : RTW89_NA; + regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ? + entry.rule_6ghz : RTW89_NA; + + return true; +} + +#define rtw89_for_each_in_regd_element(regd, element) \ + for (const void *cursor = (element)->content, \ + *end = (element)->content + \ + le32_to_cpu((element)->num_ents) * (element)->ent_sz; \ + cursor < end; cursor += (element)->ent_sz) \ + if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz)) + +static +int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev, + const struct rtw89_fw_element_hdr *elm, + const union rtw89_fw_element_arg arg) +{ + const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd; + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + u32 num_ents = le32_to_cpu(regd_elm->num_ents); + struct rtw89_regd_data *p; + struct rtw89_regd regd; + u32 i = 0; + + if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) { + rtw89_warn(rtwdev, + "regd element ents (%d) are over max num (%d)\n", + num_ents, RTW89_REGD_MAX_COUNTRY_NUM); + rtw89_warn(rtwdev, + "regd element ignore and take another/common\n"); + return 1; + } + + if (elm_info->regd) { + rtw89_debug(rtwdev, RTW89_DBG_REGD, + "regd element take the latter\n"); + devm_kfree(rtwdev->dev, elm_info->regd); + elm_info->regd = NULL; + } + + p = devm_kzalloc(rtwdev->dev, struct_size(p, map, num_ents), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->nr = num_ents; + rtw89_for_each_in_regd_element(®d, regd_elm) + p->map[i++] = regd; + + if (i != num_ents) { + rtw89_err(rtwdev, "regd element has %d invalid ents\n", + num_ents - i); + devm_kfree(rtwdev->dev, p); + return -EINVAL; + } + + elm_info->regd = p; + return 0; +} + static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm, { .fw_type = RTW89_FW_BBMCU0 }, NULL}, @@ -1114,6 +1197,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_RFKLOG_FMT] = { rtw89_build_rfk_log_fmt_from_elm, {}, NULL, }, + [RTW89_FW_ELEMENT_ID_REGD] = { + rtw89_recognize_regd_from_elm, {}, "REGD", + }, }; int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 3cc514de073a..b98da24e9af3 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3882,6 +3882,7 @@ enum rtw89_fw_element_id { RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17, RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18, RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19, + RTW89_FW_ELEMENT_ID_REGD = 20, RTW89_FW_ELEMENT_ID_NUM, }; @@ -3925,6 +3926,15 @@ struct __rtw89_fw_txpwr_element { u8 content[]; } __packed; +struct __rtw89_fw_regd_element { + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; + u8 ent_sz; + __le32 num_ents; + u8 content[]; +} __packed; + enum rtw89_fw_txpwr_trk_type { __RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0, RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0, @@ -4016,6 +4026,7 @@ struct rtw89_fw_element_hdr { __le16 offset[]; } __packed rfk_log_fmt; struct __rtw89_fw_txpwr_element txpwr; + struct __rtw89_fw_regd_element regd; } __packed u; } __packed; @@ -4874,6 +4885,17 @@ int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, return 0; } +/* Must consider compatibility; don't insert new in the mid. + * Fill each field's default value in rtw89_regd_entcpy(). + */ +struct rtw89_fw_regd_entry { + u8 alpha2_0; + u8 alpha2_1; + u8 rule_2ghz; + u8 rule_5ghz; + u8 rule_6ghz; +} __packed; + /* must consider compatibility; don't insert new in the mid */ struct rtw89_fw_txpwr_byrate_entry { u8 band; diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 80b2f74589eb..aea37dae8ef9 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -7,9 +7,12 @@ #include "ps.h" #include "util.h" -#define COUNTRY_REGD(_alpha2, _txpwr_regd...) \ - {.alpha2 = (_alpha2), \ - .txpwr_regd = {_txpwr_regd}, \ +#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz) \ + { \ + .alpha2 = _alpha2, \ + .txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \ + .txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \ + .txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \ } static const struct rtw89_regd rtw89_ww_regd = @@ -295,13 +298,16 @@ static const char rtw89_alpha2_list_eu[][3] = { "RO", }; -static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2) +static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rtwdev, + const char *alpha2) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; u32 i; - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - if (!memcmp(rtw89_regd_map[i].alpha2, alpha2, 2)) - return &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + if (!memcmp(regd_ctrl->map[i].alpha2, alpha2, 2)) + return ®d_ctrl->map[i]; } return &rtw89_ww_regd; @@ -312,22 +318,25 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd) return regd == &rtw89_ww_regd; } -static u8 rtw89_regd_get_index(const struct rtw89_regd *regd) +static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; + BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM); if (rtw89_regd_is_ww(regd)) return RTW89_REGD_MAX_COUNTRY_NUM; - return regd - rtw89_regd_map; + return regd - regd_ctrl->map; } -static u8 rtw89_regd_get_index_by_name(const char *alpha2) +static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alpha2) { const struct rtw89_regd *regd; - regd = rtw89_regd_find_reg_by_name(alpha2); - return rtw89_regd_get_index(regd); + regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2); + return rtw89_regd_get_index(rtwdev, regd); } #define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \ @@ -345,6 +354,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -382,8 +392,8 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, "acpi: eval if allow unii-4: 0x%x\n", val); bottom: - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - const struct rtw89_regd *regd = &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + const struct rtw89_regd *regd = ®d_ctrl->map[i]; switch (regd->txpwr_regd[RTW89_BAND_5G]) { case RTW89_FCC: @@ -406,7 +416,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; u8 index; - index = rtw89_regd_get_index_by_name(alpha2); + index = rtw89_regd_get_index_by_name(rtwdev, alpha2); if (index == RTW89_REGD_MAX_COUNTRY_NUM) { rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n", __func__, alpha2[0], alpha2[1]); @@ -474,6 +484,7 @@ out: static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; const struct rtw89_acpi_policy_6ghz_sp *ptr; struct rtw89_acpi_dsm_result res = {}; bool enable_by_us; @@ -505,8 +516,8 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) enable_by_us = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US); - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - const struct rtw89_regd *tmp = &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + const struct rtw89_regd *tmp = ®d_ctrl->map[i]; if (enable_by_us && memcmp(tmp->alpha2, "US", 2) == 0) clear_bit(i, regulatory->block_6ghz_sp); @@ -573,8 +584,19 @@ bottom: int rtw89_regd_setup(struct rtw89_dev *rtwdev) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + const struct rtw89_regd_data *regd_data = elm_info->regd; struct wiphy *wiphy = rtwdev->hw->wiphy; + if (regd_data) { + regulatory->ctrl.nr = regd_data->nr; + regulatory->ctrl.map = regd_data->map; + } else { + regulatory->ctrl.nr = ARRAY_SIZE(rtw89_regd_map); + regulatory->ctrl.map = rtw89_regd_map; + } + if (!wiphy) return -EINVAL; @@ -599,7 +621,7 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev, if (!wiphy) return -EINVAL; - chip_regd = rtw89_regd_find_reg_by_name(rtwdev->efuse.country_code); + chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code); if (!rtw89_regd_is_ww(chip_regd)) { rtwdev->regulatory.regd = chip_regd; /* Ignore country ie if there is a country domain programmed in chip */ @@ -637,7 +659,7 @@ static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev, if (!chip->support_unii4) return; - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index != RTW89_REGD_MAX_COUNTRY_NUM && !test_bit(index, regulatory->block_unii4)) return; @@ -655,7 +677,7 @@ static bool regd_is_6ghz_blocked(struct rtw89_dev *rtwdev) const struct rtw89_regd *regd = regulatory->regd; u8 index; - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index != RTW89_REGD_MAX_COUNTRY_NUM && !test_bit(index, regulatory->block_6ghz)) return false; @@ -700,7 +722,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, struct wiphy *wiphy, struct regulatory_request *request) { - rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(request->alpha2); + rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2); /* This notification might be set from the system of distros, * and it does not expect the regulatory will be modified by * connecting to an AP (i.e. country ie). @@ -925,7 +947,7 @@ static bool __rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev) sel = RTW89_REG_6GHZ_POWER_DFLT; if (sel == RTW89_REG_6GHZ_POWER_STD) { - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index == RTW89_REGD_MAX_COUNTRY_NUM || test_bit(index, regulatory->block_6ghz_sp)) { rtw89_debug(rtwdev, RTW89_DBG_REGD, From ef36d0a25bf2ce03f7179e0a4a417f674503900a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 060/465] wifi: rtw89: regd: handle supported regulatory functions by country JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 79a36fc56beaeac31cb9b0ca10618b88f4ac31c8 Author: Zong-Zhe Yang Date: Mon Jan 20 11:27:22 2025 +0800 wifi: rtw89: regd: handle supported regulatory functions by country There are two regulatory functions including TAS (Time Average SAR) and DAG (Dynamic Antenna Gain) for now. They are used to improve RF behavior. But, availability depends on country. Extend regd map to record the status of whether a country allows to enable certain regulatory functions. Also, extend the handling of loading regd map via FW element for this. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250120032723.19042-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 8 + drivers/net/wireless/realtek/rtw89/fw.c | 12 + drivers/net/wireless/realtek/rtw89/fw.h | 1 + drivers/net/wireless/realtek/rtw89/regd.c | 489 +++++++++++----------- 4 files changed, 266 insertions(+), 244 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 680e80b06f01..4328b3acca45 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5151,9 +5151,17 @@ struct rtw89_power_trim_info { u8 pad_bias_trim[RF_PATH_MAX]; }; +enum rtw89_regd_func { + RTW89_REGD_FUNC_TAS = 0, /* TAS (Time Average SAR) */ + RTW89_REGD_FUNC_DAG = 1, /* DAG (Dynamic Antenna Gain) */ + + NUM_OF_RTW89_REGD_FUNC, +}; + struct rtw89_regd { char alpha2[3]; u8 txpwr_regd[RTW89_BAND_NUM]; + DECLARE_BITMAP(func_bitmap, NUM_OF_RTW89_REGD_FUNC); }; struct rtw89_regd_data { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 7b7994b8e1aa..cb5ccd9df364 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1064,8 +1064,11 @@ static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor, .rule_2ghz = RTW89_NA, .rule_5ghz = RTW89_NA, .rule_6ghz = RTW89_NA, + .fmap = cpu_to_le32(0x0), }; u8 valid_size = min_t(u8, sizeof(entry), cursor_size); + unsigned int i; + u32 fmap; memcpy(&entry, cursor, valid_size); memset(regd, 0, sizeof(*regd)); @@ -1082,6 +1085,15 @@ static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor, regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ? entry.rule_6ghz : RTW89_NA; + BUILD_BUG_ON(sizeof(fmap) != sizeof(entry.fmap)); + BUILD_BUG_ON(sizeof(fmap) * 8 < NUM_OF_RTW89_REGD_FUNC); + + fmap = le32_to_cpu(entry.fmap); + for (i = 0; i < NUM_OF_RTW89_REGD_FUNC; i++) { + if (fmap & BIT(i)) + set_bit(i, regd->func_bitmap); + } + return true; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index b98da24e9af3..c8faa2f29f5e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4894,6 +4894,7 @@ struct rtw89_fw_regd_entry { u8 rule_2ghz; u8 rule_5ghz; u8 rule_6ghz; + __le32 fmap; } __packed; /* must consider compatibility; don't insert new in the mid */ diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index aea37dae8ef9..50a7f6ff4b5b 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -7,260 +7,261 @@ #include "ps.h" #include "util.h" -#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz) \ +#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz, _fmap) \ { \ .alpha2 = _alpha2, \ .txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \ .txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \ .txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \ + .func_bitmap = { BITMAP_FROM_U64(_fmap), }, \ } static const struct rtw89_regd rtw89_ww_regd = - COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW); + COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0); static const struct rtw89_regd rtw89_regd_map[] = { - COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), - COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE), - COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), - COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK), - COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR), - COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE), - COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN), - COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC), - COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI), - COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND), - COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), - COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), - COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC), - COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK), - COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA), - COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA), - COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), + COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), + COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE, 0x0), + COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), + COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x1), + COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK, 0x0), + COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR, 0x0), + COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE, 0x0), + COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN, 0x0), + COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC, 0x1), + COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI, 0x0), + COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND, 0x0), + COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), + COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), + COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC, 0x1), + COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK, 0x0), + COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), + COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), + COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), }; static const char rtw89_alpha2_list_eu[][3] = { From 075fada1c47de68d191365e83e1664d0a8c68be0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 061/465] wifi: rtw89: regd: refactor init/setup flow and prototype JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b45acf245596d01cc9e2965695d2d1c428001f62 Author: Zong-Zhe Yang Date: Mon Jan 20 11:27:23 2025 +0800 wifi: rtw89: regd: refactor init/setup flow and prototype The regulatory and wiphy setup should be done in rtw89_regd_setup(). And, what rtw89_regd_init() should do is to initialize target regulatory domain (regd), if and only if one is programmed in efuse. Since HW is registered after rtw89_regd_setup() and before rtw89_regd_init(), do not fill fields of regulatory in rtw89_regd_init(). So, move the regulatory->reg_6ghz_power assignment into rtw89_regd_setup(). Besides, rtw89_regd_notifier is assigned in rtw89_regd_setup() instead of rtw89_regd_init(). To reduce confusion, stop passing rtw89_regd_notifier to rtw89_regd_init(). And, rename rtw89_regd_init() to rtw89_regd_init_hint(). Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250120032723.19042-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/core.h | 4 +--- drivers/net/wireless/realtek/rtw89/regd.c | 13 +++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b730f70c82aa..37002cfe8325 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5303,7 +5303,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) goto err_free_supported_band; } - ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier); + ret = rtw89_regd_init_hint(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to init regd\n"); goto err_unregister_hw; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4328b3acca45..505a645e7a88 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -7163,9 +7163,7 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate); int rtw89_regd_setup(struct rtw89_dev *rtwdev); -int rtw89_regd_init(struct rtw89_dev *rtwdev, - void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); -void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 50a7f6ff4b5b..5b9d3b78c24e 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -7,6 +7,9 @@ #include "ps.h" #include "util.h" +static +void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); + #define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz, _fmap) \ { \ .alpha2 = _alpha2, \ @@ -598,6 +601,8 @@ int rtw89_regd_setup(struct rtw89_dev *rtwdev) regulatory->ctrl.map = rtw89_regd_map; } + regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; + if (!wiphy) return -EINVAL; @@ -608,17 +613,12 @@ int rtw89_regd_setup(struct rtw89_dev *rtwdev) return 0; } -int rtw89_regd_init(struct rtw89_dev *rtwdev, - void (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request)) +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev) { - struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; const struct rtw89_regd *chip_regd; struct wiphy *wiphy = rtwdev->hw->wiphy; int ret; - regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; - if (!wiphy) return -EINVAL; @@ -738,6 +738,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); } +static void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); From c893bee782b2cc0cdc3efe8c071de94d67f4453e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:28 +0200 Subject: [PATCH 062/465] wifi: rtw89: cleanup unused rtwdev::roc_work JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c281bdb8821461047d3e74206afbff190d1f7846 Author: Zong-Zhe Yang Date: Mon Jan 20 11:40:04 2025 +0800 wifi: rtw89: cleanup unused rtwdev::roc_work The needed one was moved to rtwvif::roc_work. And, rtwdev::roc_work is unused. So, remove it. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250120034004.21135-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 505a645e7a88..9a44475a8be6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5729,7 +5729,6 @@ struct rtw89_dev { struct delayed_work coex_rfk_chk_work; struct delayed_work cfo_track_work; struct delayed_work forbid_ba_work; - struct delayed_work roc_work; struct delayed_work antdiv_work; struct rtw89_ppdu_sts_info ppdu_sts; u8 total_sta_assoc; From ff831ef58948469b2a3dea82b232b5b3dee961ac Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 063/465] wifi: rtw89: add wiphy_lock() to work that isn't held wiphy_lock() yet JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ebfc9199df05d37b67f4d1b7ee997193f3d2e7c8 Author: Ping-Ke Shih Date: Wed Jan 22 14:03:01 2025 +0800 wifi: rtw89: add wiphy_lock() to work that isn't held wiphy_lock() yet To ensure where are protected by driver mutex can also be protected by wiphy_lock(), so afterward we can remove driver mutex safely. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/regd.c | 2 ++ drivers/net/wireless/realtek/rtw89/ser.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 5b9d3b78c24e..46f5c7eb1683 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -744,6 +744,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rtw89_dev *rtwdev = hw->priv; + wiphy_lock(wiphy); mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); @@ -761,6 +762,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request exit: mutex_unlock(&rtwdev->mutex); + wiphy_unlock(wiphy); } /* Maximum Transmit Power field (@raw) can be EIRP or PSD. diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 26a944d3b672..d0c8584308c0 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -156,9 +156,11 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt) rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n", ser_st_name(ser), ser_ev_name(ser, evt)); + wiphy_lock(rtwdev->hw->wiphy); mutex_lock(&rtwdev->mutex); rtw89_leave_lps(rtwdev); mutex_unlock(&rtwdev->mutex); + wiphy_unlock(rtwdev->hw->wiphy); ser->st_tbl[ser->state].st_func(ser, evt); } @@ -708,9 +710,11 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: + wiphy_lock(rtwdev->hw->wiphy); mutex_lock(&rtwdev->mutex); ser_l2_reset_st_pre_hdl(ser); mutex_unlock(&rtwdev->mutex); + wiphy_unlock(rtwdev->hw->wiphy); ieee80211_restart_hw(rtwdev->hw); ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT); From 8a5621e50f3e74923ffc7849e64cabb0afa61246 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 064/465] wifi: rtw89: use wiphy_work() to replace ieee802111_work() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4afde17d266795eff3ebb94526c7b04ea876259d Author: Ping-Ke Shih Date: Wed Jan 22 14:03:02 2025 +0800 wifi: rtw89: use wiphy_work() to replace ieee802111_work() For certain works protected by driver mutex, use wiphy_work() directly to have wiphy lock held naturally. Then every this kind of works is protected by both wiphy lock and driver mutex. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/chan.c | 6 +- drivers/net/wireless/realtek/rtw89/chan.h | 2 +- drivers/net/wireless/realtek/rtw89/coex.c | 38 ++++---- drivers/net/wireless/realtek/rtw89/coex.h | 14 +-- drivers/net/wireless/realtek/rtw89/core.c | 96 ++++++++++--------- drivers/net/wireless/realtek/rtw89/core.h | 36 +++---- drivers/net/wireless/realtek/rtw89/fw.c | 4 +- drivers/net/wireless/realtek/rtw89/fw.h | 2 +- drivers/net/wireless/realtek/rtw89/mac80211.c | 21 ++-- drivers/net/wireless/realtek/rtw89/phy.c | 18 ++-- drivers/net/wireless/realtek/rtw89/phy.h | 4 +- drivers/net/wireless/realtek/rtw89/ser.c | 9 +- 12 files changed, 128 insertions(+), 122 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 4df4e04c3e67..9858894dfc23 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -2390,7 +2390,7 @@ static void rtw89_mcc_update_limit(struct rtw89_dev *rtwdev) rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_lmt_iterator, NULL); } -void rtw89_chanctx_work(struct work_struct *work) +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, chanctx_work.work); @@ -2477,8 +2477,8 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_CHAN, "queue chanctx work for mode %d with delay %d us\n", mode, delay); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->chanctx_work, - usecs_to_jiffies(delay)); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work, + usecs_to_jiffies(delay)); } void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index 092a6f676894..e6391f6f2aa7 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -99,7 +99,7 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, const struct cfg80211_chan_def *chandef); void rtw89_entity_init(struct rtw89_dev *rtwdev); enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); -void rtw89_chanctx_work(struct work_struct *work); +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev); void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, enum rtw89_chanctx_changes change); diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 755c157c815d..c51c06909966 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -6712,7 +6712,7 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); } -void rtw89_coex_act1_work(struct work_struct *work) +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_act1_work.work); @@ -6733,7 +6733,7 @@ void rtw89_coex_act1_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } -void rtw89_coex_bt_devinfo_work(struct work_struct *work) +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_bt_devinfo_work.work); @@ -6749,7 +6749,7 @@ void rtw89_coex_bt_devinfo_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } -void rtw89_coex_rfk_chk_work(struct work_struct *work) +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_rfk_chk_work.work); @@ -7278,7 +7278,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, "[BTC], %s(): EAPOL_End cnt=%d\n", __func__, cnt); wl->status.map._4way = false; - cancel_delayed_work(&rtwdev->coex_act1_work); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); break; case PACKET_ARP: cnt = ++cx->cnt_wl[BTC_WCNT_ARP]; @@ -7297,16 +7297,16 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, } if (delay_work) { - cancel_delayed_work(&rtwdev->coex_act1_work); - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_act1_work, delay); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work, delay); } btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++; _run_coex(rtwdev, BTC_RSN_NTFY_SPECIFIC_PACKET); } -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.eapol_notify_work); @@ -7317,7 +7317,7 @@ void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.arp_notify_work); @@ -7327,7 +7327,7 @@ void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.dhcp_notify_work); @@ -7338,7 +7338,7 @@ void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.icmp_notify_work); @@ -7525,9 +7525,9 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) a2dp->vendor_id = 0; a2dp->flush_time = 0; a2dp->play_latency = 1; - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_bt_devinfo_work, - RTW89_COEX_BT_DEVINFO_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_bt_devinfo_work, + RTW89_COEX_BT_DEVINFO_WORK_PERIOD); } _run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO); @@ -7776,7 +7776,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, wl->rfk_info.state = BTC_WRFK_STOP; _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); - cancel_delayed_work(&rtwdev->coex_rfk_chk_work); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_rfk_chk_work); break; default: rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7790,9 +7790,9 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, _run_coex(rtwdev, BTC_RSN_NTFY_WL_RFK); if (wl->rfk_info.state == BTC_WRFK_START) - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_rfk_chk_work, - RTW89_COEX_RFK_CHK_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_rfk_chk_work, + RTW89_COEX_RFK_CHK_WORK_PERIOD); } rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7810,6 +7810,8 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, bool allow; int ret; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + band = FIELD_GET(BTC_RFK_BAND_MAP, phy_map); rtw89_debug(rtwdev, RTW89_DBG_RFK, diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 757d03675cf4..30134f58d531 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -267,10 +267,10 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx); void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band); void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, enum btc_pkt_type pkt_type); -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work); +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -283,9 +283,9 @@ void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev); void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m); -void rtw89_coex_act1_work(struct work_struct *work); -void rtw89_coex_bt_devinfo_work(struct work_struct *work); -void rtw89_coex_rfk_chk_work(struct work_struct *work); +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_coex_power_on(struct rtw89_dev *rtwdev); void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type); void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 37002cfe8325..330b23db4868 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -913,16 +913,17 @@ static enum btc_pkt_type rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { + struct wiphy *wiphy = rtwdev->hw->wiphy; struct sk_buff *skb = tx_req->skb; struct udphdr *udphdr; if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.eapol_notify_work); return PACKET_EAPOL; } if (skb->protocol == htons(ETH_P_ARP)) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.arp_notify_work); return PACKET_ARP; } @@ -932,14 +933,14 @@ rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev, if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) || (udphdr->source == htons(68) && udphdr->dest == htons(67))) && skb->len > 282) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.dhcp_notify_work); return PACKET_DHCP; } } if (skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->protocol == IPPROTO_ICMP) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.icmp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.icmp_notify_work); return PACKET_ICMP; } @@ -2071,7 +2072,7 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev, } } -static void rtw89_cancel_6ghz_probe_work(struct work_struct *work) +static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, cancel_6ghz_probe_work); @@ -2131,7 +2132,7 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, } if (queue_work) - ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->cancel_6ghz_probe_work); } static void rtw89_vif_sync_bcn_tsf(struct rtw89_vif_link *rtwvif_link, @@ -3143,7 +3144,7 @@ static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinv ieee80211_txq_schedule_end(hw, ac); } -static void rtw89_ips_work(struct work_struct *work) +static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ips_work); @@ -3330,9 +3331,9 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH); ieee80211_ready_on_channel(hw); - cancel_delayed_work(&rtwvif->roc.roc_work); - ieee80211_queue_delayed_work(hw, &rtwvif->roc.roc_work, - msecs_to_jiffies(rtwvif->roc.duration)); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); + wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, + msecs_to_jiffies(rtwvif->roc.duration)); } void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) @@ -3377,11 +3378,11 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) queue_work(rtwdev->txq_wq, &rtwdev->txq_work); if (hw->conf.flags & IEEE80211_CONF_IDLE) - ieee80211_queue_delayed_work(hw, &roc->roc_work, - msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT)); + wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, + msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT)); } -void rtw89_roc_work(struct work_struct *work) +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif, roc.roc_work.work); @@ -3533,7 +3534,7 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, ewma_tp_init(&stats->rx_ewma_tp); } -static void rtw89_track_work(struct work_struct *work) +static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, track_work.work); @@ -3547,8 +3548,8 @@ static void rtw89_track_work(struct work_struct *work) if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) goto out; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); tfc_changed = rtw89_traffic_stats_track(rtwdev); if (rtwdev->scanning) @@ -4438,7 +4439,7 @@ static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev) rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX; } -void rtw89_core_update_beacon_work(struct work_struct *work) +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev; struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link, @@ -4568,8 +4569,8 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) return ret; } - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); set_bit(RTW89_FLAG_RUNNING, rtwdev->flags); @@ -4583,6 +4584,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) void rtw89_core_stop(struct rtw89_dev *rtwdev) { + struct wiphy *wiphy = rtwdev->hw->wiphy; struct rtw89_btc *btc = &rtwdev->btc; /* Prvent to stop twice; enter_ips and ops_stop */ @@ -4595,21 +4597,21 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) mutex_unlock(&rtwdev->mutex); - cancel_work_sync(&rtwdev->c2h_work); - cancel_work_sync(&rtwdev->cancel_6ghz_probe_work); - cancel_work_sync(&btc->eapol_notify_work); - cancel_work_sync(&btc->arp_notify_work); - cancel_work_sync(&btc->dhcp_notify_work); - cancel_work_sync(&btc->icmp_notify_work); + wiphy_work_cancel(wiphy, &rtwdev->c2h_work); + wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work); + wiphy_work_cancel(wiphy, &btc->eapol_notify_work); + wiphy_work_cancel(wiphy, &btc->arp_notify_work); + wiphy_work_cancel(wiphy, &btc->dhcp_notify_work); + wiphy_work_cancel(wiphy, &btc->icmp_notify_work); cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work); - cancel_delayed_work_sync(&rtwdev->track_work); - cancel_delayed_work_sync(&rtwdev->chanctx_work); - cancel_delayed_work_sync(&rtwdev->coex_act1_work); - cancel_delayed_work_sync(&rtwdev->coex_bt_devinfo_work); - cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work); - cancel_delayed_work_sync(&rtwdev->cfo_track_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_rfk_chk_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->cfo_track_work); cancel_delayed_work_sync(&rtwdev->forbid_ba_work); - cancel_delayed_work_sync(&rtwdev->antdiv_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work); mutex_lock(&rtwdev->mutex); @@ -4825,14 +4827,14 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work); INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work); INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); - INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work); - INIT_DELAYED_WORK(&rtwdev->chanctx_work, rtw89_chanctx_work); - INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work); - INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); - INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); - INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); + wiphy_delayed_work_init(&rtwdev->track_work, rtw89_track_work); + wiphy_delayed_work_init(&rtwdev->chanctx_work, rtw89_chanctx_work); + wiphy_delayed_work_init(&rtwdev->coex_act1_work, rtw89_coex_act1_work); + wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); + wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); + wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); - INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); + wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); if (!rtwdev->txq_wq) return -ENOMEM; @@ -4847,10 +4849,10 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtw89_init_wait(&rtwdev->wow.wait); rtw89_init_wait(&rtwdev->mac.ps_wait); - INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); - INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); + wiphy_work_init(&rtwdev->c2h_work, rtw89_fw_c2h_work); + wiphy_work_init(&rtwdev->ips_work, rtw89_ips_work); + wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); - INIT_WORK(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); @@ -4870,10 +4872,10 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtwdev->bbs[RTW89_PHY_0].phy_idx = RTW89_PHY_0; rtwdev->bbs[RTW89_PHY_1].phy_idx = RTW89_PHY_1; - INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); - INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); - INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); - INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); + wiphy_work_init(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); + wiphy_work_init(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); + wiphy_work_init(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); + wiphy_work_init(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); init_completion(&rtwdev->fw.req.completion); init_completion(&rtwdev->rfk_wait.completion); @@ -4949,7 +4951,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rtw89_for_each_active_bb(rtwdev, bb) bb->dig.bypass_dig = true; if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) - ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->ips_work); } static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 9a44475a8be6..6970e4d4e73f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3177,10 +3177,10 @@ struct rtw89_btc { struct rtw89_btc_btf_fwinfo fwinfo; struct rtw89_btc_dbg dbg; - struct work_struct eapol_notify_work; - struct work_struct arp_notify_work; - struct work_struct dhcp_notify_work; - struct work_struct icmp_notify_work; + struct wiphy_work eapol_notify_work; + struct wiphy_work arp_notify_work; + struct wiphy_work dhcp_notify_work; + struct wiphy_work icmp_notify_work; u32 bt_req_len; @@ -3447,7 +3447,7 @@ enum rtw89_roc_state { struct rtw89_roc { struct ieee80211_channel chan; - struct delayed_work roc_work; + struct wiphy_delayed_work roc_work; enum ieee80211_roc_type type; enum rtw89_roc_state state; int duration; @@ -3517,7 +3517,7 @@ struct rtw89_vif_link { bool pre_pwr_diff_en; bool pwr_diff_en; u8 def_tri_idx; - struct work_struct update_beacon_work; + struct wiphy_work update_beacon_work; struct rtw89_addr_cam_entry addr_cam; struct rtw89_bssid_cam_entry bssid_cam; struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS]; @@ -5674,10 +5674,10 @@ struct rtw89_dev { struct rtw89_cam_info cam_info; struct sk_buff_head c2h_queue; - struct work_struct c2h_work; - struct work_struct ips_work; + struct wiphy_work c2h_work; + struct wiphy_work ips_work; + struct wiphy_work cancel_6ghz_probe_work; struct work_struct load_firmware_work; - struct work_struct cancel_6ghz_probe_work; struct list_head early_h2c_list; @@ -5722,14 +5722,14 @@ struct rtw89_dev { struct rtw89_edcca_bak edcca_bak; } bbs[RTW89_PHY_NUM]; - struct delayed_work track_work; - struct delayed_work chanctx_work; - struct delayed_work coex_act1_work; - struct delayed_work coex_bt_devinfo_work; - struct delayed_work coex_rfk_chk_work; - struct delayed_work cfo_track_work; + struct wiphy_delayed_work track_work; + struct wiphy_delayed_work chanctx_work; + struct wiphy_delayed_work coex_act1_work; + struct wiphy_delayed_work coex_bt_devinfo_work; + struct wiphy_delayed_work coex_rfk_chk_work; + struct wiphy_delayed_work cfo_track_work; struct delayed_work forbid_ba_work; - struct delayed_work antdiv_work; + struct wiphy_delayed_work antdiv_work; struct rtw89_ppdu_sts_info ppdu_sts; u8 total_sta_assoc; bool scanning; @@ -7170,8 +7170,8 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, const struct rtw89_completion_data *data); int rtw89_core_start(struct rtw89_dev *rtwdev); void rtw89_core_stop(struct rtw89_dev *rtwdev); -void rtw89_core_update_beacon_work(struct work_struct *work); -void rtw89_roc_work(struct work_struct *work); +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index cb5ccd9df364..64c2895309c6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6157,7 +6157,7 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) enqueue: skb_queue_tail(&rtwdev->c2h_queue, c2h); - ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->c2h_work); } static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, @@ -6195,7 +6195,7 @@ static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "C2H: ", skb->data, skb->len); } -void rtw89_fw_c2h_work(struct work_struct *work) +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, c2h_work); diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index c8faa2f29f5e..b621dbed2c61 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4599,7 +4599,7 @@ int rtw89_fw_h2c_dctl_sec_cam_v2(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h); -void rtw89_fw_c2h_work(struct work_struct *work); +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work); int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index b3669e0074df..7b3520e6389b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -80,7 +80,7 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) struct rtw89_dev *rtwdev = hw->priv; /* let previous ips work finish to ensure we don't leave ips twice */ - cancel_work_sync(&rtwdev->ips_work); + wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work); mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); @@ -115,7 +115,7 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev, rtw89_vif_type_mapping(rtwvif_link, false); - INIT_WORK(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); + wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); INIT_LIST_HEAD(&rtwvif_link->general_pkt_list); rtwvif_link->hit_rule = 0; @@ -143,7 +143,7 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { mutex_unlock(&rtwdev->mutex); - cancel_work_sync(&rtwvif_link->update_beacon_work); + wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); @@ -198,7 +198,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, rtwvif->offchan = false; rtwvif->roc.state = RTW89_ROC_IDLE; - INIT_DELAYED_WORK(&rtwvif->roc.roc_work, rtw89_roc_work); + wiphy_delayed_work_init(&rtwvif->roc.roc_work, rtw89_roc_work); rtw89_traffic_stats_init(rtwdev, &rtwvif->stats); @@ -242,7 +242,7 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n", vif->addr, vif->type, vif->p2p); - cancel_delayed_work_sync(&rtwvif->roc.roc_work); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); mutex_lock(&rtwdev->mutex); @@ -853,14 +853,13 @@ out: static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { - struct rtw89_dev *rtwdev = hw->priv; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_vif *rtwvif = rtwsta->rtwvif; struct rtw89_vif_link *rtwvif_link; unsigned int link_id; rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) - ieee80211_queue_work(rtwdev->hw, &rtwvif_link->update_beacon_work); + wiphy_work_queue(hw->wiphy, &rtwvif_link->update_beacon_work); return 0; } @@ -1453,7 +1452,7 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw, if (!rtwvif) return -EINVAL; - cancel_delayed_work_sync(&rtwvif->roc.roc_work); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); mutex_lock(&rtwdev->mutex); rtw89_roc_end(rtwdev, rtwvif); @@ -1751,7 +1750,7 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw, int ret; set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); - cancel_delayed_work_sync(&rtwdev->track_work); + wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work); mutex_lock(&rtwdev->mutex); ret = rtw89_wow_suspend(rtwdev, wowlan); @@ -1778,8 +1777,8 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw) mutex_unlock(&rtwdev->mutex); clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); return ret ? 1 : 0; } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 5b0cf8314fb2..2f8694fe4bd1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4651,7 +4651,7 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev) rtw89_phy_cfo_statistics_reset(rtwdev); } -void rtw89_phy_cfo_track_work(struct work_struct *work) +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, cfo_track_work.work); @@ -4662,8 +4662,8 @@ void rtw89_phy_cfo_track_work(struct work_struct *work) goto out; rtw89_leave_ps_mode(rtwdev); rtw89_phy_cfo_dm(rtwdev); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, - msecs_to_jiffies(cfo->cfo_timer_ms)); + wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work, + msecs_to_jiffies(cfo->cfo_timer_ms)); out: mutex_unlock(&rtwdev->mutex); } @@ -4672,8 +4672,8 @@ static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev) { struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, - msecs_to_jiffies(cfo->cfo_timer_ms)); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->cfo_track_work, + msecs_to_jiffies(cfo->cfo_timer_ms)); } void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev) @@ -6523,11 +6523,11 @@ static void rtw89_phy_antdiv_training_state(struct rtw89_dev *rtwdev) } antdiv->training_count++; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, - state_period); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, + state_period); } -void rtw89_phy_antdiv_work(struct work_struct *work) +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, antdiv_work.work); @@ -6563,7 +6563,7 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) return; antdiv->training_count = 0; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, 0); } static void __rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 33d466c519e3..cec165e222e7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -978,7 +978,7 @@ void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, struct rtw89_h2c_rf_tssi *h2c); void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev); -void rtw89_phy_cfo_track_work(struct work_struct *work); +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, struct rtw89_rx_phy_ppdu *phy_ppdu); void rtw89_phy_stat_track(struct rtw89_dev *rtwdev); @@ -991,7 +991,7 @@ void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu); void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); -void rtw89_phy_antdiv_work(struct work_struct *work); +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index d0c8584308c0..9658672ad274 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -485,10 +485,13 @@ static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt) static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) { struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); + struct wiphy *wiphy = rtwdev->hw->wiphy; switch (evt) { case SER_EV_STATE_IN: - cancel_delayed_work_sync(&rtwdev->track_work); + wiphy_lock(wiphy); + wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); + wiphy_unlock(wiphy); drv_stop_tx(ser); if (hal_stop_dma(ser)) { @@ -519,8 +522,8 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) hal_enable_dma(ser); drv_resume_rx(ser); drv_resume_tx(ser); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); break; default: From 4589cf01468422c583e68d061e0700807c019168 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 065/465] wifi: rtw89: debugfs: implement file_ops::read/write to replace seq_file JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 831cceed3baf498984add821d288ced95a4ec4cf Author: Ping-Ke Shih Date: Wed Jan 22 14:03:03 2025 +0800 wifi: rtw89: debugfs: implement file_ops::read/write to replace seq_file Since debugfs needs wiphy lock held, wiphy_locked_debugfs_{read,write}() will be adopted, so implmenet file_ops::read/write along with their arguments. For reading part, it needs lots of changes because seq_file is not suitable for wiphy_locked_debugfs_{read,write}(), so use spatch script below to convert basically, and manually implement the functions. @ rule1 @ identifier m; @@ - seq_printf(m, + p += scnprintf(p, end - p, ...) @ rule2 @ identifier m; @@ - seq_puts(m, + p += scnprintf(p, end - p, ...) For current version, only 4K buffer to output. To note ourselves, add ellipsis symbol "..." to trailing if buffer is full. Later, add an option to specify buffer size needed by a debugfs entry. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 2621 +++++++++++--------- drivers/net/wireless/realtek/rtw89/coex.h | 2 +- drivers/net/wireless/realtek/rtw89/debug.c | 1915 +++++++------- drivers/net/wireless/realtek/rtw89/phy.c | 16 +- drivers/net/wireless/realtek/rtw89/phy.h | 4 +- drivers/net/wireless/realtek/rtw89/sar.c | 40 +- drivers/net/wireless/realtek/rtw89/sar.h | 5 +- drivers/net/wireless/realtek/rtw89/util.c | 11 + drivers/net/wireless/realtek/rtw89/util.h | 1 + 9 files changed, 2525 insertions(+), 2090 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index c51c06909966..1065f610d80f 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -8153,7 +8153,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, #define BTC_CX_FW_OFFLOAD 0 -static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; const struct rtw89_chip_info *chip = rtwdev->chip; @@ -8165,40 +8165,43 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &btc->cx.wl; u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0; u8 cv, rfe, iso, ant_num, ant_single_pos; + char *p = buf, *end = buf + bufsz; if (!(dm->coex_info_map & BTC_COEX_INFO_CX)) - return; + return 0; dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++; - seq_printf(m, "========== [BTC COEX INFO (%d)] ==========\n", - chip->chip_id); + p += scnprintf(p, end - p, + "========== [BTC COEX INFO (%d)] ==========\n", + chip->chip_id); ver_main = FIELD_GET(GENMASK(31, 24), RTW89_COEX_VERSION); ver_sub = FIELD_GET(GENMASK(23, 16), RTW89_COEX_VERSION); ver_hotfix = FIELD_GET(GENMASK(15, 8), RTW89_COEX_VERSION); id_branch = FIELD_GET(GENMASK(7, 0), RTW89_COEX_VERSION); - seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ", - "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch); + p += scnprintf(p, end - p, " %-15s : Coex:%d.%d.%d(branch:%d), ", + "[coex_version]", ver_main, ver_sub, ver_hotfix, + id_branch); ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex); ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex); id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw_coex); - seq_printf(m, "WL_FW_coex:%d.%d.%d(branch:%d)", - ver_main, ver_sub, ver_hotfix, id_branch); + p += scnprintf(p, end - p, "WL_FW_coex:%d.%d.%d(branch:%d)", + ver_main, ver_sub, ver_hotfix, id_branch); ver_main = FIELD_GET(GENMASK(31, 24), chip->wlcx_desired); ver_sub = FIELD_GET(GENMASK(23, 16), chip->wlcx_desired); ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired); - seq_printf(m, "(%s, desired:%d.%d.%d), ", - (wl->ver_info.fw_coex >= chip->wlcx_desired ? - "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); + p += scnprintf(p, end - p, "(%s, desired:%d.%d.%d), ", + (wl->ver_info.fw_coex >= chip->wlcx_desired ? + "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); - seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n", - bt->ver_info.fw_coex, - (bt->ver_info.fw_coex >= chip->btcx_desired ? - "Match" : "Mismatch"), chip->btcx_desired); + p += scnprintf(p, end - p, "BT_FW_coex:%d(%s, desired:%d)\n", + bt->ver_info.fw_coex, + (bt->ver_info.fw_coex >= chip->btcx_desired ? + "Match" : "Mismatch"), chip->btcx_desired); if (bt->enable.now && bt->ver_info.fw == 0) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); @@ -8209,10 +8212,11 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw); id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw); - seq_printf(m, " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", - "[sub_module]", - ver_main, ver_sub, ver_hotfix, id_branch, - bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); + p += scnprintf(p, end - p, + " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", + "[sub_module]", + ver_main, ver_sub, ver_hotfix, id_branch, + bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); if (ver->fcxinit == 7) { cv = md->md_v7.kt_ver; @@ -8228,36 +8232,41 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) ant_single_pos = md->md.ant.single_pos; } - seq_printf(m, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", - "[hw_info]", cv, rfe, iso, ant_num, - ant_num > 1 ? "" : - ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); + p += scnprintf(p, end - p, + " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", + "[hw_info]", cv, rfe, iso, ant_num, + ant_num > 1 ? "" : + ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); - seq_printf(m, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", - btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, - hal->rx_nss); + p += scnprintf(p, end - p, + "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", + btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, + hal->rx_nss); + + return p - buf; } -static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_link_info *plink = NULL; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_traffic_stats *t; + char *p = buf, *end = buf + bufsz; u8 i; if (rtwdev->dbcc_en) { - seq_printf(m, - " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ", - "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0], - wl_dinfo->scan_band[RTW89_PHY_0], - wl_dinfo->real_band[RTW89_PHY_0]); - seq_printf(m, - "PHY1_band(op:%d/scan:%d/real:%d)\n", - wl_dinfo->op_band[RTW89_PHY_1], - wl_dinfo->scan_band[RTW89_PHY_1], - wl_dinfo->real_band[RTW89_PHY_1]); + p += scnprintf(p, end - p, + " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ", + "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0], + wl_dinfo->scan_band[RTW89_PHY_0], + wl_dinfo->real_band[RTW89_PHY_0]); + p += scnprintf(p, end - p, + "PHY1_band(op:%d/scan:%d/real:%d)\n", + wl_dinfo->op_band[RTW89_PHY_1], + wl_dinfo->scan_band[RTW89_PHY_1], + wl_dinfo->real_band[RTW89_PHY_1]); } for (i = 0; i < RTW89_PORT_NUM; i++) { @@ -8269,38 +8278,41 @@ static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) if (!plink->active) continue; - seq_printf(m, - " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", - plink->pid, (u32)plink->role, plink->phy, - (u32)plink->connected, plink->client_cnt - 1, - (u32)plink->mode, plink->ch, (u32)plink->bw); + p += scnprintf(p, end - p, + " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", + plink->pid, (u32)plink->role, plink->phy, + (u32)plink->connected, plink->client_cnt - 1, + (u32)plink->mode, plink->ch, (u32)plink->bw); if (plink->connected == MLME_NO_LINK) continue; - seq_printf(m, - ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", - plink->mac_id, plink->tx_time, plink->tx_retry); + p += scnprintf(p, end - p, + ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", + plink->mac_id, plink->tx_time, plink->tx_retry); - seq_printf(m, - " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ", - plink->pid, 110 - plink->stat.rssi, - plink->stat.rssi, plink->busy, - plink->dir == RTW89_TFC_UL ? "UL" : "DL"); + p += scnprintf(p, end - p, + " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ", + plink->pid, 110 - plink->stat.rssi, + plink->stat.rssi, plink->busy, + plink->dir == RTW89_TFC_UL ? "UL" : "DL"); t = &plink->stat.traffic; - seq_printf(m, - "tx[rate:%d/busy_level:%d], ", - (u32)t->tx_rate, t->tx_tfc_lv); + p += scnprintf(p, end - p, + "tx[rate:%d/busy_level:%d], ", + (u32)t->tx_rate, t->tx_tfc_lv); - seq_printf(m, "rx[rate:%d/busy_level:%d/drop:%d]\n", - (u32)t->rx_rate, - t->rx_tfc_lv, plink->rx_rate_drop_cnt); + p += scnprintf(p, end - p, + "rx[rate:%d/busy_level:%d/drop:%d]\n", + (u32)t->rx_rate, + t->rx_tfc_lv, plink->rx_rate_drop_cnt); } + + return p - buf; } -static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; @@ -8311,12 +8323,13 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; + char *p = buf, *end = buf + bufsz; u8 mode; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL)) - return; + return 0; - seq_puts(m, "========== [WL Status] ==========\n"); + p += scnprintf(p, end - p, "========== [WL Status] ==========\n"); if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; @@ -8329,24 +8342,28 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) else if (ver->fwlrole == 8) mode = wl_rinfo_v8->link_mode; else - return; + goto out; - seq_printf(m, " %-15s : link_mode:%d, ", "[status]", mode); + p += scnprintf(p, end - p, " %-15s : link_mode:%d, ", "[status]", + mode); - seq_printf(m, - "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", - wl->status.map.rf_off, wl->status.map.lps, - wl->status.map.scan ? "Y" : "N", - wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); + p += scnprintf(p, end - p, + "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", + wl->status.map.rf_off, wl->status.map.lps, + wl->status.map.scan ? "Y" : "N", + wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); - seq_printf(m, - "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", - wl->status.map.connecting ? "Y" : "N", - wl->status.map.roaming ? "Y" : "N", - wl->status.map._4way ? "Y" : "N", - wl->status.map.init_ok ? "Y" : "N"); + p += scnprintf(p, end - p, + "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", + wl->status.map.connecting ? "Y" : "N", + wl->status.map.roaming ? "Y" : "N", + wl->status.map._4way ? "Y" : "N", + wl->status.map.init_ok ? "Y" : "N"); - _show_wl_role_info(rtwdev, m); + p += _show_wl_role_info(rtwdev, p, end - p); + +out: + return p - buf; } enum btc_bt_a2dp_type { @@ -8355,7 +8372,7 @@ enum btc_bt_a2dp_type { BTC_A2DP_TWS_RELAY = 2, }; -static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; @@ -8363,50 +8380,55 @@ static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; + char *p = buf, *end = buf + bufsz; if (hfp.exist) { - seq_printf(m, " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", - "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), - bt_linfo->sut_pwr_level[0], - bt_linfo->golden_rx_shift[0]); + p += scnprintf(p, end - p, + " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", + "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), + bt_linfo->sut_pwr_level[0], + bt_linfo->golden_rx_shift[0]); } if (hid.exist) { - seq_printf(m, - "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", - "[HID]", - hid.type & BTC_HID_218 ? "2/18," : "", - hid.type & BTC_HID_418 ? "4/18," : "", - hid.type & BTC_HID_BLE ? "BLE," : "", - hid.type & BTC_HID_RCU ? "RCU," : "", - hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", - hid.pair_cnt, bt_linfo->sut_pwr_level[1], - bt_linfo->golden_rx_shift[1]); + p += scnprintf(p, end - p, + "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", + "[HID]", + hid.type & BTC_HID_218 ? "2/18," : "", + hid.type & BTC_HID_418 ? "4/18," : "", + hid.type & BTC_HID_BLE ? "BLE," : "", + hid.type & BTC_HID_RCU ? "RCU," : "", + hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", + hid.pair_cnt, bt_linfo->sut_pwr_level[1], + bt_linfo->golden_rx_shift[1]); } if (a2dp.exist) { - seq_printf(m, - " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", - "[A2DP]", - a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", - a2dp.bitpool, a2dp.flush_time); + p += scnprintf(p, end - p, + " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", + "[A2DP]", + a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", + a2dp.bitpool, a2dp.flush_time); - seq_printf(m, - "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", - a2dp.vendor_id, a2dp.device_name, - bt_linfo->sut_pwr_level[2], - bt_linfo->golden_rx_shift[2]); + p += scnprintf(p, end - p, + "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", + a2dp.vendor_id, a2dp.device_name, + bt_linfo->sut_pwr_level[2], + bt_linfo->golden_rx_shift[2]); } if (pan.exist) { - seq_printf(m, " %-15s : sut_pwr:%d, golden-rx:%d\n", - "[PAN]", - bt_linfo->sut_pwr_level[3], - bt_linfo->golden_rx_shift[3]); + p += scnprintf(p, end - p, + " %-15s : sut_pwr:%d, golden-rx:%d\n", + "[PAN]", + bt_linfo->sut_pwr_level[3], + bt_linfo->golden_rx_shift[3]); } + + return p - buf; } -static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; @@ -8415,129 +8437,136 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; union rtw89_btc_module_info *md = &btc->mdinfo; + char *p = buf, *end = buf + bufsz; u8 *afh = bt_linfo->afh_map; u8 *afh_le = bt_linfo->afh_map_le; u8 bt_pos; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT)) - return; + return 0; if (ver->fcxinit == 7) bt_pos = md->md_v7.bt_pos; else bt_pos = md->md.bt_pos; - seq_puts(m, "========== [BT Status] ==========\n"); + p += scnprintf(p, end - p, "========== [BT Status] ==========\n"); - seq_printf(m, " %-15s : enable:%s, btg:%s%s, connect:%s, ", - "[status]", bt->enable.now ? "Y" : "N", - bt->btg_type ? "Y" : "N", - (bt->enable.now && (bt->btg_type != bt_pos) ? - "(efuse-mismatch!!)" : ""), - (bt_linfo->status.map.connect ? "Y" : "N")); + p += scnprintf(p, end - p, + " %-15s : enable:%s, btg:%s%s, connect:%s, ", + "[status]", bt->enable.now ? "Y" : "N", + bt->btg_type ? "Y" : "N", + (bt->enable.now && (bt->btg_type != bt_pos) ? + "(efuse-mismatch!!)" : ""), + (bt_linfo->status.map.connect ? "Y" : "N")); - seq_printf(m, "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", - bt->igno_wl ? "Y" : "N", - bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); + p += scnprintf(p, end - p, + "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", + bt->igno_wl ? "Y" : "N", + bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); - seq_printf(m, " %-15s : profile:%s%s%s%s%s ", - "[profile]", - (bt_linfo->profile_cnt.now == 0) ? "None," : "", - bt_linfo->hfp_desc.exist ? "HFP," : "", - bt_linfo->hid_desc.exist ? "HID," : "", - bt_linfo->a2dp_desc.exist ? - (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", - bt_linfo->pan_desc.exist ? "PAN," : ""); + p += scnprintf(p, end - p, " %-15s : profile:%s%s%s%s%s ", + "[profile]", + (bt_linfo->profile_cnt.now == 0) ? "None," : "", + bt_linfo->hfp_desc.exist ? "HFP," : "", + bt_linfo->hid_desc.exist ? "HID," : "", + bt_linfo->a2dp_desc.exist ? + (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", + bt_linfo->pan_desc.exist ? "PAN," : ""); - seq_printf(m, - "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", - bt_linfo->multi_link.now ? "Y" : "N", - bt_linfo->slave_role ? "Slave" : "Master", - bt_linfo->status.map.ble_connect ? "Y" : "N", - bt_linfo->cqddr ? "Y" : "N", - bt_linfo->a2dp_desc.active ? "Y" : "N", - bt_linfo->pan_desc.active ? "Y" : "N"); + p += scnprintf(p, end - p, + "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", + bt_linfo->multi_link.now ? "Y" : "N", + bt_linfo->slave_role ? "Slave" : "Master", + bt_linfo->status.map.ble_connect ? "Y" : "N", + bt_linfo->cqddr ? "Y" : "N", + bt_linfo->a2dp_desc.active ? "Y" : "N", + bt_linfo->pan_desc.active ? "Y" : "N"); - seq_printf(m, - " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", - "[link]", bt_linfo->rssi - 100, - bt->rssi_level, - bt_linfo->tx_3m ? 3 : 2, - bt_linfo->status.map.inq_pag ? " inq-page!!" : "", - bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", - bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); + p += scnprintf(p, end - p, + " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", + "[link]", bt_linfo->rssi - 100, + bt->rssi_level, + bt_linfo->tx_3m ? 3 : 2, + bt_linfo->status.map.inq_pag ? " inq-page!!" : "", + bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", + bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); - seq_printf(m, - "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", - bt_linfo->relink.now ? " ReLink!!" : "", - afh[0], afh[1], afh[2], afh[3], afh[4], - afh[5], afh[6], afh[7], afh[8], afh[9]); + p += scnprintf(p, end - p, + "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", + bt_linfo->relink.now ? " ReLink!!" : "", + afh[0], afh[1], afh[2], afh[3], afh[4], + afh[5], afh[6], afh[7], afh[8], afh[9]); if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect) - seq_printf(m, - "LE[%02x%02x_%02x_%02x%02x]", - afh_le[0], afh_le[1], afh_le[2], - afh_le[3], afh_le[4]); + p += scnprintf(p, end - p, + "LE[%02x%02x_%02x_%02x%02x]", + afh_le[0], afh_le[1], afh_le[2], + afh_le[3], afh_le[4]); - seq_printf(m, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", - wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); + p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", + wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); - seq_printf(m, - " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", - "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], - cx->cnt_bt[BTC_BCNT_RELINK], cx->cnt_bt[BTC_BCNT_RATECHG], - cx->cnt_bt[BTC_BCNT_REINIT], cx->cnt_bt[BTC_BCNT_REENABLE]); + p += scnprintf(p, end - p, + " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", + "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], + cx->cnt_bt[BTC_BCNT_RELINK], + cx->cnt_bt[BTC_BCNT_RATECHG], + cx->cnt_bt[BTC_BCNT_REINIT], + cx->cnt_bt[BTC_BCNT_REENABLE]); - seq_printf(m, - "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", - cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], - cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], - cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); + p += scnprintf(p, end - p, + "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", + cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], + cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], + cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); - _show_bt_profile_info(rtwdev, m); + p += _show_bt_profile_info(rtwdev, p, end - p); - seq_printf(m, - " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", - "[bt_info]", bt->raw_info[2], bt->raw_info[3], - bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], - bt->raw_info[7], - bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", - cx->cnt_bt[BTC_BCNT_INFOUPDATE], - cx->cnt_bt[BTC_BCNT_INFOSAME]); + p += scnprintf(p, end - p, + " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", + "[bt_info]", bt->raw_info[2], bt->raw_info[3], + bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], + bt->raw_info[7], + bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", + cx->cnt_bt[BTC_BCNT_INFOUPDATE], + cx->cnt_bt[BTC_BCNT_INFOSAME]); - seq_printf(m, - " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", - "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], - cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX], - cx->cnt_bt[BTC_BCNT_LOPRI_TX], cx->cnt_bt[BTC_BCNT_POLUT]); + p += scnprintf(p, end - p, + " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", + "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], + cx->cnt_bt[BTC_BCNT_HIPRI_TX], + cx->cnt_bt[BTC_BCNT_LOPRI_RX], + cx->cnt_bt[BTC_BCNT_LOPRI_TX], + cx->cnt_bt[BTC_BCNT_POLUT]); if (!bt->scan_info_update) { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } else { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false); if (ver->fcxbtscan == 1) { - seq_printf(m, - "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); + p += scnprintf(p, end - p, + "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); } else if (ver->fcxbtscan == 2) { - seq_printf(m, - "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); + p += scnprintf(p, end - p, + "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (bt_linfo->profile_cnt.now || bt_linfo->status.map.ble_connect) @@ -8557,6 +8586,8 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true); else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false); + + return p - buf; } #define CASE_BTC_RSN_STR(e) case BTC_RSN_ ## e: return #e @@ -8850,114 +8881,132 @@ static const char *id_to_ant(u32 id) } static -void seq_print_segment(struct seq_file *m, const char *prefix, u16 *data, - u8 len, u8 seg_len, u8 start_idx, u8 ring_len) +int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data, + u8 len, u8 seg_len, u8 start_idx, u8 ring_len) { - u8 i; + char *p = buf, *end = buf + bufsz; u8 cur_index; + u8 i; for (i = 0; i < len ; i++) { if ((i % seg_len) == 0) - seq_printf(m, " %-15s : ", prefix); + p += scnprintf(p, end - p, " %-15s : ", prefix); cur_index = (start_idx + i) % ring_len; if (i % 3 == 0) - seq_printf(m, "-> %-20s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-20s", + steps_to_str(*(data + cur_index))); else if (i % 3 == 1) - seq_printf(m, "-> %-15s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-15s", + steps_to_str(*(data + cur_index))); else - seq_printf(m, "-> %-13s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-13s", + steps_to_str(*(data + cur_index))); if (i == (len - 1) || (i % seg_len) == (seg_len - 1)) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + + return p - buf; } -static void _show_dm_step(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u8 start_idx; u8 len; len = dm->dm_step.step_ov ? RTW89_BTC_DM_MAXSTEP : dm->dm_step.step_pos; start_idx = dm->dm_step.step_ov ? dm->dm_step.step_pos : 0; - seq_print_segment(m, "[dm_steps]", dm->dm_step.step, len, 6, start_idx, - ARRAY_SIZE(dm->dm_step.step)); + p += scnprintf_segment(p, end - p, "[dm_steps]", dm->dm_step.step, len, + 6, start_idx, ARRAY_SIZE(dm->dm_step.step)); + + return p - buf; } -static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt; + char *p = buf, *end = buf + bufsz; u8 igno_bt; if (!(dm->coex_info_map & BTC_COEX_INFO_DM)) - return; + return 0; - seq_printf(m, "========== [Mechanism Status %s] ==========\n", - (btc->manual_ctrl ? "(Manual)" : "(Auto)")); + p += scnprintf(p, end - p, + "========== [Mechanism Status %s] ==========\n", + (btc->manual_ctrl ? "(Manual)" : "(Auto)")); - seq_printf(m, - " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", - "[status]", - btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", - steps_to_str(dm->run_reason), - steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), - id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), - id_to_mode(wl->coex_mode), - dm->cnt_dm[BTC_DCNT_RUN]); + p += scnprintf(p, end - p, + " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", + "[status]", + btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", + steps_to_str(dm->run_reason), + steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), + id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), + id_to_mode(wl->coex_mode), + dm->cnt_dm[BTC_DCNT_RUN]); - _show_dm_step(rtwdev, m); + p += _show_dm_step(rtwdev, p, end - p); if (ver->fcxctrl == 7) igno_bt = btc->ctrl.ctrl_v7.igno_bt; else igno_bt = btc->ctrl.ctrl.igno_bt; - seq_printf(m, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", - "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, - dm->freerun, btc->lps, dm->wl_mimo_ps); + p += scnprintf(p, end - p, + " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", + "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, + dm->freerun, btc->lps, dm->wl_mimo_ps); - seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap, - (BTC_CX_FW_OFFLOAD ? "Y" : "N"), - (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? - "" : "(Mismatch!!)")); + p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n", + dm->leak_ap, + (BTC_CX_FW_OFFLOAD ? "Y" : "N"), + (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? + "" : "(Mismatch!!)")); if (dm->rf_trx_para.wl_tx_power == 0xff) - seq_printf(m, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", - "[trx_ctrl]", wl->rssi_level, dm->trx_para_level); + p += scnprintf(p, end - p, + " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", + "[trx_ctrl]", wl->rssi_level, + dm->trx_para_level); else - seq_printf(m, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", - "[trx_ctrl]", wl->rssi_level, dm->trx_para_level, - dm->rf_trx_para.wl_tx_power); + p += scnprintf(p, end - p, + " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", + "[trx_ctrl]", wl->rssi_level, + dm->trx_para_level, + dm->rf_trx_para.wl_tx_power); - seq_printf(m, - "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", - dm->rf_trx_para.wl_rx_gain, dm->rf_trx_para.bt_tx_power, - dm->rf_trx_para.bt_rx_gain, - (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); + p += scnprintf(p, end - p, + "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", + dm->rf_trx_para.wl_rx_gain, + dm->rf_trx_para.bt_tx_power, + dm->rf_trx_para.bt_rx_gain, + (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); - seq_printf(m, - " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", - "[dm_ctrl]", dm->wl_tx_limit.enable, dm->wl_tx_limit.tx_time, - dm->wl_tx_limit.tx_retry, btc->bt_req_len, bt->scan_rx_low_pri); + p += scnprintf(p, end - p, + " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", + "[dm_ctrl]", dm->wl_tx_limit.enable, + dm->wl_tx_limit.tx_time, + dm->wl_tx_limit.tx_retry, btc->bt_req_len, + bt->scan_rx_low_pri); + + return p - buf; } -static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_error(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; union rtw89_btc_fbtc_cysta_info *pcysta; + char *p = buf, *end = buf + bufsz; u32 except_cnt, exception_map; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo; @@ -8982,81 +9031,87 @@ static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) except_cnt = pcysta->v7.except_cnt; exception_map = le32_to_cpu(pcysta->v7.except_map); } else { - return; + return 0; } if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW] == 0 && except_cnt == 0 && !pfwinfo->len_mismch && !pfwinfo->fver_mismch) - return; + return 0; - seq_printf(m, " %-15s : ", "[error]"); + p += scnprintf(p, end - p, " %-15s : ", "[error]"); if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]) { - seq_printf(m, - "overflow-cnt: %d, ", - pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]); + p += scnprintf(p, end - p, + "overflow-cnt: %d, ", + pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]); } if (pfwinfo->len_mismch) { - seq_printf(m, - "len-mismatch: 0x%x, ", - pfwinfo->len_mismch); + p += scnprintf(p, end - p, + "len-mismatch: 0x%x, ", + pfwinfo->len_mismch); } if (pfwinfo->fver_mismch) { - seq_printf(m, - "fver-mismatch: 0x%x, ", - pfwinfo->fver_mismch); + p += scnprintf(p, end - p, + "fver-mismatch: 0x%x, ", + pfwinfo->fver_mismch); } /* cycle statistics exceptions */ if (exception_map || except_cnt) { - seq_printf(m, - "exception-type: 0x%x, exception-cnt = %d", - exception_map, except_cnt); + p += scnprintf(p, end - p, + "exception-type: 0x%x, exception-cnt = %d", + exception_map, except_cnt); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_tdma(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_tdma *t = NULL; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo; if (!pcinfo->valid) - return; + return 0; if (ver->fcxtdma == 1) t = &pfwinfo->rpt_fbtc_tdma.finfo.v1; else t = &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma; - seq_printf(m, - " %-15s : ", "[tdma_policy]"); - seq_printf(m, - "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", - (u32)t->type, - t->rxflctrl, t->txpause); + p += scnprintf(p, end - p, + " %-15s : ", "[tdma_policy]"); + p += scnprintf(p, end - p, + "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", + (u32)t->type, + t->rxflctrl, t->txpause); - seq_printf(m, - "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", - t->wtgle_n, t->leak_n, t->ext_ctrl); + p += scnprintf(p, end - p, + "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", + t->wtgle_n, t->leak_n, t->ext_ctrl); - seq_printf(m, - "policy_type:%d", - (u32)btc->policy_type); + p += scnprintf(p, end - p, + "policy_type:%d", + (u32)btc->policy_type); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u16 dur, cxtype; u32 tbl; u8 i = 0; @@ -9071,28 +9126,30 @@ static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m) tbl = le32_to_cpu(dm->slot_now.v7[i].cxtbl); cxtype = le16_to_cpu(dm->slot_now.v7[i].cxtype); } else { - return; + return 0; } if (i % 5 == 0) - seq_printf(m, - " %-15s : %5s[%03d/0x%x/%d]", - "[slot_list]", - id_to_slot((u32)i), - dur, tbl, cxtype); + p += scnprintf(p, end - p, + " %-15s : %5s[%03d/0x%x/%d]", + "[slot_list]", + id_to_slot((u32)i), + dur, tbl, cxtype); else - seq_printf(m, - ", %5s[%03d/0x%x/%d]", - id_to_slot((u32)i), - dur, tbl, cxtype); + p += scnprintf(p, end - p, + ", %5s[%03d/0x%x/%d]", + id_to_slot((u32)i), + dur, tbl, cxtype); if (i % 5 == 4) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -9101,63 +9158,64 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_cysta_v2 *pcysta_le32 = NULL; union rtw89_btc_fbtc_rxflct r; - u8 i, cnt = 0, slot_pair; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; + u8 i, cnt = 0, slot_pair; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo.v2; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta_le32->cycles), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta_le32->cycles), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le32_to_cpu(pcysta_le32->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot((u32)i), - le32_to_cpu(pcysta_le32->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot((u32)i), + le32_to_cpu(pcysta_le32->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) { - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta_le32->leakrx_cnt)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta_le32->leakrx_cnt)); } if (le32_to_cpu(pcysta_le32->collision_cnt)) { - seq_printf(m, ", collision:%d", - le32_to_cpu(pcysta_le32->collision_cnt)); + p += scnprintf(p, end - p, ", collision:%d", + le32_to_cpu(pcysta_le32->collision_cnt)); } if (le32_to_cpu(pcysta_le32->skip_cnt)) { - seq_printf(m, ", skip:%d", - le32_to_cpu(pcysta_le32->skip_cnt)); + p += scnprintf(p, end - p, ", skip:%d", + le32_to_cpu(pcysta_le32->skip_cnt)); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), - le16_to_cpu(pcysta_le32->tavg_lk) / 1000, - le16_to_cpu(pcysta_le32->tavg_lk) % 1000); - seq_printf(m, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), - le16_to_cpu(pcysta_le32->tmax_lk) / 1000, - le16_to_cpu(pcysta_le32->tmax_lk) % 1000); - seq_printf(m, ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT])); + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), + le16_to_cpu(pcysta_le32->tavg_lk) / 1000, + le16_to_cpu(pcysta_le32->tavg_lk) % 1000); + p += scnprintf(p, end - p, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), + le16_to_cpu(pcysta_le32->tmax_lk) / 1000, + le16_to_cpu(pcysta_le32->tmax_lk) % 1000); + p += scnprintf(p, end - p, ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT])); if (le16_to_cpu(pcysta_le32->cycles) <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9174,53 +9232,57 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 1) - seq_printf(m, - " %-15s : ->b%02d->w%02d", "[cycle_step]", - le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), - le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); + p += scnprintf(p, end - p, + " %-15s : ->b%02d->w%02d", + "[cycle_step]", + le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), + le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); else - seq_printf(m, - "->b%02d->w%02d", - le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), - le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); + p += scnprintf(p, end - p, + "->b%02d->w%02d", + le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), + le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, - " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta_le32->a2dpept), - le16_to_cpu(pcysta_le32->a2dpeptto)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta_le32->a2dpept), + le16_to_cpu(pcysta_le32->a2dpeptto)); - seq_printf(m, - ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta_le32->tavg_a2dpept), - le16_to_cpu(pcysta_le32->tmax_a2dpept)); + p += scnprintf(p, end - p, + ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta_le32->tavg_a2dpept), + le16_to_cpu(pcysta_le32->tmax_a2dpept)); r.val = dm->tdma_now.rxflctrl; if (r.type && r.tgln_n) { - seq_printf(m, - ", cycle[PSTDMA:%d/TDMA:%d], ", - le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); + p += scnprintf(p, end - p, + ", cycle[PSTDMA:%d/TDMA:%d], ", + le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); - seq_printf(m, - "avg_t[PSTDMA:%d/TDMA:%d], ", - le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); + p += scnprintf(p, end - p, + "avg_t[PSTDMA:%d/TDMA:%d], ", + le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); - seq_printf(m, - "max_t[PSTDMA:%d/TDMA:%d]", - le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF])); + p += scnprintf(p, end - p, + "max_t[PSTDMA:%d/TDMA:%d]", + le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF])); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9231,60 +9293,64 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v3; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le32_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le32_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le32_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (le32_to_cpu(pcysta->collision_cnt)) - seq_printf(m, ", collision:%d", le32_to_cpu(pcysta->collision_cnt)); + p += scnprintf(p, end - p, ", collision:%d", + le32_to_cpu(pcysta->collision_cnt)); if (le32_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", le32_to_cpu(pcysta->skip_cnt)); + p += scnprintf(p, end - p, ", skip:%d", + le32_to_cpu(pcysta->skip_cnt)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); - seq_printf(m, - ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, + ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9306,51 +9372,56 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9361,62 +9432,64 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v4; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (le16_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", - le16_to_cpu(pcysta->skip_cnt)); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); - seq_printf(m, - ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, + ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9438,51 +9511,56 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9493,58 +9571,60 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v5; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (le16_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", - le16_to_cpu(pcysta->skip_cnt)); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9562,58 +9642,63 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) divide_cnt = BTC_CYCLE_SLOT_MAX / 4; if (c_begin > c_end) - return; + goto out; for (cycle = c_begin; cycle <= c_end; cycle++) { cnt++; store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt; struct rtw89_btc_bt_a2dp_desc *a2dp = &bt->link_info.a2dp_desc; @@ -9621,68 +9706,75 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_fbtc_cysta_v7 *pcysta = NULL; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_rpt_cmn_info *pcinfo; + char *p = buf, *end = buf + bufsz; u16 cycle, c_begin, c_end, s_id; u8 i, cnt = 0, divide_cnt; u8 slot_pair; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v7; - seq_printf(m, "\n\r %-15s : cycle:%d", "[slot_stat]", - le16_to_cpu(pcysta->cycles)); + p += scnprintf(p, end - p, "\n\r %-15s : cycle:%d", "[slot_stat]", + le16_to_cpu(pcysta->cycles)); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", - id_to_slot(i), le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", + id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (pcysta->skip_cnt) - seq_printf(m, ", skip:%d", le16_to_cpu(pcysta->skip_cnt)); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); - seq_printf(m, "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_stat]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], - le16_to_cpu(pcysta->leak_slot.tamx) / 1000, - le16_to_cpu(pcysta->leak_slot.tamx) % 1000); - seq_printf(m, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_stat]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], + le16_to_cpu(pcysta->leak_slot.tamx) / 1000, + le16_to_cpu(pcysta->leak_slot.tamx) % 1000); + p += scnprintf(p, end - p, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); if (a2dp->exist) { - seq_printf(m, - "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", - "[a2dp_stat]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), - a2dp->no_empty_streak_2s, a2dp->no_empty_streak_max); + p += scnprintf(p, end - p, + "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", + "[a2dp_stat]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), + a2dp->no_empty_streak_2s, + a2dp->no_empty_streak_max); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); } if (le16_to_cpu(pcysta->cycles) <= 1) - return; + goto out; /* 1 cycle = 1 wl-slot + 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9700,7 +9792,7 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) divide_cnt = 6; if (c_begin > c_end) - return; + goto out; for (cycle = c_begin; cycle <= c_end; cycle++) { cnt++; @@ -9708,129 +9800,142 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) if (cnt % divide_cnt == 1) { if (a2dp->exist) - seq_printf(m, "\n\r %-15s : ", "[slotT_wermtan]"); + p += scnprintf(p, end - p, "\n\r %-15s : ", + "[slotT_wermtan]"); else - seq_printf(m, "\n\r %-15s : ", "[slotT_rxerr]"); + p += scnprintf(p, end - p, "\n\r %-15s : ", + "[slotT_rxerr]"); } - seq_printf(m, "->b%d", le16_to_cpu(pcysta->slot_step_time[s_id])); + p += scnprintf(p, end - p, "->b%d", + le16_to_cpu(pcysta->slot_step_time[s_id])); if (a2dp->exist) - seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", - pcysta->wl_rx_err_ratio[s_id], - pcysta->a2dp_trx[s_id].empty_cnt, - pcysta->a2dp_trx[s_id].retry_cnt, - (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), - pcysta->a2dp_trx[s_id].tx_cnt, - pcysta->a2dp_trx[s_id].ack_cnt, - pcysta->a2dp_trx[s_id].nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", + pcysta->wl_rx_err_ratio[s_id], + pcysta->a2dp_trx[s_id].empty_cnt, + pcysta->a2dp_trx[s_id].retry_cnt, + (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), + pcysta->a2dp_trx[s_id].tx_cnt, + pcysta->a2dp_trx[s_id].ack_cnt, + pcysta->a2dp_trx[s_id].nack_cnt); else - seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id]); + p += scnprintf(p, end - p, "(%d)", + pcysta->wl_rx_err_ratio[s_id]); - seq_printf(m, "->w%d", le16_to_cpu(pcysta->slot_step_time[s_id + 1])); + p += scnprintf(p, end - p, "->w%d", + le16_to_cpu(pcysta->slot_step_time[s_id + 1])); if (a2dp->exist) - seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", - pcysta->wl_rx_err_ratio[s_id + 1], - pcysta->a2dp_trx[s_id + 1].empty_cnt, - pcysta->a2dp_trx[s_id + 1].retry_cnt, - (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), - pcysta->a2dp_trx[s_id + 1].tx_cnt, - pcysta->a2dp_trx[s_id + 1].ack_cnt, - pcysta->a2dp_trx[s_id + 1].nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", + pcysta->wl_rx_err_ratio[s_id + 1], + pcysta->a2dp_trx[s_id + 1].empty_cnt, + pcysta->a2dp_trx[s_id + 1].retry_cnt, + (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), + pcysta->a2dp_trx[s_id + 1].tx_cnt, + pcysta->a2dp_trx[s_id + 1].ack_cnt, + pcysta->a2dp_trx[s_id + 1].nack_cnt); else - seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id + 1]); + p += scnprintf(p, end - p, "(%d)", + pcysta->wl_rx_err_ratio[s_id + 1]); } + +out: + return p - buf; } -static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo; union rtw89_btc_fbtc_cynullsta_info *ns; + char *p = buf, *end = buf + bufsz; u8 i = 0; if (!btc->dm.tdma_now.rxflctrl) - return; + return 0; pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo; if (!pcinfo->valid) - return; + return 0; ns = &pfwinfo->rpt_fbtc_nullsta.finfo; if (ver->fcxnullsta == 1) { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v1.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v1.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v1.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v1.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v1.avg_t[i]) / 1000, - le32_to_cpu(ns->v1.avg_t[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v1.max_t[i]) / 1000, - le32_to_cpu(ns->v1.max_t[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v1.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v1.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v1.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v1.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v1.avg_t[i]) / 1000, + le32_to_cpu(ns->v1.avg_t[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v1.max_t[i]) / 1000, + le32_to_cpu(ns->v1.max_t[i]) % 1000); } } else if (ver->fcxnullsta == 7) { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[Tx:%d/", - le32_to_cpu(ns->v7.result[i][4])); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v7.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v7.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v7.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v7.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v7.tavg[i]) / 1000, - le32_to_cpu(ns->v7.tavg[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v7.tmax[i]) / 1000, - le32_to_cpu(ns->v7.tmax[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[Tx:%d/", + le32_to_cpu(ns->v7.result[i][4])); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v7.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v7.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v7.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v7.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v7.tavg[i]) / 1000, + le32_to_cpu(ns->v7.tavg[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v7.tmax[i]) / 1000, + le32_to_cpu(ns->v7.tmax[i]) % 1000); } } else { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[Tx:%d/", - le32_to_cpu(ns->v2.result[i][4])); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v2.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v2.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v2.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v2.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v2.avg_t[i]) / 1000, - le32_to_cpu(ns->v2.avg_t[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v2.max_t[i]) / 1000, - le32_to_cpu(ns->v2.max_t[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[Tx:%d/", + le32_to_cpu(ns->v2.result[i][4])); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v2.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v2.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v2.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v2.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v2.avg_t[i]) / 1000, + le32_to_cpu(ns->v2.avg_t[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v2.max_t[i]) / 1000, + le32_to_cpu(ns->v2.max_t[i]) % 1000); } } + + return p - buf; } -static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_steps_v2 *pstep = NULL; const struct rtw89_btc_ver *ver = btc->ver; + char *p = buf, *end = buf + bufsz; u8 type, val, cnt = 0, state = 0; bool outloop = false; u16 i, diff_t, n_start = 0, n_stop = 0; @@ -9838,14 +9943,14 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; if (!pcinfo->valid) - return; + return 0; pstep = &pfwinfo->rpt_fbtc_step.finfo.v2; pos_old = le16_to_cpu(pstep->pos_old); pos_new = le16_to_cpu(pstep->pos_new); if (pcinfo->req_fver != pstep->fver) - return; + return 0; /* store step info by using ring instead of FIFO*/ do { @@ -9874,13 +9979,15 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) continue; if (cnt % 10 == 0) - seq_printf(m, " %-15s : ", "[steps]"); + p += scnprintf(p, end - p, + " %-15s : ", "[steps]"); - seq_printf(m, "-> %s(%02d)(%02d)", - (type == CXSTEP_SLOT ? "SLT" : - "EVT"), (u32)val, diff_t); + p += scnprintf(p, end - p, + "-> %s(%02d)(%02d)", + (type == CXSTEP_SLOT ? "SLT" : + "EVT"), (u32)val, diff_t); if (cnt % 10 == 9) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; } @@ -9897,29 +10004,32 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) break; } } while (!outloop); + + return p - buf; } -static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_step_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo; struct rtw89_btc_fbtc_steps_v3 *pstep; u32 i, n_begin, n_end, array_idx, cnt = 0; + char *p = buf, *end = buf + bufsz; u8 type, val; u16 diff_t; if ((pfwinfo->rpt_en_map & rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_FW_STEP_INFO)) == 0) - return; + return 0; pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; if (!pcinfo->valid) - return; + return 0; pstep = &pfwinfo->rpt_fbtc_step.finfo.v3; if (pcinfo->req_fver != pstep->fver) - return; + return 0; if (le32_to_cpu(pstep->cnt) <= FCXDEF_STEP) n_begin = 1; @@ -9929,7 +10039,7 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) n_end = le32_to_cpu(pstep->cnt); if (n_begin > n_end) - return; + return 0; /* restore step info by using ring instead of FIFO */ for (i = n_begin; i <= n_end; i++) { @@ -9942,50 +10052,55 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) continue; if (cnt % 10 == 0) - seq_printf(m, " %-15s : ", "[steps]"); + p += scnprintf(p, end - p, " %-15s : ", "[steps]"); - seq_printf(m, "-> %s(%02d)", - (type == CXSTEP_SLOT ? - id_to_slot((u32)val) : - id_to_evt((u32)val)), diff_t); + p += scnprintf(p, end - p, "-> %s(%02d)", + (type == CXSTEP_SLOT ? + id_to_slot((u32)val) : + id_to_evt((u32)val)), diff_t); if (cnt % 10 == 9) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; } + + return p - buf; } -static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; + char *p = buf, *end = buf + bufsz; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_DM)) - return; + goto out; - _show_error(rtwdev, m); - _show_fbtc_tdma(rtwdev, m); - _show_fbtc_slots(rtwdev, m); + p += _show_error(rtwdev, p, end - p); + p += _show_fbtc_tdma(rtwdev, p, end - p); + p += _show_fbtc_slots(rtwdev, p, end - p); if (ver->fcxcysta == 2) - _show_fbtc_cysta_v2(rtwdev, m); + p += _show_fbtc_cysta_v2(rtwdev, p, end - p); else if (ver->fcxcysta == 3) - _show_fbtc_cysta_v3(rtwdev, m); + p += _show_fbtc_cysta_v3(rtwdev, p, end - p); else if (ver->fcxcysta == 4) - _show_fbtc_cysta_v4(rtwdev, m); + p += _show_fbtc_cysta_v4(rtwdev, p, end - p); else if (ver->fcxcysta == 5) - _show_fbtc_cysta_v5(rtwdev, m); + p += _show_fbtc_cysta_v5(rtwdev, p, end - p); else if (ver->fcxcysta == 7) - _show_fbtc_cysta_v7(rtwdev, m); + p += _show_fbtc_cysta_v7(rtwdev, p, end - p); - _show_fbtc_nullsta(rtwdev, m); + p += _show_fbtc_nullsta(rtwdev, p, end - p); if (ver->fcxstep == 2) - _show_fbtc_step_v2(rtwdev, m); + p += _show_fbtc_step_v2(rtwdev, p, end - p); else if (ver->fcxstep == 3) - _show_fbtc_step_v3(rtwdev, m); + p += _show_fbtc_step_v3(rtwdev, p, end - p); +out: + return p - buf; } static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg) @@ -10030,12 +10145,13 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt } } -static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; union rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; + char *p = buf, *end = buf + bufsz; u8 *gpio_map, i; u32 en_map; @@ -10045,8 +10161,8 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n", __func__); - seq_puts(m, "\n"); - return; + p += scnprintf(p, end - p, "\n"); + goto out; } if (ver->fcxgpiodbg == 7) { @@ -10058,20 +10174,24 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) } if (!en_map) - return; + goto out; - seq_printf(m, " %-15s : enable_map:0x%08x", - "[gpio_dbg]", en_map); + p += scnprintf(p, end - p, " %-15s : enable_map:0x%08x", + "[gpio_dbg]", en_map); for (i = 0; i < BTC_DBG_MAX1; i++) { if (!(en_map & BIT(i))) continue; - seq_printf(m, ", %s->GPIO%d", id_to_gdbg(i), gpio_map[i]); + p += scnprintf(p, end - p, ", %s->GPIO%d", id_to_gdbg(i), + gpio_map[i]); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + +out: + return p - buf; } -static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; @@ -10083,45 +10203,47 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &btc->cx.bt; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; + char *p = buf, *end = buf + bufsz; u8 i = 0, type = 0, cnt = 0; u32 val, offset; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "========== [HW Status] ==========\n"); + p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); - seq_printf(m, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); gnt = gnt_cfg.band[0]; - seq_printf(m, - " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", - "[gnt_status]", - chip->chip_id == RTL8852C ? "HW" : - btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); + p += scnprintf(p, end - p, + " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", + "[gnt_status]", + chip->chip_id == RTL8852C ? "HW" : + btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); gnt = gnt_cfg.band[1]; - seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", - gnt.gnt_wl_sw_en ? "SW" : "HW", - gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", - gnt.gnt_bt); + p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", + gnt.gnt_wl_sw_en ? "SW" : "HW", + gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", + gnt.gnt_bt); pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n", __func__); - return; + goto out; } pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v1; @@ -10135,21 +10257,26 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, " %-15s : %d_0x%04x=0x%08x", - "[reg]", (u32)type, offset, val); + p += scnprintf(p, end - p, + " %-15s : %d_0x%04x=0x%08x", + "[reg]", (u32)type, offset, val); else - seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, - offset, val); + p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", + (u32)type, + offset, val); if (cnt % 6 == 5) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; if (i >= pmreg->reg_num) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; @@ -10161,46 +10288,48 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &btc->cx.bt; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; + char *p = buf, *end = buf + bufsz; u8 i = 0, type = 0, cnt = 0; u32 val, offset; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "========== [HW Status] ==========\n"); + p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); - seq_printf(m, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); gnt = gnt_cfg.band[0]; - seq_printf(m, - " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", - "[gnt_status]", - chip->chip_id == RTL8852C ? "HW" : - btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, - id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); + p += scnprintf(p, end - p, + " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", + "[gnt_status]", + chip->chip_id == RTL8852C ? "HW" : + btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, + id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); gnt = gnt_cfg.band[1]; - seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", - gnt.gnt_wl_sw_en ? "SW" : "HW", - gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", - gnt.gnt_bt); + p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", + gnt.gnt_wl_sw_en ? "SW" : "HW", + gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", + gnt.gnt_bt); pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n", __func__); - return; + goto out; } pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v2; @@ -10214,21 +10343,26 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, " %-15s : %d_0x%04x=0x%08x", - "[reg]", (u32)type, offset, val); + p += scnprintf(p, end - p, + " %-15s : %d_0x%04x=0x%08x", + "[reg]", (u32)type, offset, val); else - seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, - offset, val); + p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", + (u32)type, + offset, val); if (cnt % 6 == 5) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; if (i >= pmreg->reg_num) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10239,46 +10373,50 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &cx->bt; struct rtw89_mac_ax_gnt *gnt = NULL; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u8 i, type, cnt = 0; u32 val, offset; if (!(dm->coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "\n\r========== [HW Status] =========="); + p += scnprintf(p, end - p, "\n\r========== [HW Status] =========="); - seq_printf(m, - "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); /* To avoid I/O if WL LPS or power-off */ dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); - seq_printf(m, - "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", - "[gnt_status]", - rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : - dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); + p += scnprintf(p, end - p, + "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", + "[gnt_status]", + rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : + dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + wl->pta_req_mac, + id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); gnt = &dm->gnt.band[RTW89_PHY_0]; - seq_printf(m, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", + gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, + gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); if (rtwdev->dbcc_en) { gnt = &dm->gnt.band[RTW89_PHY_1]; - seq_printf(m, ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + p += scnprintf(p, end - p, + ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", + gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, + gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); } pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) - return; + goto out; pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v7; @@ -10288,17 +10426,21 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, "\n\r %-15s : %s_0x%x=0x%x", "[reg]", - id_to_regtype(type), offset, val); + p += scnprintf(p, end - p, + "\n\r %-15s : %s_0x%x=0x%x", "[reg]", + id_to_regtype(type), offset, val); else - seq_printf(m, ", %s_0x%x=0x%x", - id_to_regtype(type), offset, val); + p += scnprintf(p, end - p, ", %s_0x%x=0x%x", + id_to_regtype(type), offset, val); cnt++; } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + +out: + return p - buf; } -static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10309,56 +10451,59 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_info *bt = &cx->bt; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v1; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, - pfwinfo->cnt_c2h, prptctrl->c2h_cnt); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, + pfwinfo->cnt_c2h, prptctrl->c2h_cnt); - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", - pfwinfo->event[BTF_EVNT_RPT], prptctrl->rpt_cnt, - prptctrl->rpt_enable, dm->error.val); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", + pfwinfo->event[BTF_EVNT_RPT], + prptctrl->rpt_cnt, + prptctrl->rpt_enable, dm->error.val); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d", - "[mailbox]", prptctrl->mb_send_ok_cnt, - prptctrl->mb_send_fail_cnt, prptctrl->mb_recv_cnt); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d", + "[mailbox]", prptctrl->mb_send_ok_cnt, + prptctrl->mb_send_fail_cnt, + prptctrl->mb_recv_cnt); - seq_printf(m, - "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", - prptctrl->mb_a2dp_empty_cnt, - prptctrl->mb_a2dp_flct_cnt, - prptctrl->mb_a2dp_full_cnt); + p += scnprintf(p, end - p, + "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", + prptctrl->mb_a2dp_empty_cnt, + prptctrl->mb_a2dp_flct_cnt, + prptctrl->mb_a2dp_full_cnt); - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", + "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - seq_printf(m, - ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]); + p += scnprintf(p, end - p, + ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]); if (prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT] > 0) bt->rfk_info.map.timeout = 1; @@ -10367,42 +10512,44 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout; } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, - pfwinfo->event[BTF_EVNT_RPT], - btc->fwinfo.rpt_en_map); - seq_puts(m, " (WL FW report invalid!!)\n"); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, + pfwinfo->event[BTF_EVNT_RPT], + btc->fwinfo.rpt_en_map); + p += scnprintf(p, end - p, " (WL FW report invalid!!)\n"); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], - cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], + cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d\n", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d\n", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); + + return p - buf; } -static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10413,64 +10560,65 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_info *bt = &cx->bt; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v4; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, - le32_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le32_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le32_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en), - dm->error.val); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le32_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en), + dm->error.val); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", + "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - seq_printf(m, - ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL])); + p += scnprintf(p, end - p, + ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL])); if (le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]) > 0) bt->rfk_info.map.timeout = 1; @@ -10479,42 +10627,44 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout; } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, - pfwinfo->event[BTF_EVNT_RPT], - btc->fwinfo.rpt_en_map); - seq_puts(m, " (WL FW report invalid!!)\n"); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, + pfwinfo->event[BTF_EVNT_RPT], + btc->fwinfo.rpt_en_map); + p += scnprintf(p, end - p, " (WL FW report invalid!!)\n"); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], - cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], + cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d\n", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d\n", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); + + return p - buf; } -static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10524,112 +10674,118 @@ static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v5; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h)); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h)); - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - seq_printf(m, - ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + p += scnprintf(p, end - p, + ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - seq_printf(m, - ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, + ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); } if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch || pfwinfo->err[BTFRE_EXCEPTION]) { - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" - "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", - "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, - pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" + "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", + "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, + pfwinfo->fver_mismch, + pfwinfo->err[BTFRE_EXCEPTION], + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); + + return p - buf; } -static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10639,112 +10795,118 @@ static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v105; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h)); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h)); - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - seq_printf(m, - ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + p += scnprintf(p, end - p, + ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - seq_printf(m, - ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, + ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); } if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch || pfwinfo->err[BTFRE_EXCEPTION]) { - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" - "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", - "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, - pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" + "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", + "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, + pfwinfo->fver_mismch, + pfwinfo->err[BTFRE_EXCEPTION], + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); + + return p - buf; } -static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; struct rtw89_btc_fbtc_rpt_ctrl_v7 *prptctrl = NULL; @@ -10753,100 +10915,111 @@ static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u32 cnt_sum = 0; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_printf(m, "%s", "\n\r========== [Statistics] =========="); + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v7; - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," - "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h), - rtwdev->btc.ver->info_buf); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," + "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h), + rtwdev->btc.ver->info_buf); - seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); - seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - seq_printf(m, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], - wl->rfk_info.proc_time); + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); - seq_printf(m, ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + p += scnprintf(p, end - p, ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", - "[summary]", - pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - pfwinfo->cnt_c2h, - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], - rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], - cnt[BTC_NCNT_COUNTRYCODE]); + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); + + return p - buf; } -static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; @@ -10855,153 +11028,173 @@ static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u32 cnt_sum = 0; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_printf(m, "%s", "\n\r========== [Statistics] =========="); + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v8; - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h), - (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, - rtwdev->btc.ver->info_buf); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h), + (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, + rtwdev->btc.ver->info_buf); - seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); - seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - seq_printf(m, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], - wl->rfk_info.proc_time); + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); - seq_printf(m, ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + p += scnprintf(p, end - p, ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", - "[summary]", - pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - pfwinfo->cnt_c2h, - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], - rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], - cnt[BTC_NCNT_COUNTRYCODE]); + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); + + return p - buf; } -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m) +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_fw_suit *fw_suit = &rtwdev->fw.normal; struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_bt_info *bt = &cx->bt; + char *p = buf, *end = buf + bufsz; - seq_puts(m, "=========================================\n"); - seq_printf(m, "WL FW / BT FW %d.%d.%d.%d / NA\n", - fw_suit->major_ver, fw_suit->minor_ver, - fw_suit->sub_ver, fw_suit->sub_idex); - seq_printf(m, "manual %d\n", btc->manual_ctrl); + p += scnprintf(p, end - p, + "=========================================\n"); + p += scnprintf(p, end - p, + "WL FW / BT FW %d.%d.%d.%d / NA\n", + fw_suit->major_ver, fw_suit->minor_ver, + fw_suit->sub_ver, fw_suit->sub_idex); + p += scnprintf(p, end - p, "manual %d\n", + btc->manual_ctrl); - seq_puts(m, "=========================================\n"); + p += scnprintf(p, end - p, + "=========================================\n"); - seq_printf(m, "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)", - "[bt_info]", - bt->raw_info[2], bt->raw_info[3], - bt->raw_info[4], bt->raw_info[5], - bt->raw_info[6], bt->raw_info[7], - bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", - cx->cnt_bt[BTC_BCNT_INFOUPDATE], - cx->cnt_bt[BTC_BCNT_INFOSAME]); + p += scnprintf(p, end - p, + "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)", + "[bt_info]", + bt->raw_info[2], bt->raw_info[3], + bt->raw_info[4], bt->raw_info[5], + bt->raw_info[6], bt->raw_info[7], + bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", + cx->cnt_bt[BTC_BCNT_INFOUPDATE], + cx->cnt_bt[BTC_BCNT_INFOSAME]); - seq_puts(m, "\n=========================================\n"); + p += scnprintf(p, end - p, + "\n=========================================\n"); - _show_cx_info(rtwdev, m); - _show_wl_info(rtwdev, m); - _show_bt_info(rtwdev, m); - _show_dm_info(rtwdev, m); - _show_fw_dm_msg(rtwdev, m); + p += _show_cx_info(rtwdev, p, end - p); + p += _show_wl_info(rtwdev, p, end - p); + p += _show_bt_info(rtwdev, p, end - p); + p += _show_dm_info(rtwdev, p, end - p); + p += _show_fw_dm_msg(rtwdev, p, end - p); if (ver->fcxmreg == 1) - _show_mreg_v1(rtwdev, m); + p += _show_mreg_v1(rtwdev, p, end - p); else if (ver->fcxmreg == 2) - _show_mreg_v2(rtwdev, m); + p += _show_mreg_v2(rtwdev, p, end - p); else if (ver->fcxmreg == 7) - _show_mreg_v7(rtwdev, m); + p += _show_mreg_v7(rtwdev, p, end - p); - _show_gpio_dbg(rtwdev, m); + p += _show_gpio_dbg(rtwdev, p, end - p); if (ver->fcxbtcrpt == 1) - _show_summary_v1(rtwdev, m); + p += _show_summary_v1(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 4) - _show_summary_v4(rtwdev, m); + p += _show_summary_v4(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 5) - _show_summary_v5(rtwdev, m); + p += _show_summary_v5(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 105) - _show_summary_v105(rtwdev, m); + p += _show_summary_v105(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 7) - _show_summary_v7(rtwdev, m); + p += _show_summary_v7(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 8) - _show_summary_v8(rtwdev, m); + p += _show_summary_v8(rtwdev, p, end - p); + + return p - buf; } void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 30134f58d531..e3a1fcd79620 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -282,7 +282,7 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev); void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m); +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 09fa977a6e6d..db0e7a4beb07 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -13,6 +13,7 @@ #include "ps.h" #include "reg.h" #include "sar.h" +#include "util.h" #ifdef CONFIG_RTW89_DEBUGMSG unsigned int rtw89_debug_mask; @@ -24,9 +25,12 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask"); #ifdef CONFIG_RTW89_DEBUGFS struct rtw89_debugfs_priv { struct rtw89_dev *rtwdev; - int (*cb_read)(struct seq_file *m, void *v); - ssize_t (*cb_write)(struct file *filp, const char __user *buffer, - size_t count, loff_t *loff); + ssize_t (*cb_read)(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz); + ssize_t (*cb_write)(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count); union { u32 cb_data; struct { @@ -74,6 +78,28 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv disable_dm; }; +struct rtw89_debugfs_iter_data { + char *buf; + size_t bufsz; + int written_sz; +}; + +static void rtw89_debugfs_iter_data_setup(struct rtw89_debugfs_iter_data *iter_data, + char *buf, size_t bufsz) +{ + iter_data->buf = buf; + iter_data->bufsz = bufsz; + iter_data->written_sz = 0; +} + +static void rtw89_debugfs_iter_data_next(struct rtw89_debugfs_iter_data *iter_data, + char *buf, size_t bufsz, int written_sz) +{ + iter_data->buf = buf; + iter_data->bufsz = bufsz; + iter_data->written_sz += written_sz; +} + static const u16 rtw89_rate_info_bw_to_mhz_map[] = { [RATE_INFO_BW_20] = 20, [RATE_INFO_BW_40] = 40, @@ -90,84 +116,78 @@ static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw) return 0; } -static int rtw89_debugfs_single_show(struct seq_file *m, void *v) +static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; + struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + size_t bufsz = PAGE_SIZE; + char *buf; + ssize_t n; - return debugfs_priv->cb_read(m, v); + buf = kvmalloc(bufsz, GFP_KERNEL); + if (!buf) + return 0; + + n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); + rtw89_might_trailing_ellipsis(buf, bufsz, n); + + n = simple_read_from_buffer(userbuf, count, ppos, buf, n); + + kvfree(buf); + + return n; } -static ssize_t rtw89_debugfs_single_write(struct file *filp, - const char __user *buffer, - size_t count, loff_t *loff) +static ssize_t rtw89_debugfs_file_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *loff) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; + struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL); - return debugfs_priv->cb_write(filp, buffer, count, loff); -} + if (!buf) + return -ENOMEM; -static ssize_t rtw89_debugfs_seq_file_write(struct file *filp, - const char __user *buffer, - size_t count, loff_t *loff) -{ - struct seq_file *seqpriv = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private; + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; - return debugfs_priv->cb_write(filp, buffer, count, loff); -} + buf[count] = '\0'; -static int rtw89_debugfs_single_open(struct inode *inode, struct file *filp) -{ - return single_open(filp, rtw89_debugfs_single_show, inode->i_private); -} - -static int rtw89_debugfs_close(struct inode *inode, struct file *filp) -{ - return 0; + return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count); } static const struct file_operations file_ops_single_r = { .owner = THIS_MODULE, - .open = rtw89_debugfs_single_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, + .read = rtw89_debugfs_file_read, + .open = simple_open, + .llseek = generic_file_llseek, }; static const struct file_operations file_ops_common_rw = { .owner = THIS_MODULE, - .open = rtw89_debugfs_single_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek, - .write = rtw89_debugfs_seq_file_write, + .read = rtw89_debugfs_file_read, + .write = rtw89_debugfs_file_write, + .open = simple_open, + .llseek = generic_file_llseek, }; static const struct file_operations file_ops_single_w = { .owner = THIS_MODULE, - .write = rtw89_debugfs_single_write, + .write = rtw89_debugfs_file_write, .open = simple_open, - .release = rtw89_debugfs_close, + .llseek = generic_file_llseek, }; static ssize_t -rtw89_debug_priv_read_reg_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_read_reg_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x", &addr, &len); if (num != 2) { rtw89_info(rtwdev, "invalid format: \n"); @@ -182,11 +202,13 @@ rtw89_debug_priv_read_reg_select(struct file *filp, return count; } -static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_reg_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - u32 addr, end, data, k; + char *p = buf, *end = buf + bufsz; + u32 addr, addr_end, data, k; u32 len; len = debugfs_priv->read_reg.len; @@ -210,41 +232,34 @@ static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v) return -EINVAL; } - seq_printf(m, "get %d bytes at 0x%08x=0x%08x\n", len, addr, data); + p += scnprintf(p, end - p, "get %d bytes at 0x%08x=0x%08x\n", len, + addr, data); - return 0; + return p - buf; ndata: - end = addr + len; + addr_end = addr + len; - for (; addr < end; addr += 16) { - seq_printf(m, "%08xh : ", 0x18600000 + addr); + for (; addr < addr_end; addr += 16) { + p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + addr); for (k = 0; k < 16; k += 4) { data = rtw89_read32(rtwdev, addr + k); - seq_printf(m, "%08x ", data); + p += scnprintf(p, end - p, "%08x ", data); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - return 0; + return p - buf; } -static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_reg_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, val, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x %x", &addr, &val, &len); if (num != 3) { rtw89_info(rtwdev, "invalid format: \n"); @@ -273,24 +288,14 @@ static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp, } static ssize_t -rtw89_debug_priv_read_rf_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_read_rf_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, mask; u8 path; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%hhd %x %x", &path, &addr, &mask); if (num != 3) { rtw89_info(rtwdev, "invalid format: \n"); @@ -310,10 +315,12 @@ rtw89_debug_priv_read_rf_select(struct file *filp, return count; } -static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_rf_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; u32 addr, data, mask; u8 path; @@ -323,28 +330,21 @@ static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v) data = rtw89_read_rf(rtwdev, path, addr, mask); - seq_printf(m, "path %d, rf register 0x%08x=0x%08x\n", path, addr, data); + p += scnprintf(p, end - p, "path %d, rf register 0x%08x=0x%08x\n", + path, addr, data); - return 0; + return p - buf; } -static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_rf_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, val, mask; u8 path; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%hhd %x %x %x", &path, &addr, &mask, &val); if (num != 4) { rtw89_info(rtwdev, "invalid format: \n"); @@ -363,29 +363,31 @@ static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp, return count; } -static int rtw89_debug_priv_rf_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_rf_reg_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 addr, offset, data; u8 path; for (path = 0; path < chip->rf_path_num; path++) { - seq_printf(m, "RF path %d:\n\n", path); + p += scnprintf(p, end - p, "RF path %d:\n\n", path); for (addr = 0; addr < 0x100; addr += 4) { - seq_printf(m, "0x%08x: ", addr); + p += scnprintf(p, end - p, "0x%08x: ", addr); for (offset = 0; offset < 4; offset++) { data = rtw89_read_rf(rtwdev, path, addr + offset, RFREG_MASK); - seq_printf(m, "0x%05x ", data); + p += scnprintf(p, end - p, "0x%05x ", data); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - return 0; + return p - buf; } struct txpwr_ent { @@ -704,56 +706,71 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = { }; static unsigned int -__print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent, - const s8 *buf, const unsigned int cur) +__print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, + const s8 *bufp, const unsigned int cur, unsigned int *ate) { + char *p = buf, *end = buf + bufsz; unsigned int cnt, i; + unsigned int eaten; char *fmt; if (ent->nested) { - for (cnt = 0, i = 0; i < ent->len; i++) - cnt += __print_txpwr_ent(m, ent->ptr + i, buf, - cur + cnt); - return cnt; + for (cnt = 0, i = 0; i < ent->len; i++, cnt += eaten) + p += __print_txpwr_ent(p, end - p, ent->ptr + i, bufp, + cur + cnt, &eaten); + *ate = cnt; + goto out; } switch (ent->len) { case 0: - seq_printf(m, "\t<< %s >>\n", ent->txt); - return 0; + p += scnprintf(p, end - p, "\t<< %s >>\n", ent->txt); + *ate = 0; + goto out; case 2: fmt = "%s\t| %3d, %3d,\t\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1]); - return 2; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1]); + *ate = 2; + goto out; case 4: fmt = "%s\t| %3d, %3d, %3d, %3d,\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], - buf[cur + 2], buf[cur + 3]); - return 4; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1], + bufp[cur + 2], bufp[cur + 3]); + *ate = 4; + goto out; case 8: fmt = "%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], - buf[cur + 2], buf[cur + 3], buf[cur + 4], - buf[cur + 5], buf[cur + 6], buf[cur + 7]); - return 8; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1], + bufp[cur + 2], bufp[cur + 3], bufp[cur + 4], + bufp[cur + 5], bufp[cur + 6], bufp[cur + 7]); + *ate = 8; + goto out; default: return 0; } + +out: + return p - buf; } -static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct txpwr_map *map) +static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct txpwr_map *map) { u8 fct = rtwdev->chip->txpwr_factor_mac; u8 path_num = rtwdev->chip->rf_path_num; + char *p = buf, *end = buf + bufsz; unsigned int cur, i; + unsigned int eaten; u32 max_valid_addr; u32 val, addr; - s8 *buf, tmp; + s8 *bufp, tmp; int ret; - buf = vzalloc(map->addr_to - map->addr_from + 4); - if (!buf) + bufp = vzalloc(map->addr_to - map->addr_from + 4); + if (!bufp) return -ENOMEM; if (path_num == 1) @@ -773,31 +790,32 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, for (i = 0; i < 4; i++, val >>= 8) { /* signed 7 bits, and reserved BIT(7) */ tmp = sign_extend32(val, 6); - buf[cur + i] = tmp >> fct; + bufp[cur + i] = tmp >> fct; } } - for (cur = 0, i = 0; i < map->size; i++) - cur += __print_txpwr_ent(m, &map->ent[i], buf, cur); + for (cur = 0, i = 0; i < map->size; i++, cur += eaten) + p += __print_txpwr_ent(p, end - p, &map->ent[i], bufp, cur, &eaten); - vfree(buf); - return 0; + vfree(bufp); + return p - buf; } #define case_REGD(_regd) \ case RTW89_ ## _regd: \ - seq_puts(m, #_regd "\n"); \ + p += scnprintf(p, end - p, #_regd "\n"); \ break -static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan) { + char *p = buf, *end = buf + bufsz; u8 band = chan->band_type; u8 regd = rtw89_regd_get(rtwdev, band); switch (regd) { default: - seq_printf(m, "UNKNOWN: %d\n", regd); + p += scnprintf(p, end - p, "UNKNOWN: %d\n", regd); break; case_REGD(WW); case_REGD(ETSI); @@ -816,6 +834,8 @@ static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev, case_REGD(UK); case_REGD(THAILAND); } + + return p - buf; } #undef case_REGD @@ -844,96 +864,101 @@ static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] = }; static -void rtw89_debug_priv_txpwr_table_get_regd(struct seq_file *m, - struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +int rtw89_debug_priv_txpwr_table_get_regd(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + const struct rtw89_chan *chan) { const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; const struct rtw89_reg_6ghz_tpe *tpe6 = ®ulatory->reg_6ghz_tpe; + char *p = buf, *end = buf + bufsz; - seq_printf(m, "[Chanctx] band %u, ch %u, bw %u\n", - chan->band_type, chan->channel, chan->band_width); + p += scnprintf(p, end - p, "[Chanctx] band %u, ch %u, bw %u\n", + chan->band_type, chan->channel, chan->band_width); - seq_puts(m, "[Regulatory] "); - __print_regd(m, rtwdev, chan); + p += scnprintf(p, end - p, "[Regulatory] "); + p += __print_regd(rtwdev, p, end - p, chan); if (chan->band_type == RTW89_BAND_6G) { - seq_printf(m, "[reg6_pwr_type] %u\n", regulatory->reg_6ghz_power); + p += scnprintf(p, end - p, "[reg6_pwr_type] %u\n", + regulatory->reg_6ghz_power); if (tpe6->valid) - seq_printf(m, "[TPE] %d dBm\n", tpe6->constraint); + p += scnprintf(p, end - p, "[TPE] %d dBm\n", + tpe6->constraint); } + + return p - buf; } -static int rtw89_debug_priv_txpwr_table_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; - int ret = 0; + char *p = buf, *end = buf + bufsz; + ssize_t n; mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); - rtw89_debug_priv_txpwr_table_get_regd(m, rtwdev, chan); + p += rtw89_debug_priv_txpwr_table_get_regd(rtwdev, p, end - p, chan); - seq_puts(m, "[SAR]\n"); - rtw89_print_sar(m, rtwdev, chan->freq); + p += scnprintf(p, end - p, "[SAR]\n"); + p += rtw89_print_sar(rtwdev, p, end - p, chan->freq); - seq_puts(m, "[TAS]\n"); - rtw89_print_tas(m, rtwdev); + p += scnprintf(p, end - p, "[TAS]\n"); + p += rtw89_print_tas(rtwdev, p, end - p); - seq_puts(m, "[DAG]\n"); - rtw89_print_ant_gain(m, rtwdev, chan); + p += scnprintf(p, end - p, "[DAG]\n"); + p += rtw89_print_ant_gain(rtwdev, p, end - p, chan); tbl = dbgfs_txpwr_tables[chip_gen]; if (!tbl) { - ret = -EOPNOTSUPP; + n = -EOPNOTSUPP; goto err; } - seq_puts(m, "\n[TX power byrate]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->byr); - if (ret) + p += scnprintf(p, end - p, "\n[TX power byrate]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->byr); + if (n < 0) goto err; + p += n; - seq_puts(m, "\n[TX power limit]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->lmt); - if (ret) + p += scnprintf(p, end - p, "\n[TX power limit]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt); + if (n < 0) goto err; + p += n; - seq_puts(m, "\n[TX power limit_ru]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->lmt_ru); - if (ret) + p += scnprintf(p, end - p, "\n[TX power limit_ru]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru); + if (n < 0) goto err; + p += n; + + mutex_unlock(&rtwdev->mutex); + + return p - buf; err: mutex_unlock(&rtwdev->mutex); - return ret; + + return n; } static ssize_t -rtw89_debug_priv_mac_reg_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_reg_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_chip_info *chip = rtwdev->chip; - char buf[32]; - size_t buf_size; int sel; int ret; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; ret = kstrtoint(buf, 0, &sel); if (ret) return ret; @@ -957,99 +982,91 @@ rtw89_debug_priv_mac_reg_dump_select(struct file *filp, #define RTW89_MAC_PAGE_SIZE 0x100 -static int rtw89_debug_priv_mac_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_mac_reg_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data; - u32 start, end; + char *p = buf, *end = buf + bufsz; + u32 start, end_addr; u32 i, j, k, page; u32 val; switch (reg_sel) { case RTW89_DBG_SEL_MAC_00: - seq_puts(m, "Debug selected MAC page 0x00\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x00\n"); start = 0x000; - end = 0x014; + end_addr = 0x014; break; case RTW89_DBG_SEL_MAC_30: - seq_puts(m, "Debug selected MAC page 0x30\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x30\n"); start = 0x030; - end = 0x033; + end_addr = 0x033; break; case RTW89_DBG_SEL_MAC_40: - seq_puts(m, "Debug selected MAC page 0x40\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x40\n"); start = 0x040; - end = 0x07f; + end_addr = 0x07f; break; case RTW89_DBG_SEL_MAC_80: - seq_puts(m, "Debug selected MAC page 0x80\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x80\n"); start = 0x080; - end = 0x09f; + end_addr = 0x09f; break; case RTW89_DBG_SEL_MAC_C0: - seq_puts(m, "Debug selected MAC page 0xc0\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0xc0\n"); start = 0x0c0; - end = 0x0df; + end_addr = 0x0df; break; case RTW89_DBG_SEL_MAC_E0: - seq_puts(m, "Debug selected MAC page 0xe0\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0xe0\n"); start = 0x0e0; - end = 0x0ff; + end_addr = 0x0ff; break; case RTW89_DBG_SEL_BB: - seq_puts(m, "Debug selected BB register\n"); + p += scnprintf(p, end - p, "Debug selected BB register\n"); start = 0x100; - end = 0x17f; + end_addr = 0x17f; break; case RTW89_DBG_SEL_IQK: - seq_puts(m, "Debug selected IQK register\n"); + p += scnprintf(p, end - p, "Debug selected IQK register\n"); start = 0x180; - end = 0x1bf; + end_addr = 0x1bf; break; case RTW89_DBG_SEL_RFC: - seq_puts(m, "Debug selected RFC register\n"); + p += scnprintf(p, end - p, "Debug selected RFC register\n"); start = 0x1c0; - end = 0x1ff; + end_addr = 0x1ff; break; default: - seq_puts(m, "Selected invalid register page\n"); + p += scnprintf(p, end - p, "Selected invalid register page\n"); return -EINVAL; } - for (i = start; i <= end; i++) { + for (i = start; i <= end_addr; i++) { page = i << 8; for (j = page; j < page + RTW89_MAC_PAGE_SIZE; j += 16) { - seq_printf(m, "%08xh : ", 0x18600000 + j); + p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + j); for (k = 0; k < 4; k++) { val = rtw89_read32(rtwdev, j + (k << 2)); - seq_printf(m, "%08x ", val); + p += scnprintf(p, end - p, "%08x ", val); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } } - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_mac_mem_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_mem_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 sel, start_addr, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x %x", &sel, &start_addr, &len); if (num != 3) { rtw89_info(rtwdev, "invalid format: \n"); @@ -1066,15 +1083,16 @@ rtw89_debug_priv_mac_mem_dump_select(struct file *filp, return count; } -static void rtw89_debug_dump_mac_mem(struct seq_file *m, - struct rtw89_dev *rtwdev, - u8 sel, u32 start_addr, u32 len) +static int rtw89_debug_dump_mac_mem(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + u8 sel, u32 start_addr, u32 len) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 filter_model_addr = mac->filter_model_addr; u32 indir_access_addr = mac->indir_access_addr; u32 base_addr, start_page, residue; - u32 i, j, p, pages; + char *p = buf, *end = buf + bufsz; + u32 i, j, pp, pages; u32 dump_len, remain; u32 val; @@ -1085,30 +1103,33 @@ static void rtw89_debug_dump_mac_mem(struct seq_file *m, base_addr = mac->mem_base_addrs[sel]; base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE; - for (p = 0; p < pages; p++) { + for (pp = 0; pp < pages; pp++) { dump_len = min_t(u32, remain, MAC_MEM_DUMP_PAGE_SIZE); rtw89_write32(rtwdev, filter_model_addr, base_addr); for (i = indir_access_addr + residue; i < indir_access_addr + dump_len;) { - seq_printf(m, "%08xh:", i); + p += scnprintf(p, end - p, "%08xh:", i); for (j = 0; j < 4 && i < indir_access_addr + dump_len; j++, i += 4) { val = rtw89_read32(rtwdev, i); - seq_printf(m, " %08x", val); + p += scnprintf(p, end - p, " %08x", val); remain -= 4; } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } base_addr += MAC_MEM_DUMP_PAGE_SIZE; } + + return p - buf; } -static int -rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; bool grant_read = false; if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM) @@ -1131,36 +1152,26 @@ rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) rtw89_leave_ps_mode(rtwdev); if (grant_read) rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); - rtw89_debug_dump_mac_mem(m, rtwdev, - debugfs_priv->mac_mem.sel, - debugfs_priv->mac_mem.start, - debugfs_priv->mac_mem.len); + p += rtw89_debug_dump_mac_mem(rtwdev, p, end - p, + debugfs_priv->mac_mem.sel, + debugfs_priv->mac_mem.start, + debugfs_priv->mac_mem.len); if (grant_read) rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); mutex_unlock(&rtwdev->mutex); - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_dbg_port_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; int sel, set; int num; bool enable; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%d %d", &sel, &set); if (num != 2) { rtw89_info(rtwdev, "invalid format: \n"); @@ -1196,13 +1207,13 @@ rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp, } static int rtw89_debug_mac_dump_ss_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { return 0; } static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { #define DLE_DFI_DUMP(__type, __target, __sel) \ ({ \ @@ -1231,7 +1242,7 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, __data; \ }) -#define DLE_DFI_FREE_PAGE_DUMP(__m, __type) \ +#define DLE_DFI_FREE_PAGE_DUMP(__p, __end, __type) \ ({ \ u32 __freepg, __pubpg; \ u32 __freepg_head, __freepg_tail, __pubpg_num; \ @@ -1241,24 +1252,25 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, __freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg); \ __freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg); \ __pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg); \ - seq_printf(__m, "[%s] freepg head: %d\n", \ - #__type, __freepg_head); \ - seq_printf(__m, "[%s] freepg tail: %d\n", \ - #__type, __freepg_tail); \ - seq_printf(__m, "[%s] pubpg num : %d\n", \ - #__type, __pubpg_num); \ + __p += scnprintf(__p, __end - __p, "[%s] freepg head: %d\n", \ + #__type, __freepg_head); \ + __p += scnprintf(__p, __end - __p, "[%s] freepg tail: %d\n", \ + #__type, __freepg_tail); \ + __p += scnprintf(__p, __end - __p, "[%s] pubpg num : %d\n", \ + #__type, __pubpg_num); \ }) -#define case_QUOTA(__m, __type, __id) \ +#define case_QUOTA(__p, __end, __type, __id) \ case __type##_QTAID_##__id: \ - val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \ + val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \ rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32); \ use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32); \ - seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n", \ - #__type, #__id, rsv_pgnum); \ - seq_printf(__m, "[%s][%s] use_pgnum: %d\n", \ - #__type, #__id, use_pgnum); \ + __p += scnprintf(__p, __end - __p, "[%s][%s] rsv_pgnum: %d\n", \ + #__type, #__id, rsv_pgnum); \ + __p += scnprintf(__p, __end - __p, "[%s][%s] use_pgnum: %d\n", \ + #__type, #__id, use_pgnum); \ break + char *p = buf, *end = buf + bufsz; u32 quota_id; u32 val32; u16 rsv_pgnum, use_pgnum; @@ -1266,38 +1278,39 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); if (ret) { - seq_puts(m, "[DLE] : DMAC not enabled\n"); - return ret; + p += scnprintf(p, end - p, "[DLE] : DMAC not enabled\n"); + goto out; } - DLE_DFI_FREE_PAGE_DUMP(m, WDE); - DLE_DFI_FREE_PAGE_DUMP(m, PLE); + DLE_DFI_FREE_PAGE_DUMP(p, end, WDE); + DLE_DFI_FREE_PAGE_DUMP(p, end, PLE); for (quota_id = 0; quota_id <= WDE_QTAID_CPUIO; quota_id++) { switch (quota_id) { - case_QUOTA(m, WDE, HOST_IF); - case_QUOTA(m, WDE, WLAN_CPU); - case_QUOTA(m, WDE, DATA_CPU); - case_QUOTA(m, WDE, PKTIN); - case_QUOTA(m, WDE, CPUIO); + case_QUOTA(p, end, WDE, HOST_IF); + case_QUOTA(p, end, WDE, WLAN_CPU); + case_QUOTA(p, end, WDE, DATA_CPU); + case_QUOTA(p, end, WDE, PKTIN); + case_QUOTA(p, end, WDE, CPUIO); } } for (quota_id = 0; quota_id <= PLE_QTAID_CPUIO; quota_id++) { switch (quota_id) { - case_QUOTA(m, PLE, B0_TXPL); - case_QUOTA(m, PLE, B1_TXPL); - case_QUOTA(m, PLE, C2H); - case_QUOTA(m, PLE, H2C); - case_QUOTA(m, PLE, WLAN_CPU); - case_QUOTA(m, PLE, MPDU); - case_QUOTA(m, PLE, CMAC0_RX); - case_QUOTA(m, PLE, CMAC1_RX); - case_QUOTA(m, PLE, CMAC1_BBRPT); - case_QUOTA(m, PLE, WDRLS); - case_QUOTA(m, PLE, CPUIO); + case_QUOTA(p, end, PLE, B0_TXPL); + case_QUOTA(p, end, PLE, B1_TXPL); + case_QUOTA(p, end, PLE, C2H); + case_QUOTA(p, end, PLE, H2C); + case_QUOTA(p, end, PLE, WLAN_CPU); + case_QUOTA(p, end, PLE, MPDU); + case_QUOTA(p, end, PLE, CMAC0_RX); + case_QUOTA(p, end, PLE, CMAC1_RX); + case_QUOTA(p, end, PLE, CMAC1_BBRPT); + case_QUOTA(p, end, PLE, WDRLS); + case_QUOTA(p, end, PLE, CPUIO); } } - return 0; +out: + return p - buf; #undef case_QUOTA #undef DLE_DFI_DUMP @@ -1305,73 +1318,88 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, } static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 dmac_err; int i, ret; ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); if (ret) { - seq_puts(m, "[DMAC] : DMAC not enabled\n"); - return ret; + p += scnprintf(p, end - p, "[DMAC] : DMAC not enabled\n"); + goto out; } dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); - seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); - seq_printf(m, "R_AX_DMAC_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); + p += scnprintf(p, end - p, "R_AX_DMAC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); if (dmac_err) { - seq_printf(m, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); - seq_printf(m, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); - seq_printf(m, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); - seq_printf(m, "R_AX_PLE_DBGERR_STS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); + p += scnprintf(p, end - p, + "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); + p += scnprintf(p, end - p, + "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); + p += scnprintf(p, end - p, + "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); + p += scnprintf(p, end - p, + "R_AX_PLE_DBGERR_STS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); } } if (dmac_err & B_AX_WDRLS_ERR_FLAG) { - seq_printf(m, "R_AX_WDRLS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); - seq_printf(m, "R_AX_WDRLS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); if (chip->chip_id == RTL8852C) - seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); + p += scnprintf(p, end - p, + "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); else - seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); + p += scnprintf(p, end - p, + "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); } if (dmac_err & B_AX_WSEC_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_SEC_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); - seq_printf(m, "R_AX_SEC_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); - seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); - seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); - seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); - seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); - seq_printf(m, "R_AX_SEC_DEBUG1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); - seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); - seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + p += scnprintf(p, end - p, + "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + p += scnprintf(p, end - p, "R_AX_SEC_DEBUG1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); + p += scnprintf(p, end - p, + "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, B_AX_DBG_SEL0, 0x8B); @@ -1382,187 +1410,229 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, for (i = 0; i < 0x10; i++) { rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL, B_AX_SEC_DBG_PORT_FIELD_MASK, i); - seq_printf(m, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", - i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); + p += scnprintf(p, end - p, + "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", + i, + rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); } } else { - seq_printf(m, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); - seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); - seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); - seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); - seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); - seq_printf(m, "R_AX_SEC_CAM_WDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); - seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); - seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); - seq_printf(m, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); - seq_printf(m, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + p += scnprintf(p, end - p, + "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_WDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); + p += scnprintf(p, end - p, + "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); + p += scnprintf(p, end - p, + "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); } } if (dmac_err & B_AX_MPDU_ERR_FLAG) { - seq_printf(m, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); - seq_printf(m, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); - seq_printf(m, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); - seq_printf(m, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); } if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) { - seq_printf(m, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); - seq_printf(m, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); } if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) { - seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); - seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); } if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); - seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); - seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); - seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); } else { - seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); - seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); } } if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) { - seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); - seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); - seq_printf(m, "R_AX_WD_CPUQ_OP_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); - seq_printf(m, "R_AX_WD_CPUQ_OP_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); - seq_printf(m, "R_AX_WD_CPUQ_OP_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); - seq_printf(m, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); - seq_printf(m, "R_AX_PL_CPUQ_OP_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); - seq_printf(m, "R_AX_PL_CPUQ_OP_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); - seq_printf(m, "R_AX_PL_CPUQ_OP_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); - seq_printf(m, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_RX_CTRL0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL0)); - seq_printf(m, "R_AX_RX_CTRL1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL1)); - seq_printf(m, "R_AX_RX_CTRL2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL2)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL0)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL1)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL2)); } else { - seq_printf(m, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); - seq_printf(m, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); - seq_printf(m, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); } } if (dmac_err & B_AX_PKTIN_ERR_FLAG) { - seq_printf(m, "R_AX_PKTIN_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); - seq_printf(m, "R_AX_PKTIN_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); } if (dmac_err & B_AX_DISPATCH_ERR_FLAG) { - seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); - seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); - seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); } if (dmac_err & B_AX_BBRPT_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); } else { - seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); } } if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); - seq_printf(m, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); + p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); + p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); } - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, - struct seq_file *m, + char *buf, size_t bufsz, enum rtw89_mac_idx band) { const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 offset = 0; u32 cmac_err; int ret; @@ -1570,96 +1640,127 @@ static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL); if (ret) { if (band) - seq_puts(m, "[CMAC] : CMAC1 not enabled\n"); + p += scnprintf(p, end - p, + "[CMAC] : CMAC1 not enabled\n"); else - seq_puts(m, "[CMAC] : CMAC0 not enabled\n"); - return ret; + p += scnprintf(p, end - p, + "[CMAC] : CMAC0 not enabled\n"); + goto out; } if (band) offset = RTW89_MAC_AX_BAND_REG_OFFSET; cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset); - seq_printf(m, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); - seq_printf(m, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); - seq_printf(m, "R_AX_CK_EN [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CK_EN + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); + p += scnprintf(p, end - p, "R_AX_CK_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CK_EN + offset)); if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) { - seq_printf(m, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); - seq_printf(m, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); } if (cmac_err & B_AX_PTCL_TOP_ERR_IND) { - seq_printf(m, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); - seq_printf(m, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); + p += scnprintf(p, end - p, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); + p += scnprintf(p, end - p, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); } if (cmac_err & B_AX_DMA_TOP_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); - seq_printf(m, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); + p += scnprintf(p, end - p, + "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); } else { - seq_printf(m, "R_AX_DLE_CTRL [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); + p += scnprintf(p, end - p, + "R_AX_DLE_CTRL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); } } if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); - seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); } else { - seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); } } if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) { - seq_printf(m, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); - seq_printf(m, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); + p += scnprintf(p, end - p, "R_AX_TXPWR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); + p += scnprintf(p, end - p, "R_AX_TXPWR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); } if (cmac_err & B_AX_WMAC_TX_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset)); - seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); + p += scnprintf(p, end - p, + "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TRXPTCL_ERROR_INDICA + offset)); + p += scnprintf(p, end - p, + "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); } else { - seq_printf(m, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TMAC_ERR_IMR_ISR + offset)); } - seq_printf(m, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); + p += scnprintf(p, end - p, + "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); } - seq_printf(m, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { - rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_0); - if (rtwdev->dbcc_en) - rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_1); + char *p = buf, *end = buf + bufsz; - return 0; + p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_0); + if (rtwdev->dbcc_en) + p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_1); + + return p - buf; } static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = { @@ -2465,11 +2566,12 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = { .rd_msk = B_AX_DEBUG_ST_MASK }; -static const struct rtw89_mac_dbg_port_info * -rtw89_debug_mac_dbg_port_sel(struct seq_file *m, - struct rtw89_dev *rtwdev, u32 sel) +static int +rtw89_debug_mac_dbg_port_sel(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + u32 sel, const struct rtw89_mac_dbg_port_info **ppinfo) { - const struct rtw89_mac_dbg_port_info *info; + const struct rtw89_mac_dbg_port_info *info = NULL; + char *p = buf, *end = buf + bufsz; u32 index; u32 val32; u16 val16; @@ -2481,28 +2583,28 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG); val16 |= B_AX_PTCL_DBG_EN; rtw89_write16(rtwdev, R_AX_PTCL_DBG, val16); - seq_puts(m, "Enable PTCL C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable PTCL C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_PTCL_C1: info = &dbg_port_ptcl_c1; val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG_C1); val16 |= B_AX_PTCL_DBG_EN; rtw89_write16(rtwdev, R_AX_PTCL_DBG_C1, val16); - seq_puts(m, "Enable PTCL C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable PTCL C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_SCH_C0: info = &dbg_port_sch_c0; val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL); val32 |= B_AX_SCH_DBG_EN; rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL, val32); - seq_puts(m, "Enable SCH C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable SCH C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_SCH_C1: info = &dbg_port_sch_c1; val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL_C1); val32 |= B_AX_SCH_DBG_EN; rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL_C1, val32); - seq_puts(m, "Enable SCH C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable SCH C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TMAC_C0: info = &dbg_port_tmac_c0; @@ -2519,7 +2621,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TMAC C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TMAC C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TMAC_C1: info = &dbg_port_tmac_c1; @@ -2536,7 +2638,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TMAC C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TMAC C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_C0: info = &dbg_port_rmac_c0; @@ -2558,7 +2660,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL, B_AX_DBGSEL_TRXPTCL_MASK); rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL, val8); - seq_puts(m, "Enable RMAC C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_C1: info = &dbg_port_rmac_c1; @@ -2580,23 +2682,23 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL, B_AX_DBGSEL_TRXPTCL_MASK); rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val8); - seq_puts(m, "Enable RMAC C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMACST_C0: info = &dbg_port_rmacst_c0; - seq_puts(m, "Enable RMAC state C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC state C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMACST_C1: info = &dbg_port_rmacst_c1; - seq_puts(m, "Enable RMAC state C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC state C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0: info = &dbg_port_rmac_plcp_c0; - seq_puts(m, "Enable RMAC PLCP C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC PLCP C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1: info = &dbg_port_rmac_plcp_c1; - seq_puts(m, "Enable RMAC PLCP C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC PLCP C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TRXPTCL_C0: info = &dbg_port_trxptcl_c0; @@ -2608,7 +2710,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TRXPTCL C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TRXPTCL C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TRXPTCL_C1: info = &dbg_port_trxptcl_c1; @@ -2620,131 +2722,137 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TRXPTCL C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TRXPTCL C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOL_C0: info = &dbg_port_tx_infol_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOH_C0: info = &dbg_port_tx_infoh_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOL_C1: info = &dbg_port_tx_infol_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOH_C1: info = &dbg_port_tx_infoh_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0: info = &dbg_port_txtf_infol_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx tf infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0: info = &dbg_port_txtf_infoh_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx tf infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1: info = &dbg_port_txtf_infol_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx tf infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1: info = &dbg_port_txtf_infoh_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx tf infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG: info = &dbg_port_wde_bufmgn_freepg; - seq_puts(m, "Enable wde bufmgn freepg dump.\n"); + p += scnprintf(p, end - p, "Enable wde bufmgn freepg dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA: info = &dbg_port_wde_bufmgn_quota; - seq_puts(m, "Enable wde bufmgn quota dump.\n"); + p += scnprintf(p, end - p, "Enable wde bufmgn quota dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT: info = &dbg_port_wde_bufmgn_pagellt; - seq_puts(m, "Enable wde bufmgn pagellt dump.\n"); + p += scnprintf(p, end - p, + "Enable wde bufmgn pagellt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO: info = &dbg_port_wde_bufmgn_pktinfo; - seq_puts(m, "Enable wde bufmgn pktinfo dump.\n"); + p += scnprintf(p, end - p, + "Enable wde bufmgn pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT: info = &dbg_port_wde_quemgn_prepkt; - seq_puts(m, "Enable wde quemgn prepkt dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn prepkt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT: info = &dbg_port_wde_quemgn_nxtpkt; - seq_puts(m, "Enable wde quemgn nxtpkt dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn nxtpkt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL: info = &dbg_port_wde_quemgn_qlnktbl; - seq_puts(m, "Enable wde quemgn qlnktbl dump.\n"); + p += scnprintf(p, end - p, + "Enable wde quemgn qlnktbl dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY: info = &dbg_port_wde_quemgn_qempty; - seq_puts(m, "Enable wde quemgn qempty dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn qempty dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG: info = &dbg_port_ple_bufmgn_freepg; - seq_puts(m, "Enable ple bufmgn freepg dump.\n"); + p += scnprintf(p, end - p, "Enable ple bufmgn freepg dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA: info = &dbg_port_ple_bufmgn_quota; - seq_puts(m, "Enable ple bufmgn quota dump.\n"); + p += scnprintf(p, end - p, "Enable ple bufmgn quota dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT: info = &dbg_port_ple_bufmgn_pagellt; - seq_puts(m, "Enable ple bufmgn pagellt dump.\n"); + p += scnprintf(p, end - p, + "Enable ple bufmgn pagellt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO: info = &dbg_port_ple_bufmgn_pktinfo; - seq_puts(m, "Enable ple bufmgn pktinfo dump.\n"); + p += scnprintf(p, end - p, + "Enable ple bufmgn pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT: info = &dbg_port_ple_quemgn_prepkt; - seq_puts(m, "Enable ple quemgn prepkt dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn prepkt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT: info = &dbg_port_ple_quemgn_nxtpkt; - seq_puts(m, "Enable ple quemgn nxtpkt dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn nxtpkt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL: info = &dbg_port_ple_quemgn_qlnktbl; - seq_puts(m, "Enable ple quemgn qlnktbl dump.\n"); + p += scnprintf(p, end - p, + "Enable ple quemgn qlnktbl dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY: info = &dbg_port_ple_quemgn_qempty; - seq_puts(m, "Enable ple quemgn qempty dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn qempty dump.\n"); break; case RTW89_DBG_PORT_SEL_PKTINFO: info = &dbg_port_pktinfo; - seq_puts(m, "Enable pktinfo dump.\n"); + p += scnprintf(p, end - p, "Enable pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX0: rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, @@ -2763,7 +2871,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX6: info = &dbg_port_dspt_hdt_tx6; @@ -2771,7 +2880,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 6); - seq_puts(m, "Enable Dispatcher hdt tx6 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx6 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX7: info = &dbg_port_dspt_hdt_tx7; @@ -2779,7 +2889,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 7); - seq_puts(m, "Enable Dispatcher hdt tx7 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx7 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX8: info = &dbg_port_dspt_hdt_tx8; @@ -2787,7 +2898,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 8); - seq_puts(m, "Enable Dispatcher hdt tx8 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx8 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX9: case RTW89_DBG_PORT_SEL_DSPT_HDT_TXA: @@ -2799,7 +2911,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TXD: info = &dbg_port_dspt_hdt_txD; @@ -2807,7 +2920,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0xD); - seq_puts(m, "Enable Dispatcher hdt txD dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt txD dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX0: info = &dbg_port_dspt_cdt_tx0; @@ -2815,7 +2929,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher cdt tx0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX1: info = &dbg_port_dspt_cdt_tx1; @@ -2823,7 +2938,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 1); - seq_puts(m, "Enable Dispatcher cdt tx1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX3: info = &dbg_port_dspt_cdt_tx3; @@ -2831,7 +2947,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher cdt tx3 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx3 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX4: info = &dbg_port_dspt_cdt_tx4; @@ -2839,7 +2956,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher cdt tx4 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx4 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX5: case RTW89_DBG_PORT_SEL_DSPT_CDT_TX6: @@ -2851,7 +2969,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX9: info = &dbg_port_dspt_cdt_tx9; @@ -2859,7 +2978,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 9); - seq_puts(m, "Enable Dispatcher cdt tx9 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx9 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TXA: case RTW89_DBG_PORT_SEL_DSPT_CDT_TXB: @@ -2870,7 +2990,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX0: info = &dbg_port_dspt_hdt_rx0; @@ -2878,7 +2999,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher hdt rx0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX1: case RTW89_DBG_PORT_SEL_DSPT_HDT_RX2: @@ -2888,7 +3010,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt rx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX3: info = &dbg_port_dspt_hdt_rx3; @@ -2896,7 +3019,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher hdt rx3 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx3 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX4: info = &dbg_port_dspt_hdt_rx4; @@ -2904,7 +3028,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher hdt rx4 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx4 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX5: info = &dbg_port_dspt_hdt_rx5; @@ -2912,7 +3037,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 5); - seq_puts(m, "Enable Dispatcher hdt rx5 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx5 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0: info = &dbg_port_dspt_cdt_rx_p0_0; @@ -2920,7 +3046,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher cdt rx part0 0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0: case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1: @@ -2929,7 +3056,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 1); - seq_puts(m, "Enable Dispatcher cdt rx part0 1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2: info = &dbg_port_dspt_cdt_rx_p0_2; @@ -2937,43 +3065,50 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 2); - seq_puts(m, "Enable Dispatcher cdt rx part0 2 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 2 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1: info = &dbg_port_dspt_cdt_rx_p1; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher cdt rx part1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_STF_CTRL: info = &dbg_port_dspt_stf_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher stf control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher stf control dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL: info = &dbg_port_dspt_addr_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 5); - seq_puts(m, "Enable Dispatcher addr control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher addr control dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_WDE_INTF: info = &dbg_port_dspt_wde_intf; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 6); - seq_puts(m, "Enable Dispatcher wde interface dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher wde interface dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_PLE_INTF: info = &dbg_port_dspt_ple_intf; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 7); - seq_puts(m, "Enable Dispatcher ple interface dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher ple interface dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL: info = &dbg_port_dspt_flow_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 8); - seq_puts(m, "Enable Dispatcher flow control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher flow control dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_TXDMA: info = &dbg_port_pcie_txdma; @@ -2981,7 +3116,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie txdma dump.\n"); + p += scnprintf(p, end - p, "Enable pcie txdma dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_RXDMA: info = &dbg_port_pcie_rxdma; @@ -2989,7 +3124,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie rxdma dump.\n"); + p += scnprintf(p, end - p, "Enable pcie rxdma dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_CVT: info = &dbg_port_pcie_cvt; @@ -2997,7 +3132,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie cvt dump.\n"); + p += scnprintf(p, end - p, "Enable pcie cvt dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_CXPL: info = &dbg_port_pcie_cxpl; @@ -3005,7 +3140,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie cxpl dump.\n"); + p += scnprintf(p, end - p, "Enable pcie cxpl dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_IO: info = &dbg_port_pcie_io; @@ -3013,7 +3148,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie io dump.\n"); + p += scnprintf(p, end - p, "Enable pcie io dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_MISC: info = &dbg_port_pcie_misc; @@ -3021,7 +3156,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie misc dump.\n"); + p += scnprintf(p, end - p, "Enable pcie misc dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_MISC2: info = &dbg_port_pcie_misc2; @@ -3029,14 +3164,16 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL, B_AX_PCIE_DBG_SEL_MASK); rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16); - seq_puts(m, "Enable pcie misc2 dump.\n"); + p += scnprintf(p, end - p, "Enable pcie misc2 dump.\n"); break; default: - seq_puts(m, "Dbg port select err\n"); - return NULL; + p += scnprintf(p, end - p, "Dbg port select err\n"); + break; } - return info; + *ppinfo = info; + + return p - buf; } static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) @@ -3070,23 +3207,25 @@ static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) } static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, - struct seq_file *m, u32 sel) + char *buf, size_t bufsz, u32 sel) { - const struct rtw89_mac_dbg_port_info *info; - u8 val8; - u16 val16; + const struct rtw89_mac_dbg_port_info *info = NULL; + char *p = buf, *end = buf + bufsz; u32 val32; + u16 val16; + u8 val8; u32 i; - info = rtw89_debug_mac_dbg_port_sel(m, rtwdev, sel); + p += rtw89_debug_mac_dbg_port_sel(rtwdev, p, end - p, sel, &info); + if (!info) { rtw89_err(rtwdev, "failed to select debug port %d\n", sel); - return -EINVAL; + goto out; } #define case_DBG_SEL(__sel) \ case RTW89_DBG_PORT_SEL_##__sel: \ - seq_puts(m, "Dump debug port " #__sel ":\n"); \ + p += scnprintf(p, end - p, "Dump debug port " #__sel ":\n"); \ break switch (sel) { @@ -3182,8 +3321,8 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, #undef case_DBG_SEL - seq_printf(m, "Sel addr = 0x%X\n", info->sel_addr); - seq_printf(m, "Read addr = 0x%X\n", info->rd_addr); + p += scnprintf(p, end - p, "Sel addr = 0x%X\n", info->sel_addr); + p += scnprintf(p, end - p, "Read addr = 0x%X\n", info->rd_addr); for (i = info->srt; i <= info->end; i++) { switch (info->sel_byte) { @@ -3191,17 +3330,17 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, default: rtw89_write8_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%02X: ", i); + p += scnprintf(p, end - p, "0x%02X: ", i); break; case 2: rtw89_write16_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%04X: ", i); + p += scnprintf(p, end - p, "0x%04X: ", i); break; case 4: rtw89_write32_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%04X: ", i); + p += scnprintf(p, end - p, "0x%04X: ", i); break; } @@ -3212,77 +3351,75 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, default: val8 = rtw89_read8_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%02X\n", val8); + p += scnprintf(p, end - p, "0x%02X\n", val8); break; case 2: val16 = rtw89_read16_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%04X\n", val16); + p += scnprintf(p, end - p, "0x%04X\n", val16); break; case 4: val32 = rtw89_read32_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%08X\n", val32); + p += scnprintf(p, end - p, "0x%08X\n", val32); break; } } - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_dbg_port(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { + char *p = buf, *end = buf + bufsz; + ssize_t n; u32 sel; - int ret = 0; for (sel = RTW89_DBG_PORT_SEL_PTCL_C0; sel < RTW89_DBG_PORT_SEL_LAST; sel++) { if (!is_dbg_port_valid(rtwdev, sel)) continue; - ret = rtw89_debug_mac_dbg_port_dump(rtwdev, m, sel); - if (ret) { + n = rtw89_debug_mac_dbg_port_dump(rtwdev, p, end - p, sel); + if (n < 0) { rtw89_err(rtwdev, "failed to dump debug port %d\n", sel); break; } + p += n; } - return ret; + return p - buf; } -static int -rtw89_debug_priv_mac_dbg_port_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_dbg_port_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; if (debugfs_priv->dbgpkg_en.ss_dbg) - rtw89_debug_mac_dump_ss_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_ss_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dle_dbg) - rtw89_debug_mac_dump_dle_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_dle_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dmac_dbg) - rtw89_debug_mac_dump_dmac_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_dmac_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.cmac_dbg) - rtw89_debug_mac_dump_cmac_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_cmac_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dbg_port) - rtw89_debug_mac_dump_dbg_port(rtwdev, m); + p += rtw89_debug_mac_dump_dbg_port(rtwdev, p, end - p); - return 0; + return p - buf; }; -static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev, - const char __user *user_buf, size_t count) +static u8 *rtw89_hex2bin(struct rtw89_dev *rtwdev, const char *buf, size_t count) { - char *buf; u8 *bin; int num; int err = 0; - buf = memdup_user(user_buf, count); - if (IS_ERR(buf)) - return buf; - num = count / 2; bin = kmalloc(num, GFP_KERNEL); if (!bin) { @@ -3297,22 +3434,18 @@ static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev, } out: - kfree(buf); - return err ? ERR_PTR(err) : bin; } -static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_send_h2c_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; u8 *h2c; int ret; u16 h2c_len = count / 2; - h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); + h2c = rtw89_hex2bin(rtwdev, buf, count); if (IS_ERR(h2c)) return -EFAULT; @@ -3323,34 +3456,34 @@ static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, return ret ? ret : count; } -static int -rtw89_debug_priv_early_h2c_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_early_h2c *early_h2c; + char *p = buf, *end = buf + bufsz; int seq = 0; mutex_lock(&rtwdev->mutex); list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) - seq_printf(m, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c); + p += scnprintf(p, end - p, "%d: %*ph\n", ++seq, + early_h2c->h2c_len, early_h2c->h2c); mutex_unlock(&rtwdev->mutex); - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_early_h2c *early_h2c; u8 *h2c; u16 h2c_len = count / 2; - h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); + h2c = rtw89_hex2bin(rtwdev, buf, count); if (IS_ERR(h2c)) return -EFAULT; @@ -3404,15 +3537,16 @@ static int rtw89_dbg_trigger_ctrl_error(struct rtw89_dev *rtwdev) return 0; } -static int -rtw89_debug_priv_fw_crash_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_fw_crash_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; - seq_printf(m, "%d\n", - test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); - return 0; + p += scnprintf(p, end - p, "%d\n", + test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); + return p - buf; } enum rtw89_dbg_crash_simulation_type { @@ -3421,17 +3555,15 @@ enum rtw89_dbg_crash_simulation_type { }; static ssize_t -rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; int (*sim)(struct rtw89_dev *rtwdev); u8 crash_type; int ret; - ret = kstrtou8_from_user(user_buf, count, 0, &crash_type); + ret = kstrtou8(buf, 0, &crash_type); if (ret) return -EINVAL; @@ -3459,27 +3591,22 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, return count; } -static int rtw89_debug_priv_btc_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_btc_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - - rtw89_btc_dump_info(rtwdev, m); - - return 0; + return rtw89_btc_dump_info(rtwdev, buf, bufsz); } -static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; int ret; - ret = kstrtobool_from_user(user_buf, count, &btc->manual_ctrl); + ret = kstrtobool(buf, &btc->manual_ctrl); if (ret) return ret; @@ -3491,16 +3618,14 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp, return count; } -static ssize_t rtw89_debug_priv_fw_log_manual_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_fw_log *log = &rtwdev->fw.log; bool fw_log_manual; - if (kstrtobool_from_user(user_buf, count, &fw_log_manual)) + if (kstrtobool(buf, &fw_log_manual)) goto out; mutex_lock(&rtwdev->mutex); @@ -3513,9 +3638,9 @@ out: return count; } -static void rtw89_sta_link_info_get_iter(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link) { static const char * const he_gi_str[] = { [NL80211_RATE_INFO_HE_GI_0_8] = "0.8", @@ -3533,6 +3658,7 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m, u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num; bool ant_asterisk = hal->tx_path_diversity || hal->ant_diversity; struct ieee80211_link_sta *link_sta; + char *p = buf, *end = buf + bufsz; u8 evm_min, evm_max, evm_1ss; u16 max_rc_amsdu_len; u8 rssi; @@ -3546,107 +3672,136 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m, rcu_read_unlock(); - seq_printf(m, "TX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); + p += scnprintf(p, end - p, "TX rate [%u, %u]: ", rtwsta_link->mac_id, + rtwsta_link->link_id); if (rate->flags & RATE_INFO_FLAGS_MCS) - seq_printf(m, "HT MCS-%d%s", rate->mcs, - rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "HT MCS-%d%s", rate->mcs, + rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) - seq_printf(m, "VHT %dSS MCS-%d%s", rate->nss, rate->mcs, - rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", rate->nss, + rate->mcs, + rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) - seq_printf(m, "HE %dSS MCS-%d GI:%s", rate->nss, rate->mcs, - rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? - he_gi_str[rate->he_gi] : "N/A"); + p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", rate->nss, + rate->mcs, + rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[rate->he_gi] : "N/A"); else if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) - seq_printf(m, "EHT %dSS MCS-%d GI:%s", rate->nss, rate->mcs, - rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? - eht_gi_str[rate->eht_gi] : "N/A"); + p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", rate->nss, + rate->mcs, + rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? + eht_gi_str[rate->eht_gi] : "N/A"); else - seq_printf(m, "Legacy %d", rate->legacy); - seq_printf(m, "%s", rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); - seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(rate->bw)); - seq_printf(m, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); - seq_printf(m, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, - max_rc_amsdu_len); + p += scnprintf(p, end - p, "Legacy %d", rate->legacy); + p += scnprintf(p, end - p, "%s", + rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); + p += scnprintf(p, end - p, " BW:%u", + rtw89_rate_info_bw_to_mhz(rate->bw)); + p += scnprintf(p, end - p, " (hw_rate=0x%x)", + rtwsta_link->ra_report.hw_rate); + p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", + rtwsta_link->max_agg_wait, + max_rc_amsdu_len); - seq_printf(m, "RX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); + p += scnprintf(p, end - p, "RX rate [%u, %u]: ", rtwsta_link->mac_id, + rtwsta_link->link_id); switch (status->encoding) { case RX_ENC_LEGACY: - seq_printf(m, "Legacy %d", status->rate_idx + - (status->band != NL80211_BAND_2GHZ ? 4 : 0)); + p += scnprintf(p, end - p, "Legacy %d", status->rate_idx + + (status->band != NL80211_BAND_2GHZ ? 4 : 0)); break; case RX_ENC_HT: - seq_printf(m, "HT MCS-%d%s", status->rate_idx, - status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "HT MCS-%d%s", status->rate_idx, + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); break; case RX_ENC_VHT: - seq_printf(m, "VHT %dSS MCS-%d%s", status->nss, status->rate_idx, - status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", status->nss, + status->rate_idx, + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); break; case RX_ENC_HE: - seq_printf(m, "HE %dSS MCS-%d GI:%s", status->nss, status->rate_idx, - status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? - he_gi_str[status->he_gi] : "N/A"); + p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", + status->nss, status->rate_idx, + status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[status->he_gi] : "N/A"); break; case RX_ENC_EHT: - seq_printf(m, "EHT %dSS MCS-%d GI:%s", status->nss, status->rate_idx, - status->eht.gi < ARRAY_SIZE(eht_gi_str) ? - eht_gi_str[status->eht.gi] : "N/A"); + p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", + status->nss, status->rate_idx, + status->eht.gi < ARRAY_SIZE(eht_gi_str) ? + eht_gi_str[status->eht.gi] : "N/A"); break; } - seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(status->bw)); - seq_printf(m, " (hw_rate=0x%x)\n", rtwsta_link->rx_hw_rate); + p += scnprintf(p, end - p, " BW:%u", + rtw89_rate_info_bw_to_mhz(status->bw)); + p += scnprintf(p, end - p, " (hw_rate=0x%x)\n", + rtwsta_link->rx_hw_rate); rssi = ewma_rssi_read(&rtwsta_link->avg_rssi); - seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d) [", - RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta_link->prev_rssi); + p += scnprintf(p, end - p, "RSSI: %d dBm (raw=%d, prev=%d) [", + RTW89_RSSI_RAW_TO_DBM(rssi), rssi, + rtwsta_link->prev_rssi); for (i = 0; i < ant_num; i++) { rssi = ewma_rssi_read(&rtwsta_link->rssi[i]); - seq_printf(m, "%d%s%s", RTW89_RSSI_RAW_TO_DBM(rssi), - ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", - i + 1 == ant_num ? "" : ", "); + p += scnprintf(p, end - p, "%d%s%s", + RTW89_RSSI_RAW_TO_DBM(rssi), + ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", + i + 1 == ant_num ? "" : ", "); } - seq_puts(m, "]\n"); + p += scnprintf(p, end - p, "]\n"); evm_1ss = ewma_evm_read(&rtwsta_link->evm_1ss); - seq_printf(m, "EVM: [%2u.%02u, ", evm_1ss >> 2, (evm_1ss & 0x3) * 25); + p += scnprintf(p, end - p, "EVM: [%2u.%02u, ", evm_1ss >> 2, + (evm_1ss & 0x3) * 25); for (i = 0; i < (hal->ant_diversity ? 2 : 1); i++) { evm_min = ewma_evm_read(&rtwsta_link->evm_min[i]); evm_max = ewma_evm_read(&rtwsta_link->evm_max[i]); - seq_printf(m, "%s(%2u.%02u, %2u.%02u)", i == 0 ? "" : " ", - evm_min >> 2, (evm_min & 0x3) * 25, - evm_max >> 2, (evm_max & 0x3) * 25); + p += scnprintf(p, end - p, "%s(%2u.%02u, %2u.%02u)", + i == 0 ? "" : " ", + evm_min >> 2, (evm_min & 0x3) * 25, + evm_max >> 2, (evm_max & 0x3) * 25); } - seq_puts(m, "]\t"); + p += scnprintf(p, end - p, "]\t"); snr = ewma_snr_read(&rtwsta_link->avg_snr); - seq_printf(m, "SNR: %u\n", snr); + p += scnprintf(p, end - p, "SNR: %u\n", snr); + + return p - buf; } static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_dev *rtwdev = rtwsta->rtwdev; struct rtw89_sta_link *rtwsta_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) - rtw89_sta_link_info_get_iter(m, rtwdev, rtwsta_link); + p += rtw89_sta_link_info_get_iter(rtwdev, p, end - p, rtwsta_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static void -rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat, +static int +rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat, enum rtw89_hw_rate first_rate, int len) { + char *p = buf, *end = buf + bufsz; int i; for (i = 0; i < len; i++) - seq_printf(m, "%s%u", i == 0 ? "" : ", ", - pkt_stat->rx_rate_cnt[first_rate + i]); + p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", + pkt_stat->rx_rate_cnt[first_rate + i]); + + return p - buf; } #define FIRST_RATE_SAME(rate) {RTW89_HW_RATE_ ## rate, RTW89_HW_RATE_ ## rate} @@ -3671,34 +3826,40 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; -static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_traffic_stats *stats = &rtwdev->stats; struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_debugfs_iter_data iter_data; const struct rtw89_rx_rate_cnt_info *info; struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; enum rtw89_hw_rate first_rate; u8 rssi; int i; rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); - seq_printf(m, "TP TX: %u [%u] Mbps (lv: %d", - stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv); + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", + stats->tx_throughput, stats->tx_throughput_raw, + stats->tx_tfc_lv); if (hal->thermal_prot_lv) - seq_printf(m, ", duty: %d%%", - 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); - seq_printf(m, "), RX: %u [%u] Mbps (lv: %d)\n", - stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv); - seq_printf(m, "Beacon: %u (%d dBm), TF: %u\n", pkt_stat->beacon_nr, - RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); - seq_printf(m, "Avg packet length: TX=%u, RX=%u\n", stats->tx_avg_len, - stats->rx_avg_len); + p += scnprintf(p, end - p, ", duty: %d%%", + 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); + p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", + stats->rx_throughput, stats->rx_throughput_raw, + stats->rx_tfc_lv); + p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", + pkt_stat->beacon_nr, + RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); - seq_puts(m, "RX count:\n"); + p += scnprintf(p, end - p, "RX count:\n"); for (i = 0; i < ARRAY_SIZE(rtw89_rx_rate_cnt_infos); i++) { info = &rtw89_rx_rate_cnt_infos[i]; @@ -3706,189 +3867,240 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) if (first_rate >= RTW89_HW_RATE_NR) continue; - seq_printf(m, "%10s [", info->rate_mode); - rtw89_debug_append_rx_rate(m, pkt_stat, - first_rate, info->len); + p += scnprintf(p, end - p, "%10s [", info->rate_mode); + p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, + first_rate, info->len); if (info->ext) { - seq_puts(m, "]["); - rtw89_debug_append_rx_rate(m, pkt_stat, - first_rate + info->len, info->ext); + p += scnprintf(p, end - p, "]["); + p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, + first_rate + info->len, info->ext); } - seq_puts(m, "]\n"); + p += scnprintf(p, end - p, "]\n"); } - ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, m); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); + p += iter_data.written_sz; - return 0; + return p - buf; } -static void rtw89_dump_addr_cam(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_addr_cam_entry *addr_cam) +static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_addr_cam_entry *addr_cam) { struct rtw89_cam_info *cam_info = &rtwdev->cam_info; const struct rtw89_sec_cam_entry *sec_entry; + char *p = buf, *end = buf + bufsz; u8 sec_cam_idx; int i; - seq_printf(m, "\taddr_cam_idx=%u\n", addr_cam->addr_cam_idx); - seq_printf(m, "\t-> bssid_cam_idx=%u\n", addr_cam->bssid_cam_idx); - seq_printf(m, "\tsec_cam_bitmap=%*ph\n", (int)sizeof(addr_cam->sec_cam_map), - addr_cam->sec_cam_map); + p += scnprintf(p, end - p, "\taddr_cam_idx=%u\n", + addr_cam->addr_cam_idx); + p += scnprintf(p, end - p, "\t-> bssid_cam_idx=%u\n", + addr_cam->bssid_cam_idx); + p += scnprintf(p, end - p, "\tsec_cam_bitmap=%*ph\n", + (int)sizeof(addr_cam->sec_cam_map), + addr_cam->sec_cam_map); for_each_set_bit(i, addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM) { sec_cam_idx = addr_cam->sec_ent[i]; sec_entry = cam_info->sec_entries[sec_cam_idx]; if (!sec_entry) continue; - seq_printf(m, "\tsec[%d]: sec_cam_idx %u", i, sec_entry->sec_cam_idx); + p += scnprintf(p, end - p, "\tsec[%d]: sec_cam_idx %u", i, + sec_entry->sec_cam_idx); if (sec_entry->ext_key) - seq_printf(m, ", %u", sec_entry->sec_cam_idx + 1); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, ", %u", + sec_entry->sec_cam_idx + 1); + p += scnprintf(p, end - p, "\n"); } + + return p - buf; } -__printf(3, 4) -static void rtw89_dump_pkt_offload(struct seq_file *m, struct list_head *pkt_list, - const char *fmt, ...) +__printf(4, 5) +static int rtw89_dump_pkt_offload(char *buf, size_t bufsz, struct list_head *pkt_list, + const char *fmt, ...) { + char *p = buf, *end = buf + bufsz; struct rtw89_pktofld_info *info; struct va_format vaf; va_list args; if (list_empty(pkt_list)) - return; + return 0; va_start(args, fmt); vaf.va = &args; vaf.fmt = fmt; - seq_printf(m, "%pV", &vaf); + p += scnprintf(p, end - p, "%pV", &vaf); va_end(args); list_for_each_entry(info, pkt_list, list) - seq_printf(m, "%d ", info->id); + p += scnprintf(p, end - p, "%d ", info->id); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void rtw89_vif_link_ids_get(struct seq_file *m, u8 *mac, - struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link) +static int rtw89_vif_link_ids_get(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, u8 *mac, + struct rtw89_vif_link *rtwvif_link) { struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif_link->bssid_cam; + char *p = buf, *end = buf + bufsz; - seq_printf(m, " [%u] %pM\n", rtwvif_link->mac_id, rtwvif_link->mac_addr); - seq_printf(m, "\tlink_id=%u\n", rtwvif_link->link_id); - seq_printf(m, "\tbssid_cam_idx=%u\n", bssid_cam->bssid_cam_idx); - rtw89_dump_addr_cam(m, rtwdev, &rtwvif_link->addr_cam); - rtw89_dump_pkt_offload(m, &rtwvif_link->general_pkt_list, - "\tpkt_ofld[GENERAL]: "); + p += scnprintf(p, end - p, " [%u] %pM\n", rtwvif_link->mac_id, + rtwvif_link->mac_addr); + p += scnprintf(p, end - p, "\tlink_id=%u\n", rtwvif_link->link_id); + p += scnprintf(p, end - p, "\tbssid_cam_idx=%u\n", + bssid_cam->bssid_cam_idx); + p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwvif_link->addr_cam); + p += rtw89_dump_pkt_offload(p, end - p, &rtwvif_link->general_pkt_list, + "\tpkt_ofld[GENERAL]: "); + + return p - buf; } static void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_dev *rtwdev = rtwvif->rtwdev; struct rtw89_vif_link *rtwvif_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; - seq_printf(m, "VIF %pM\n", rtwvif->mac_addr); + p += scnprintf(p, end - p, "VIF %pM\n", rtwvif->mac_addr); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) - rtw89_vif_link_ids_get(m, mac, rtwdev, rtwvif_link); + p += rtw89_vif_link_ids_get(rtwdev, p, end - p, mac, rtwvif_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static void rtw89_dump_ba_cam(struct seq_file *m, struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_dump_ba_cam(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link) { struct rtw89_ba_cam_entry *entry; + char *p = buf, *end = buf + bufsz; bool first = true; list_for_each_entry(entry, &rtwsta_link->ba_cam_list, list) { if (first) { - seq_puts(m, "\tba_cam "); + p += scnprintf(p, end - p, "\tba_cam "); first = false; } else { - seq_puts(m, ", "); + p += scnprintf(p, end - p, ", "); } - seq_printf(m, "tid[%u]=%d", entry->tid, - (int)(entry - rtwdev->cam_info.ba_cam_entry)); + p += scnprintf(p, end - p, "tid[%u]=%d", entry->tid, + (int)(entry - rtwdev->cam_info.ba_cam_entry)); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void rtw89_sta_link_ids_get(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_ids_get(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link) { struct ieee80211_link_sta *link_sta; + char *p = buf, *end = buf + bufsz; rcu_read_lock(); link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, true); - seq_printf(m, " [%u] %pM\n", rtwsta_link->mac_id, link_sta->addr); + p += scnprintf(p, end - p, " [%u] %pM\n", rtwsta_link->mac_id, + link_sta->addr); rcu_read_unlock(); - seq_printf(m, "\tlink_id=%u\n", rtwsta_link->link_id); - rtw89_dump_addr_cam(m, rtwdev, &rtwsta_link->addr_cam); - rtw89_dump_ba_cam(m, rtwdev, rtwsta_link); + p += scnprintf(p, end - p, "\tlink_id=%u\n", rtwsta_link->link_id); + p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwsta_link->addr_cam); + p += rtw89_dump_ba_cam(rtwdev, p, end - p, rtwsta_link); + + return p - buf; } static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_dev *rtwdev = rtwsta->rtwdev; struct rtw89_sta_link *rtwsta_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; - seq_printf(m, "STA %pM %s\n", sta->addr, sta->tdls ? "(TDLS)" : ""); + p += scnprintf(p, end - p, "STA %pM %s\n", sta->addr, + sta->tdls ? "(TDLS)" : ""); rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) - rtw89_sta_link_ids_get(m, rtwdev, rtwsta_link); + p += rtw89_sta_link_ids_get(rtwdev, p, end - p, rtwsta_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static int rtw89_debug_priv_stations_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_cam_info *cam_info = &rtwdev->cam_info; + struct rtw89_debugfs_iter_data iter_data; + char *p = buf, *end = buf + bufsz; u8 idx; mutex_lock(&rtwdev->mutex); - seq_puts(m, "map:\n"); - seq_printf(m, "\tmac_id: %*ph\n", (int)sizeof(rtwdev->mac_id_map), - rtwdev->mac_id_map); - seq_printf(m, "\taddr_cam: %*ph\n", (int)sizeof(cam_info->addr_cam_map), - cam_info->addr_cam_map); - seq_printf(m, "\tbssid_cam: %*ph\n", (int)sizeof(cam_info->bssid_cam_map), - cam_info->bssid_cam_map); - seq_printf(m, "\tsec_cam: %*ph\n", (int)sizeof(cam_info->sec_cam_map), - cam_info->sec_cam_map); - seq_printf(m, "\tba_cam: %*ph\n", (int)sizeof(cam_info->ba_cam_map), - cam_info->ba_cam_map); - seq_printf(m, "\tpkt_ofld: %*ph\n", (int)sizeof(rtwdev->pkt_offload), - rtwdev->pkt_offload); + p += scnprintf(p, end - p, "map:\n"); + p += scnprintf(p, end - p, "\tmac_id: %*ph\n", + (int)sizeof(rtwdev->mac_id_map), + rtwdev->mac_id_map); + p += scnprintf(p, end - p, "\taddr_cam: %*ph\n", + (int)sizeof(cam_info->addr_cam_map), + cam_info->addr_cam_map); + p += scnprintf(p, end - p, "\tbssid_cam: %*ph\n", + (int)sizeof(cam_info->bssid_cam_map), + cam_info->bssid_cam_map); + p += scnprintf(p, end - p, "\tsec_cam: %*ph\n", + (int)sizeof(cam_info->sec_cam_map), + cam_info->sec_cam_map); + p += scnprintf(p, end - p, "\tba_cam: %*ph\n", + (int)sizeof(cam_info->ba_cam_map), + cam_info->ba_cam_map); + p += scnprintf(p, end - p, "\tpkt_ofld: %*ph\n", + (int)sizeof(rtwdev->pkt_offload), + rtwdev->pkt_offload); for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { if (!(rtwdev->chip->support_bands & BIT(idx))) continue; - rtw89_dump_pkt_offload(m, &rtwdev->scan_info.pkt_list[idx], - "\t\t[SCAN %u]: ", idx); + p += rtw89_dump_pkt_offload(p, end - p, &rtwdev->scan_info.pkt_list[idx], + "\t\t[SCAN %u]: ", idx); } + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, - IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, m); + IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, &iter_data); + p += iter_data.written_sz; - ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, m); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, &iter_data); + p += iter_data.written_sz; mutex_unlock(&rtwdev->mutex); - return 0; + return p - buf; } #define DM_INFO(type) {RTW89_DM_ ## type, #type} @@ -3901,41 +4113,42 @@ static const struct rtw89_disabled_dm_info { DM_INFO(THERMAL_PROTECT), }; -static int -rtw89_debug_priv_disable_dm_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_disable_dm_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_disabled_dm_info *info; struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; u32 disabled; int i; - seq_printf(m, "Disabled DM: 0x%x\n", hal->disabled_dm_bitmap); + p += scnprintf(p, end - p, "Disabled DM: 0x%x\n", + hal->disabled_dm_bitmap); for (i = 0; i < ARRAY_SIZE(rtw89_disabled_dm_infos); i++) { info = &rtw89_disabled_dm_infos[i]; disabled = BIT(info->type) & hal->disabled_dm_bitmap; - seq_printf(m, "[%d] %s: %c\n", info->type, info->name, - disabled ? 'X' : 'O'); + p += scnprintf(p, end - p, "[%d] %s: %c\n", info->type, + info->name, + disabled ? 'X' : 'O'); } - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_disable_dm_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_hal *hal = &rtwdev->hal; u32 conf; int ret; - ret = kstrtou32_from_user(user_buf, count, 0, &conf); + ret = kstrtou32(buf, 0, &conf); if (ret) return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 2f8694fe4bd1..fc8a93573144 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2070,24 +2070,28 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, } EXPORT_SYMBOL(rtw89_phy_ant_gain_pwr_offset); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan) { struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; const struct rtw89_chip_info *chip = rtwdev->chip; u8 regd = rtw89_regd_get(rtwdev, chan->band_type); + char *p = buf, *end = buf + bufsz; s8 offset_patha, offset_pathb; if (!chip->support_ant_gain || !(ant_gain->regd_enabled & BIT(regd))) { - seq_puts(m, "no DAG is applied\n"); - return; + p += scnprintf(p, end - p, "no DAG is applied\n"); + goto out; } offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, chan->freq); - seq_printf(m, "ChainA offset: %d dBm\n", offset_patha); - seq_printf(m, "ChainB offset: %d dBm\n", offset_pathb); + p += scnprintf(p, end - p, "ChainA offset: %d dBm\n", offset_patha); + p += scnprintf(p, end - p, "ChainB offset: %d dBm\n", offset_pathb); + +out: + return p - buf; } static const u8 rtw89_rs_idx_num_ax[] = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index cec165e222e7..e20fff9bac3e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -835,8 +835,8 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, u8 bw, void rtw89_phy_ant_gain_init(struct rtw89_dev *rtwdev); s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan); +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan); void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl); s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 94db695e78e6..1f67a2fbce81 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -178,12 +178,14 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); } -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + u32 center_freq) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; + char *p = buf, *end = buf + bufsz; int ret; s32 cfg; u8 fct; @@ -191,36 +193,46 @@ void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_fr lockdep_assert_held(&rtwdev->mutex); if (src == RTW89_SAR_SOURCE_NONE) { - seq_puts(m, "no SAR is applied\n"); - return; + p += scnprintf(p, end - p, "no SAR is applied\n"); + goto out; } - seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); + p += scnprintf(p, end - p, "source: %d (%s)\n", src, + sar_hdl->descr_sar_source); ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); if (ret) { - seq_printf(m, "config: return code: %d\n", ret); - seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", - RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); - return; + p += scnprintf(p, end - p, "config: return code: %d\n", ret); + p += scnprintf(p, end - p, + "assign: max setting: %d (unit: 1/%lu dBm)\n", + RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); + goto out; } fct = sar_hdl->txpwr_factor_sar; - seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); + p += scnprintf(p, end - p, "config: %d (unit: 1/%lu dBm)\n", cfg, + BIT(fct)); + +out: + return p - buf; } -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_tas_info *tas = &rtwdev->tas; + char *p = buf, *end = buf + bufsz; if (!tas->enable) { - seq_puts(m, "no TAS is applied\n"); - return; + p += scnprintf(p, end - p, "no TAS is applied\n"); + goto out; } - seq_printf(m, "DPR gap: %d\n", tas->dpr_gap); - seq_printf(m, "TAS delta: %d\n", tas->delta); + p += scnprintf(p, end - p, "DPR gap: %d\n", tas->dpr_gap); + p += scnprintf(p, end - p, "TAS delta: %d\n", tas->delta); + +out: + return p - buf; } static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 4ae081d2d3b4..095277df5a71 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -19,8 +19,9 @@ struct rtw89_sar_handler { extern const struct cfg80211_sar_capa rtw89_sar_capa; s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev); +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + u32 center_freq); +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); void rtw89_tas_init(struct rtw89_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/util.c b/drivers/net/wireless/realtek/rtw89/util.c index e71956ce9853..33be26873473 100644 --- a/drivers/net/wireless/realtek/rtw89/util.c +++ b/drivers/net/wireless/realtek/rtw89/util.c @@ -104,3 +104,14 @@ u64 rtw89_db_2_linear(u32 db) return linear; } EXPORT_SYMBOL(rtw89_db_2_linear); + +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used) +{ + static const char ellipsis[] = "..."; + + /* length of null terminiator isn't included in 'used' */ + if (used + 1 < size || size < sizeof(ellipsis)) + return; + + memcpy(buf + size - sizeof(ellipsis), ellipsis, sizeof(ellipsis)); +} diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index e669544cafd3..71c7d3502202 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -77,5 +77,6 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask) u32 rtw89_linear_2_db(u64 linear); u64 rtw89_db_2_linear(u32 db); +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used); #endif From f7d06ff7586afa1a705dbb718c210d8394df0729 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 066/465] wifi: rtw89: debugfs: specify buffer size allocated by devm_kazlloc() for reading JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 01fd45d9e102bd9bc9d234aa8f409fa8b94e3919 Author: Ping-Ke Shih Date: Wed Jan 22 14:03:04 2025 +0800 wifi: rtw89: debugfs: specify buffer size allocated by devm_kazlloc() for reading Some debufs entries need output buffer over default size 4K. Since reading of many debugfs entries is to access IO, it costs time to dynamically re-allocate larger buffer and access IO again and again. Add an option to specify the size it needs. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/debug.c | 62 ++++++++++++++++------ 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index db0e7a4beb07..f3ec18860797 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -23,6 +23,10 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask"); #endif #ifdef CONFIG_RTW89_DEBUGFS +struct rtw89_debugfs_priv_opt { + size_t rsize; +}; + struct rtw89_debugfs_priv { struct rtw89_dev *rtwdev; ssize_t (*cb_read)(struct rtw89_dev *rtwdev, @@ -31,6 +35,7 @@ struct rtw89_debugfs_priv { ssize_t (*cb_write)(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, const char *buf, size_t count); + struct rtw89_debugfs_priv_opt opt; union { u32 cb_data; struct { @@ -55,6 +60,8 @@ struct rtw89_debugfs_priv { u8 sel; } mac_mem; }; + ssize_t rused; + char *rbuf; }; struct rtw89_debugfs { @@ -120,23 +127,31 @@ static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - size_t bufsz = PAGE_SIZE; + size_t bufsz = opt->rsize ? opt->rsize : PAGE_SIZE; char *buf; ssize_t n; - buf = kvmalloc(bufsz, GFP_KERNEL); + if (!debugfs_priv->rbuf) + debugfs_priv->rbuf = devm_kzalloc(rtwdev->dev, bufsz, GFP_KERNEL); + + buf = debugfs_priv->rbuf; if (!buf) - return 0; + return -ENOMEM; + + if (*ppos) { + n = debugfs_priv->rused; + goto out; + } n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); rtw89_might_trailing_ellipsis(buf, bufsz, n); - n = simple_read_from_buffer(userbuf, count, ppos, buf, n); + debugfs_priv->rused = n; - kvfree(buf); - - return n; +out: + return simple_read_from_buffer(userbuf, count, ppos, buf, n); } static ssize_t rtw89_debugfs_file_write(struct file *file, @@ -4157,42 +4172,55 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, return count; } -#define rtw89_debug_priv_get(name) \ +#define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_set(name) \ +#define rtw89_debug_priv_set(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _set, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_select_and_get(name) \ +#define rtw89_debug_priv_select_and_get(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _select, \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_set_and_get(name) \ +#define rtw89_debug_priv_set_and_get(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _set, \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } +#define RSIZE_8K .rsize = 0x2000 +#define RSIZE_12K .rsize = 0x3000 +#define RSIZE_16K .rsize = 0x4000 +#define RSIZE_20K .rsize = 0x5000 +#define RSIZE_32K .rsize = 0x8000 +#define RSIZE_64K .rsize = 0x10000 +#define RSIZE_128K .rsize = 0x20000 +#define RSIZE_1M .rsize = 0x100000 + static const struct rtw89_debugfs rtw89_debugfs_templ = { .read_reg = rtw89_debug_priv_select_and_get(read_reg), .write_reg = rtw89_debug_priv_set(write_reg), .read_rf = rtw89_debug_priv_select_and_get(read_rf), .write_rf = rtw89_debug_priv_set(write_rf), - .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump), - .txpwr_table = rtw89_debug_priv_get(txpwr_table), - .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump), - .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump), - .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump), + .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K), + .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K), + .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K), + .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K), + .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump, RSIZE_1M), .send_h2c = rtw89_debug_priv_set(send_h2c), .early_h2c = rtw89_debug_priv_set_and_get(early_h2c), .fw_crash = rtw89_debug_priv_set_and_get(fw_crash), - .btc_info = rtw89_debug_priv_get(btc_info), + .btc_info = rtw89_debug_priv_get(btc_info, RSIZE_12K), .btc_manual = rtw89_debug_priv_set(btc_manual), .fw_log_manual = rtw89_debug_priv_set(fw_log_manual), .phy_info = rtw89_debug_priv_get(phy_info), From 7554b88ea49458f8f2d2ccc4e28c88157c74236b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 067/465] wifi: rtw89: debugfs: use wiphy_locked_debugfs_{read,write}() if needed JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8fdf78f3cd5f87eced0c4f7eed7160a2ec31a008 Author: Ping-Ke Shih Date: Wed Jan 22 14:03:05 2025 +0800 wifi: rtw89: debugfs: use wiphy_locked_debugfs_{read,write}() if needed If a debugfs entry takes driver mutex now, let it uses wiphy_locked_debugfs_{read,write}() to take both driver mutex and wiphy lock. Add rwlock option to support this. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-6-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/debug.c | 63 +++++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index f3ec18860797..35bf627dbae2 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -24,6 +24,8 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask"); #ifdef CONFIG_RTW89_DEBUGFS struct rtw89_debugfs_priv_opt { + bool rlock:1; + bool wlock:1; size_t rsize; }; @@ -123,6 +125,19 @@ static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw) return 0; } +static ssize_t rtw89_debugfs_file_read_helper(struct wiphy *wiphy, struct file *file, + char *buf, size_t bufsz, void *data) +{ + struct rtw89_debugfs_priv *debugfs_priv = data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + ssize_t n; + + n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); + rtw89_might_trailing_ellipsis(buf, bufsz, n); + + return n; +} + static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -145,26 +160,55 @@ static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, goto out; } - n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); - rtw89_might_trailing_ellipsis(buf, bufsz, n); + if (opt->rlock) { + n = wiphy_locked_debugfs_read(rtwdev->hw->wiphy, file, buf, bufsz, + userbuf, count, ppos, + rtw89_debugfs_file_read_helper, + debugfs_priv); + debugfs_priv->rused = n; + return n; + } + + n = rtw89_debugfs_file_read_helper(rtwdev->hw->wiphy, file, buf, bufsz, + debugfs_priv); debugfs_priv->rused = n; out: return simple_read_from_buffer(userbuf, count, ppos, buf, n); } +static ssize_t rtw89_debugfs_file_write_helper(struct wiphy *wiphy, struct file *file, + char *buf, size_t count, void *data) +{ + struct rtw89_debugfs_priv *debugfs_priv = data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + + return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count); +} + static ssize_t rtw89_debugfs_file_write(struct file *file, const char __user *userbuf, size_t count, loff_t *loff) { struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; char *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL); + ssize_t n; if (!buf) return -ENOMEM; + if (opt->wlock) { + n = wiphy_locked_debugfs_write(rtwdev->hw->wiphy, + file, buf, count + 1, + userbuf, count, + rtw89_debugfs_file_write_helper, + debugfs_priv); + return n; + } + if (copy_from_user(buf, userbuf, count)) return -EFAULT; @@ -4206,6 +4250,9 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, #define RSIZE_64K .rsize = 0x10000 #define RSIZE_128K .rsize = 0x20000 #define RSIZE_1M .rsize = 0x100000 +#define RLOCK .rlock = 1 +#define WLOCK .wlock = 1 +#define RWLOCK RLOCK, WLOCK static const struct rtw89_debugfs rtw89_debugfs_templ = { .read_reg = rtw89_debug_priv_select_and_get(read_reg), @@ -4213,18 +4260,18 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .read_rf = rtw89_debug_priv_select_and_get(read_rf), .write_rf = rtw89_debug_priv_set(write_rf), .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K), - .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K), + .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK), .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K), - .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K), + .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK), .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump, RSIZE_1M), .send_h2c = rtw89_debug_priv_set(send_h2c), - .early_h2c = rtw89_debug_priv_set_and_get(early_h2c), - .fw_crash = rtw89_debug_priv_set_and_get(fw_crash), + .early_h2c = rtw89_debug_priv_set_and_get(early_h2c, RWLOCK), + .fw_crash = rtw89_debug_priv_set_and_get(fw_crash, WLOCK), .btc_info = rtw89_debug_priv_get(btc_info, RSIZE_12K), .btc_manual = rtw89_debug_priv_set(btc_manual), - .fw_log_manual = rtw89_debug_priv_set(fw_log_manual), + .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK), .phy_info = rtw89_debug_priv_get(phy_info), - .stations = rtw89_debug_priv_get(stations), + .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm), }; From b7781fbabb447e36c7c5cd228068d04a2efa03d7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 068/465] wifi: rtw89: debugfs: use debugfs_short_fops JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bdf874dc3c7639aace300cd8c5d24486b1a8ec7a Author: Ping-Ke Shih Date: Wed Jan 22 14:03:06 2025 +0800 wifi: rtw89: debugfs: use debugfs_short_fops With this change, the object code size can reduce 768 bytes. text data bss dec hex filename 77257 4262 4 81523 13e73 debug.o (before) 76489 4262 4 80755 13b73 debug.o (after) Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-7-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/debug.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 35bf627dbae2..0863ed6acda2 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -217,25 +217,19 @@ static ssize_t rtw89_debugfs_file_write(struct file *file, return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count); } -static const struct file_operations file_ops_single_r = { - .owner = THIS_MODULE, +static const struct debugfs_short_fops file_ops_single_r = { .read = rtw89_debugfs_file_read, - .open = simple_open, .llseek = generic_file_llseek, }; -static const struct file_operations file_ops_common_rw = { - .owner = THIS_MODULE, +static const struct debugfs_short_fops file_ops_common_rw = { .read = rtw89_debugfs_file_read, .write = rtw89_debugfs_file_write, - .open = simple_open, .llseek = generic_file_llseek, }; -static const struct file_operations file_ops_single_w = { - .owner = THIS_MODULE, +static const struct debugfs_short_fops file_ops_single_w = { .write = rtw89_debugfs_file_write, - .open = simple_open, .llseek = generic_file_llseek, }; From b4795ee088362107090df8abb85961927cdf6930 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 069/465] wifi: rtw89: remove consumers of driver mutex JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6ee1937d8bc93fe0d3eaacbcfd582d84998d557f Author: Ping-Ke Shih Date: Wed Jan 22 14:03:07 2025 +0800 wifi: rtw89: remove consumers of driver mutex All need lock have taken both driver mutex and wiphy lock, so we can remove driver mutex safely by below spatch script. Also, check every lockdep_assert_wiphy() is executed without locks warning at runtime. @ rule1_1 @ @@ - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); @ rule1_2 @ @@ - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); @ rule2_1 @ @@ - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); ... - mutex_unlock(&rtwdev->mutex); @ rule2_2 @ @@ - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); ... - mutex_lock(&rtwdev->mutex); @ rule3_1 @ type t; identifier fn; @@ t fn(struct wiphy *wiphy, ...) { ... - lockdep_assert_wiphy(rtwdev->hw->wiphy); + lockdep_assert_wiphy(wiphy); ... } @ rule3_1_1 @ type t; identifier fn; @@ t fn(...) { ... struct wiphy *wiphy = ...; ... - lockdep_assert_wiphy(rtwdev->hw->wiphy); + lockdep_assert_wiphy(wiphy); ... } @ rule3_2 @ type t; identifier fn; @@ t fn(struct ieee80211_hw *hw, ...) { ... - lockdep_assert_wiphy(rtwdev->hw->wiphy); + lockdep_assert_wiphy(hw->wiphy); ... } @ rule3_2_1 @ type t; identifier fn; @@ t fn(...) { ... struct ieee80211_hw *hw = ...; ... - lockdep_assert_wiphy(rtwdev->hw->wiphy); + lockdep_assert_wiphy(hw->wiphy); ... } The compiler warnings will be fixed manually by latter patch: rtw89/mac80211.c:371:1: warning: statement expected after label Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-8-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/chan.c | 19 ++- drivers/net/wireless/realtek/rtw89/coex.c | 23 ++- drivers/net/wireless/realtek/rtw89/core.c | 28 ++-- drivers/net/wireless/realtek/rtw89/debug.c | 24 +--- drivers/net/wireless/realtek/rtw89/fw.c | 7 +- drivers/net/wireless/realtek/rtw89/mac80211.c | 131 ++++++------------ drivers/net/wireless/realtek/rtw89/phy.c | 7 +- drivers/net/wireless/realtek/rtw89/ps.c | 6 +- drivers/net/wireless/realtek/rtw89/regd.c | 5 +- drivers/net/wireless/realtek/rtw89/sar.c | 9 +- drivers/net/wireless/realtek/rtw89/ser.c | 6 +- drivers/net/wireless/realtek/rtw89/util.h | 2 +- drivers/net/wireless/realtek/rtw89/wow.c | 3 +- 13 files changed, 92 insertions(+), 178 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 9858894dfc23..98f420208632 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -155,7 +155,7 @@ int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev, int ret; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) { chan = rtw89_chan_get(rtwdev, idx); @@ -310,7 +310,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, enum rtw89_entity_mode mode; u8 role_index; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) { WARN(1, "link index %u is invalid (max link inst num: %d)\n", @@ -366,7 +366,7 @@ static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev) u8 pos = 0; int i, j; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++) mgnt->active_roles[i] = NULL; @@ -427,7 +427,7 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) struct rtw89_chan chan; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX); @@ -2401,10 +2401,9 @@ void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work) int ret; int i; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (hal->entity_pause) { - mutex_unlock(&rtwdev->mutex); return; } @@ -2445,8 +2444,6 @@ void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work) default: break; } - - mutex_unlock(&rtwdev->mutex); } void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -2491,7 +2488,7 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev) struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_entity_mode mode; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (hal->entity_pause) return; @@ -2512,7 +2509,7 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_entity_mode mode; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (hal->entity_pause) return; @@ -2555,7 +2552,7 @@ void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, enum rtw89_entity_mode mode; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (unlikely(!hal->entity_pause)) { rtw89_chanctx_proceed_cb(rtwdev, cb_parm); diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 1065f610d80f..a7b7675dca97 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -6721,7 +6721,7 @@ void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->status.map._4way) @@ -6730,7 +6730,6 @@ void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) wl->status.map.connecting = false; _run_coex(rtwdev, BTC_RSN_ACT1_WORK); - mutex_unlock(&rtwdev->mutex); } void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -6741,12 +6740,11 @@ void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; a2dp->play_latency = 0; _run_coex(rtwdev, BTC_RSN_BT_DEVINFO_WORK); - mutex_unlock(&rtwdev->mutex); } void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -6758,7 +6756,7 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->rfk_info.state != BTC_WRFK_STOP) { @@ -6770,7 +6768,6 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); _run_coex(rtwdev, BTC_RSN_RFK_CHK_WORK); } - mutex_unlock(&rtwdev->mutex); } static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) @@ -6874,7 +6871,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; u8 mode, igno_bt, always_freerun; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); dm->run_reason = reason; _update_dm_step(rtwdev, reason); @@ -7311,10 +7308,9 @@ void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *wo struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.eapol_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL); - mutex_unlock(&rtwdev->mutex); } void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -7322,9 +7318,8 @@ void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.arp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP); - mutex_unlock(&rtwdev->mutex); } void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -7332,10 +7327,9 @@ void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *wor struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.dhcp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP); - mutex_unlock(&rtwdev->mutex); } void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -7343,10 +7337,9 @@ void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *wor struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.icmp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ICMP); - mutex_unlock(&rtwdev->mutex); } static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 330b23db4868..602185b3c9b0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2079,7 +2079,7 @@ static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work struct list_head *pkt_list = rtwdev->scan_info.pkt_list; struct rtw89_pktofld_info *info; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (!rtwdev->scanning) goto out; @@ -2097,7 +2097,6 @@ static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work } out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, @@ -3148,9 +3147,8 @@ static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ips_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_enter_ips_by_hwflags(rtwdev); - mutex_unlock(&rtwdev->mutex); } static void rtw89_core_txq_work(struct work_struct *w) @@ -3292,7 +3290,7 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) u32 reg; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ips_by_hwflags(rtwdev); rtw89_leave_lps(rtwdev); @@ -3346,7 +3344,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) u32 reg; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ieee80211_remain_on_channel_expired(hw); @@ -3389,7 +3387,7 @@ void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_dev *rtwdev = rtwvif->rtwdev; struct rtw89_roc *roc = &rtwvif->roc; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); switch (roc->state) { case RTW89_ROC_IDLE: @@ -3402,8 +3400,6 @@ void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work) default: break; } - - mutex_unlock(&rtwdev->mutex); } static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, @@ -3543,7 +3539,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags)) return; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) goto out; @@ -3580,7 +3576,6 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_enter_lps_track(rtwdev); out: - mutex_unlock(&rtwdev->mutex); } u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size) @@ -3614,7 +3609,7 @@ int rtw89_core_acquire_sta_ba_entry(struct rtw89_dev *rtwdev, u8 idx; int i; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); idx = rtw89_core_acquire_bit_map(cam_info->ba_cam_map, chip->bacam_num); if (idx == chip->bacam_num) { @@ -3658,7 +3653,7 @@ int rtw89_core_release_sta_ba_entry(struct rtw89_dev *rtwdev, struct rtw89_ba_cam_entry *entry = NULL, *tmp; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_for_each_entry_safe(entry, tmp, &rtwsta_link->ba_cam_list, list) { if (entry->tid != tid) @@ -4450,9 +4445,8 @@ void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) rtwdev = rtwvif_link->rtwvif->rtwdev; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); - mutex_unlock(&rtwdev->mutex); } int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond) @@ -4595,7 +4589,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags); - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); wiphy_work_cancel(wiphy, &rtwdev->c2h_work); wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work); @@ -4613,8 +4607,6 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) cancel_delayed_work_sync(&rtwdev->forbid_ba_work); wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work); - mutex_lock(&rtwdev->mutex); - rtw89_btc_ntfy_poweroff(rtwdev); rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true); rtw89_mac_flush_txq(rtwdev, BIT(rtwdev->hw->queues) - 1, true); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 0863ed6acda2..56be6705e9ea 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -954,7 +954,7 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; ssize_t n; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_leave_ps_mode(rtwdev); chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); @@ -993,12 +993,9 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, goto err; p += n; - mutex_unlock(&rtwdev->mutex); - return p - buf; err: - mutex_unlock(&rtwdev->mutex); return n; } @@ -1201,7 +1198,7 @@ rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, } } - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_leave_ps_mode(rtwdev); if (grant_read) rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); @@ -1211,7 +1208,6 @@ rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, debugfs_priv->mac_mem.len); if (grant_read) rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); - mutex_unlock(&rtwdev->mutex); return p - buf; } @@ -3518,11 +3514,10 @@ rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; int seq = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) p += scnprintf(p, end - p, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c); - mutex_unlock(&rtwdev->mutex); return p - buf; } @@ -3555,9 +3550,8 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, early_h2c->h2c = h2c; early_h2c->h2c_len = h2c_len; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_add_tail(&early_h2c->list, &rtwdev->early_h2c_list); - mutex_unlock(&rtwdev->mutex); out: return count; @@ -3633,10 +3627,9 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, return -EINVAL; } - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); set_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags); ret = sim(rtwdev); - mutex_unlock(&rtwdev->mutex); if (ret) return ret; @@ -3681,12 +3674,11 @@ static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev, if (kstrtobool(buf, &fw_log_manual)) goto out; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); log->enable = fw_log_manual; if (log->enable) rtw89_fw_log_prepare(rtwdev); rtw89_fw_h2c_fw_log(rtwdev, fw_log_manual); - mutex_unlock(&rtwdev->mutex); out: return count; } @@ -4113,7 +4105,7 @@ static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; u8 idx; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); p += scnprintf(p, end - p, "map:\n"); p += scnprintf(p, end - p, "\tmac_id: %*ph\n", @@ -4151,8 +4143,6 @@ static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, &iter_data); p += iter_data.written_sz; - mutex_unlock(&rtwdev->mutex); - return p - buf; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 64c2895309c6..2713bae44990 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6096,7 +6096,7 @@ void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev) { struct rtw89_early_h2c *early_h2c; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) { rtw89_fw_h2c_raw(rtwdev, early_h2c->h2c, early_h2c->h2c_len); @@ -6203,9 +6203,8 @@ void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work) skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { skb_unlink(skb, &rtwdev->c2h_queue); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_fw_c2h_cmd_handle(rtwdev, skb); - mutex_unlock(&rtwdev->mutex); dev_kfree_skb_any(skb); } } @@ -6286,7 +6285,7 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev, u32 ret; if (h2c_info && h2c_info->id != RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE) - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (!h2c_info && !c2h_info) return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 7b3520e6389b..11516ede13e4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -59,9 +59,8 @@ static int rtw89_ops_start(struct ieee80211_hw *hw) struct rtw89_dev *rtwdev = hw->priv; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ret = rtw89_core_start(rtwdev); - mutex_unlock(&rtwdev->mutex); return ret; } @@ -70,9 +69,8 @@ static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_core_stop(rtwdev); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) @@ -82,7 +80,7 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) /* let previous ips work finish to ensure we don't leave ips twice */ wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); if ((changed & IEEE80211_CONF_CHANGE_IDLE) && @@ -100,8 +98,6 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) !rtwdev->scanning) rtw89_enter_ips(rtwdev); - mutex_unlock(&rtwdev->mutex); - return 0; } @@ -142,9 +138,8 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev, static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); - mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); @@ -165,7 +160,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, rtw89_debug(rtwdev, RTW89_DBG_STATE, "add vif %pM type %d, p2p %d\n", vif->addr, vif->type, vif->p2p); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ips_by_hwflags(rtwdev); @@ -213,8 +208,6 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, goto unset_link; rtw89_recalc_lps(rtwdev); - - mutex_unlock(&rtwdev->mutex); return 0; unset_link: @@ -225,7 +218,6 @@ release_port: release_macid: rtw89_release_mac_id(rtwdev, mac_id); err: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -244,7 +236,7 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID]; if (unlikely(!rtwvif_link)) { @@ -265,8 +257,6 @@ bottom: rtw89_recalc_lps(rtwdev); rtw89_enter_ips_by_hwflags(rtwdev); - - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_change_interface(struct ieee80211_hw *hw, @@ -304,7 +294,7 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 rx_fltr; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | @@ -374,7 +364,6 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, rx_fltr); out: - mutex_unlock(&rtwdev->mutex); } static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = { @@ -689,7 +678,7 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); if (changed & BSS_CHANGED_ASSOC) { @@ -712,8 +701,6 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ARP_FILTER) rtwvif->ip_addr = vif->cfg.arp_addr_list[0]; - - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, @@ -725,7 +712,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[conf->link_id]; @@ -765,7 +752,6 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, true); out: - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_start_ap(struct ieee80211_hw *hw, @@ -778,7 +764,7 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, const struct rtw89_chan *chan; int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { @@ -791,7 +777,6 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); if (chan->band_type == RTW89_BAND_6G) { - mutex_unlock(&rtwdev->mutex); return -EOPNOTSUPP; } @@ -816,7 +801,6 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, rtw89_queue_chanctx_work(rtwdev); out: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -829,7 +813,7 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { @@ -847,7 +831,6 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); out: - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, @@ -874,7 +857,7 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, struct rtw89_vif_link *rtwvif_link; int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[link_id]; @@ -890,7 +873,6 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, __rtw89_conf_tx(rtwdev, rtwvif_link, ac); out: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -938,10 +920,9 @@ static int rtw89_ops_sta_state(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); ret = __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); - mutex_unlock(&rtwdev->mutex); return ret; } @@ -954,7 +935,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct rtw89_dev *rtwdev = hw->priv; int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); switch (cmd) { @@ -979,7 +960,6 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } out: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -1002,32 +982,28 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); clear_bit(tid, rtwsta->ampdu_map); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); - mutex_unlock(&rtwdev->mutex); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); rtwsta->ampdu_params[tid].agg_num = params->buf_size; rtwsta->ampdu_params[tid].amsdu = params->amsdu; set_bit(tid, rtwsta->ampdu_map); rtw89_leave_ps_mode(rtwdev); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); - mutex_unlock(&rtwdev->mutex); break; case IEEE80211_AMPDU_RX_START: - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); - mutex_unlock(&rtwdev->mutex); break; case IEEE80211_AMPDU_RX_STOP: - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params); - mutex_unlock(&rtwdev->mutex); break; default: WARN_ON(1); @@ -1041,11 +1017,10 @@ static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) rtw89_mac_update_rts_threshold(rtwdev); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1085,7 +1060,7 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_lps(rtwdev); rtw89_hci_flush_queues(rtwdev, queues, drop); @@ -1093,8 +1068,6 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, __rtw89_drop_packets(rtwdev, vif); else rtw89_mac_flush_txq(rtwdev, queues, drop); - - mutex_unlock(&rtwdev->mutex); } struct rtw89_iter_bitrate_mask_data { @@ -1141,10 +1114,9 @@ static int rtw89_ops_set_bitrate_mask(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_phy_rate_pattern_vif(rtwdev, vif, mask); rtw89_ra_mask_info_update(rtwdev, vif, mask); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1162,12 +1134,11 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) return -EINVAL; } - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); hal->antenna_tx = tx_ant; hal->antenna_rx = rx_ant; hal->tx_path_diversity = false; hal->ant_diversity_fixed = true; - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1192,7 +1163,7 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { @@ -1203,7 +1174,6 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw, rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false); out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, @@ -1213,7 +1183,7 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { @@ -1224,7 +1194,6 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, rtw89_core_scan_complete(rtwdev, rtwvif_link, false); out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw, @@ -1247,7 +1216,7 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); if (rtwdev->scanning || rtwvif->offchan) { ret = -EBUSY; @@ -1269,7 +1238,6 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } out: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -1284,7 +1252,7 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw, if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); if (!rtwdev->scanning) goto out; @@ -1298,7 +1266,6 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw, rtw89_hw_scan_abort(rtwdev, rtwvif_link); out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_sta_rc_update(struct ieee80211_hw *hw, @@ -1323,9 +1290,8 @@ static int rtw89_ops_add_chanctx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ret = rtw89_chanctx_ops_add(rtwdev, ctx); - mutex_unlock(&rtwdev->mutex); return ret; } @@ -1335,9 +1301,8 @@ static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_chanctx_ops_remove(rtwdev, ctx); - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, @@ -1346,9 +1311,8 @@ static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_chanctx_ops_change(rtwdev, ctx, changed); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -1361,7 +1325,7 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, struct rtw89_vif_link *rtwvif_link; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { @@ -1375,7 +1339,6 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); out: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -1389,11 +1352,10 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { - mutex_unlock(&rtwdev->mutex); rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); @@ -1401,7 +1363,6 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, } rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, ctx); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, @@ -1417,10 +1378,9 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, if (!rtwvif) return -EINVAL; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); if (roc->state != RTW89_ROC_IDLE) { - mutex_unlock(&rtwdev->mutex); return -EBUSY; } @@ -1438,8 +1398,6 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, rtw89_roc_start(rtwdev, rtwvif); - mutex_unlock(&rtwdev->mutex); - return 0; } @@ -1454,9 +1412,8 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw, wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_roc_end(rtwdev, rtwvif); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1477,14 +1434,13 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); if (sta) rtw89_core_set_tid_config(rtwdev, sta, tid_config); else ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_set_tid_config_iter, tid_config); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1508,7 +1464,7 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); return rtw89_can_work_on_links(rtwdev, vif, active_links); } @@ -1570,7 +1526,7 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw, int ret = 0; int i; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1719,7 +1675,7 @@ int rtw89_ops_change_sta_links(struct ieee80211_hw *hw, unsigned long set_links = new_links & ~old_links; int ret = 0; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1752,9 +1708,8 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw, set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ret = rtw89_wow_suspend(rtwdev, wowlan); - mutex_unlock(&rtwdev->mutex); if (ret) { rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret); @@ -1770,11 +1725,10 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw) struct rtw89_dev *rtwdev = hw->priv; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ret = rtw89_wow_resume(rtwdev); if (ret) rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret); - mutex_unlock(&rtwdev->mutex); clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work, @@ -1804,12 +1758,10 @@ static void rtw89_set_rekey_data(struct ieee80211_hw *hw, return; } - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); memcpy(gtk_info->kek, data->kek, data->kek_len); memcpy(gtk_info->kck, data->kck, data->kck_len); - - mutex_unlock(&rtwdev->mutex); } #endif @@ -1817,7 +1769,7 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); /* wl_disable GPIO get floating when entering LPS */ if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) @@ -1826,7 +1778,6 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) rtw89_core_rfkill_poll(rtwdev, false); out: - mutex_unlock(&rtwdev->mutex); } const struct ieee80211_ops rtw89_ops = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index fc8a93573144..26370eadf77f 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4661,7 +4661,7 @@ void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work) cfo_track_work.work); struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (!cfo->cfo_trig_by_timer_en) goto out; rtw89_leave_ps_mode(rtwdev); @@ -4669,7 +4669,6 @@ void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work) wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work, msecs_to_jiffies(cfo->cfo_timer_ms)); out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev) @@ -6537,7 +6536,7 @@ void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work) antdiv_work.work); struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (antdiv->training_count <= ANTDIV_TRAINNING_CNT) { rtw89_phy_antdiv_training_state(rtwdev); @@ -6545,8 +6544,6 @@ void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_phy_antdiv_decision_state(rtwdev); rtw89_phy_antdiv_set_ant(rtwdev); } - - mutex_unlock(&rtwdev->mutex); } void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 96ea04d90cd3..ac46a7baa00d 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -113,7 +113,7 @@ static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) { - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); __rtw89_leave_ps_mode(rtwdev); } @@ -125,7 +125,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool can_ps_mode = true; unsigned int link_id; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) return; @@ -162,7 +162,7 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev) struct rtw89_vif *rtwvif; unsigned int link_id; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) return; diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 46f5c7eb1683..cc4b080ecc3c 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -745,7 +745,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request struct rtw89_dev *rtwdev = hw->priv; wiphy_lock(wiphy); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); rtw89_leave_ps_mode(rtwdev); if (wiphy->regd) { @@ -761,7 +761,6 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request rtw89_core_set_chip_txpwr(rtwdev); exit: - mutex_unlock(&rtwdev->mutex); wiphy_unlock(wiphy); } @@ -1012,7 +1011,7 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi unsigned int changed = 0; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); /* The result of reg_6ghz_tpe may depend on reg_6ghz_power type, * so must do reg_6ghz_tpe_recalc() after reg_6ghz_power_recalc(). diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 1f67a2fbce81..949edb4bd4df 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -150,7 +150,7 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) s32 cfg; u8 fct; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (src == RTW89_SAR_SOURCE_NONE) return RTW89_SAR_TXPWR_MAC_MAX; @@ -190,7 +190,7 @@ int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, s32 cfg; u8 fct; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (src == RTW89_SAR_SOURCE_NONE) { p += scnprintf(p, end - p, "no SAR is applied\n"); @@ -241,7 +241,7 @@ static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, enum rtw89_sar_sources src; int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); src = rtwdev->sar.src; if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { @@ -254,7 +254,6 @@ static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, rtw89_core_set_chip_txpwr(rtwdev); exit: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -328,7 +327,7 @@ static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) const struct rtw89_chan *chan; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (src == RTW89_SAR_SOURCE_NONE) return; diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 9658672ad274..b9607bdca6c8 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -157,9 +157,8 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt) ser_st_name(ser), ser_ev_name(ser, evt)); wiphy_lock(rtwdev->hw->wiphy); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_leave_lps(rtwdev); - mutex_unlock(&rtwdev->mutex); wiphy_unlock(rtwdev->hw->wiphy); ser->st_tbl[ser->state].st_func(ser, evt); @@ -714,9 +713,8 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: wiphy_lock(rtwdev->hw->wiphy); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); ser_l2_reset_st_pre_hdl(ser); - mutex_unlock(&rtwdev->mutex); wiphy_unlock(rtwdev->hw->wiphy); ieee80211_restart_hw(rtwdev->hw); diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index 71c7d3502202..df283a858046 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -25,7 +25,7 @@ static inline bool rtw89_rtwvif_in_list(struct rtw89_dev *rtwdev, { struct rtw89_vif *rtwvif; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_for_each_rtwvif(rtwdev, rtwvif) if (rtwvif == new) diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 01754d031bb4..6031633b2709 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -619,12 +619,11 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev, /* ieee80211_gtk_rekey_add() will call set_key(), therefore we * need to unlock mutex */ - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (ieee80211_vif_is_mld(wow_vif)) key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, rtwvif_link->link_id); else key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, -1); - mutex_lock(&rtwdev->mutex); kfree(rekey_conf); if (IS_ERR(key)) { From 09492432f96a7faf79e737ff651a37f96940689d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:29 +0200 Subject: [PATCH 070/465] wifi: rtw89: manual cosmetic along lockdep_assert_wiphy() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ed114a7ac636b7151b41935d566d07b2a84b092a Author: Ping-Ke Shih Date: Wed Jan 22 14:03:08 2025 +0800 wifi: rtw89: manual cosmetic along lockdep_assert_wiphy() With spatch script, already remove most driver mutex and generate lockdep_assert_wiphy(), and some are needed to refine manually further to be expected: - lockdep_assert_wiphy() always be the first statement in function - return directly rather than unnecessary goto - change assert from mutex to wiphy lock, which script can't convert automatically. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-9-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/chan.c | 3 +- drivers/net/wireless/realtek/rtw89/coex.c | 7 + drivers/net/wireless/realtek/rtw89/core.c | 23 ++- drivers/net/wireless/realtek/rtw89/debug.c | 30 ++-- drivers/net/wireless/realtek/rtw89/fw.c | 7 +- drivers/net/wireless/realtek/rtw89/mac80211.c | 157 +++++++----------- drivers/net/wireless/realtek/rtw89/phy.c | 4 +- drivers/net/wireless/realtek/rtw89/regd.c | 1 - drivers/net/wireless/realtek/rtw89/sar.c | 11 +- drivers/net/wireless/realtek/rtw89/ser.c | 2 - drivers/net/wireless/realtek/rtw89/util.h | 2 +- drivers/net/wireless/realtek/rtw89/wow.c | 6 +- 12 files changed, 112 insertions(+), 141 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 98f420208632..be6d73273910 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -2403,9 +2403,8 @@ void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work) lockdep_assert_wiphy(wiphy); - if (hal->entity_pause) { + if (hal->entity_pause) return; - } for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) { if (test_and_clear_bit(i, hal->changes)) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index a7b7675dca97..2c0ccf2f8bd9 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -6722,6 +6722,7 @@ void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_wl_info *wl = &cx->wl; lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->status.map._4way) @@ -6741,6 +6742,7 @@ void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; a2dp->play_latency = 0; @@ -6757,6 +6759,7 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_btc_wl_info *wl = &cx->wl; lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->rfk_info.state != BTC_WRFK_STOP) { @@ -7309,6 +7312,7 @@ void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *wo btc.eapol_notify_work); lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL); } @@ -7319,6 +7323,7 @@ void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work btc.arp_notify_work); lockdep_assert_wiphy(wiphy); + rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP); } @@ -7328,6 +7333,7 @@ void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *wor btc.dhcp_notify_work); lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP); } @@ -7338,6 +7344,7 @@ void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *wor btc.icmp_notify_work); lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ICMP); } diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 602185b3c9b0..4e6d117439a5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2082,7 +2082,7 @@ static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work lockdep_assert_wiphy(wiphy); if (!rtwdev->scanning) - goto out; + return; list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { if (!info->cancel || !test_bit(info->id, rtwdev->pkt_offload)) @@ -2095,8 +2095,6 @@ static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work * since if during scanning, pkt_list is accessed in bottom half. */ } - -out: } static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, @@ -3147,7 +3145,9 @@ static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ips_work); + lockdep_assert_wiphy(wiphy); + rtw89_enter_ips_by_hwflags(rtwdev); } @@ -3536,20 +3536,20 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) track_work.work); bool tfc_changed; + lockdep_assert_wiphy(wiphy); + if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags)) return; - lockdep_assert_wiphy(wiphy); - if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) - goto out; + return; wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, RTW89_TRACK_WORK_PERIOD); tfc_changed = rtw89_traffic_stats_track(rtwdev); if (rtwdev->scanning) - goto out; + return; rtw89_leave_lps(rtwdev); @@ -3574,8 +3574,6 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) if (rtwdev->lps_enabled && !rtwdev->btc.lps) rtw89_enter_lps_track(rtwdev); - -out: } u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size) @@ -4440,12 +4438,13 @@ void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link, update_beacon_work); + lockdep_assert_wiphy(wiphy); + if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE) return; rtwdev = rtwvif_link->rtwvif->rtwdev; - lockdep_assert_wiphy(wiphy); rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); } @@ -4581,6 +4580,8 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) struct wiphy *wiphy = rtwdev->hw->wiphy; struct rtw89_btc *btc = &rtwdev->btc; + lockdep_assert_wiphy(wiphy); + /* Prvent to stop twice; enter_ips and ops_stop */ if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) return; @@ -4589,8 +4590,6 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags); - lockdep_assert_wiphy(wiphy); - wiphy_work_cancel(wiphy, &rtwdev->c2h_work); wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work); wiphy_work_cancel(wiphy, &btc->eapol_notify_work); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 56be6705e9ea..885a5ebeb6cd 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -955,6 +955,7 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, ssize_t n; lockdep_assert_wiphy(rtwdev->hw->wiphy); + rtw89_leave_ps_mode(rtwdev); chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); @@ -970,34 +971,28 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, p += rtw89_print_ant_gain(rtwdev, p, end - p, chan); tbl = dbgfs_txpwr_tables[chip_gen]; - if (!tbl) { - n = -EOPNOTSUPP; - goto err; - } + if (!tbl) + return -EOPNOTSUPP; p += scnprintf(p, end - p, "\n[TX power byrate]\n"); n = __print_txpwr_map(rtwdev, p, end - p, tbl->byr); if (n < 0) - goto err; + return n; p += n; p += scnprintf(p, end - p, "\n[TX power limit]\n"); n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt); if (n < 0) - goto err; + return n; p += n; p += scnprintf(p, end - p, "\n[TX power limit_ru]\n"); n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru); if (n < 0) - goto err; + return n; p += n; return p - buf; - -err: - - return n; } static ssize_t @@ -1182,6 +1177,8 @@ rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; bool grant_read = false; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM) return -ENOENT; @@ -1198,7 +1195,6 @@ rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, } } - lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_leave_ps_mode(rtwdev); if (grant_read) rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); @@ -3515,6 +3511,7 @@ rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev, int seq = 0; lockdep_assert_wiphy(rtwdev->hw->wiphy); + list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) p += scnprintf(p, end - p, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c); @@ -3531,6 +3528,8 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, u8 *h2c; u16 h2c_len = count / 2; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + h2c = rtw89_hex2bin(rtwdev, buf, count); if (IS_ERR(h2c)) return -EFAULT; @@ -3550,7 +3549,6 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, early_h2c->h2c = h2c; early_h2c->h2c_len = h2c_len; - lockdep_assert_wiphy(rtwdev->hw->wiphy); list_add_tail(&early_h2c->list, &rtwdev->early_h2c_list); out: @@ -3610,6 +3608,8 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, u8 crash_type; int ret; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + ret = kstrtou8(buf, 0, &crash_type); if (ret) return -EINVAL; @@ -3627,7 +3627,6 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, return -EINVAL; } - lockdep_assert_wiphy(rtwdev->hw->wiphy); set_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags); ret = sim(rtwdev); @@ -3671,10 +3670,11 @@ static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev, struct rtw89_fw_log *log = &rtwdev->fw.log; bool fw_log_manual; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + if (kstrtobool(buf, &fw_log_manual)) goto out; - lockdep_assert_wiphy(rtwdev->hw->wiphy); log->enable = fw_log_manual; if (log->enable) rtw89_fw_log_prepare(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2713bae44990..2982ec367d9d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6107,13 +6107,13 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) { struct rtw89_early_h2c *early_h2c, *tmp; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + list_for_each_entry_safe(early_h2c, tmp, &rtwdev->early_h2c_list, list) { list_del(&early_h2c->list); kfree(early_h2c->h2c); kfree(early_h2c); } - mutex_unlock(&rtwdev->mutex); } static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) @@ -6201,9 +6201,10 @@ void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work) c2h_work); struct sk_buff *skb, *tmp; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { skb_unlink(skb, &rtwdev->c2h_queue); - lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_fw_c2h_cmd_handle(rtwdev, skb); dev_kfree_skb_any(skb); } diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 11516ede13e4..778ca8589284 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -57,12 +57,10 @@ static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw, static int rtw89_ops_start(struct ieee80211_hw *hw) { struct rtw89_dev *rtwdev = hw->priv; - int ret; lockdep_assert_wiphy(hw->wiphy); - ret = rtw89_core_start(rtwdev); - return ret; + return rtw89_core_start(rtwdev); } static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend) @@ -70,6 +68,7 @@ static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend) struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_core_stop(rtwdev); } @@ -77,10 +76,11 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) { struct rtw89_dev *rtwdev = hw->priv; + lockdep_assert_wiphy(hw->wiphy); + /* let previous ips work finish to ensure we don't leave ips twice */ wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work); - lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ps_mode(rtwdev); if ((changed & IEEE80211_CONF_CHANGE_IDLE) && @@ -139,6 +139,7 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { lockdep_assert_wiphy(rtwdev->hw->wiphy); + wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); rtw89_leave_ps_mode(rtwdev); @@ -157,11 +158,11 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, u8 mac_id, port; int ret = 0; + lockdep_assert_wiphy(hw->wiphy); + rtw89_debug(rtwdev, RTW89_DBG_STATE, "add vif %pM type %d, p2p %d\n", vif->addr, vif->type, vif->p2p); - lockdep_assert_wiphy(hw->wiphy); - rtw89_leave_ips_by_hwflags(rtwdev); if (RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw)) @@ -169,10 +170,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, IEEE80211_VIF_SUPPORTS_CQM_RSSI; mac_id = rtw89_acquire_mac_id(rtwdev); - if (mac_id == RTW89_MAX_MAC_ID_NUM) { - ret = -ENOSPC; - goto err; - } + if (mac_id == RTW89_MAX_MAC_ID_NUM) + return -ENOSPC; port = rtw89_core_acquire_bit_map(rtwdev->hw_port, RTW89_PORT_NUM); if (port == RTW89_PORT_NUM) { @@ -217,7 +216,6 @@ release_port: rtw89_core_release_bit_map(rtwdev->hw_port, port); release_macid: rtw89_release_mac_id(rtwdev, mac_id); -err: return ret; } @@ -231,13 +229,13 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, u8 port = rtw89_vif_get_main_port(rtwvif); struct rtw89_vif_link *rtwvif_link; + lockdep_assert_wiphy(hw->wiphy); + rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n", vif->addr, vif->type, vif->p2p); wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); - lockdep_assert_wiphy(hw->wiphy); - rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID]; if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, @@ -295,6 +293,7 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, u32 rx_fltr; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | @@ -357,13 +356,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, B_AX_RX_FLTR_CFG_MASK, rx_fltr); if (!rtwdev->dbcc_en) - goto out; + return; rtw89_write32_mask(rtwdev, rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1), B_AX_RX_FLTR_CFG_MASK, rx_fltr); - -out: } static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = { @@ -679,6 +676,7 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); if (changed & BSS_CHANGED_ASSOC) { @@ -713,6 +711,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, struct rtw89_vif_link *rtwvif_link; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[conf->link_id]; @@ -720,7 +719,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, conf->link_id); - goto out; + return; } if (changed & BSS_CHANGED_BSSID) { @@ -750,8 +749,6 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_TPE) rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, true); - -out: } static int rtw89_ops_start_ap(struct ieee80211_hw *hw, @@ -771,14 +768,12 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - if (chan->band_type == RTW89_BAND_6G) { + if (chan->band_type == RTW89_BAND_6G) return -EOPNOTSUPP; - } if (rtwdev->scanning) rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); @@ -795,14 +790,12 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) { ret = rtw89_fw_h2c_ap_info_refcount(rtwdev, true); if (ret) - goto out; + return ret; } rtw89_queue_chanctx_work(rtwdev); -out: - - return ret; + return 0; } static @@ -820,7 +813,7 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - goto out; + return; } if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) @@ -829,8 +822,6 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_mac_stop_ap(rtwdev, rtwvif_link); rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL); rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); - -out: } static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, @@ -855,9 +846,9 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - int ret = 0; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[link_id]; @@ -865,16 +856,13 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } rtwvif_link->tx_params[ac] = *params; __rtw89_conf_tx(rtwdev, rtwvif_link, ac); -out: - - return ret; + return 0; } static int __rtw89_ops_sta_state(struct ieee80211_hw *hw, @@ -918,13 +906,11 @@ static int rtw89_ops_sta_state(struct ieee80211_hw *hw, enum ieee80211_sta_state new_state) { struct rtw89_dev *rtwdev = hw->priv; - int ret; lockdep_assert_wiphy(hw->wiphy); - rtw89_leave_ps_mode(rtwdev); - ret = __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); - return ret; + rtw89_leave_ps_mode(rtwdev); + return __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); } static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -933,9 +919,10 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_key_conf *key) { struct rtw89_dev *rtwdev = hw->priv; - int ret = 0; + int ret; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); switch (cmd) { @@ -944,7 +931,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = rtw89_cam_sec_key_add(rtwdev, vif, sta, key); if (ret && ret != -EOPNOTSUPP) { rtw89_err(rtwdev, "failed to add key to sec cam\n"); - goto out; + return ret; } break; case DISABLE_KEY: @@ -954,13 +941,11 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = rtw89_cam_sec_key_del(rtwdev, vif, sta, key, true); if (ret) { rtw89_err(rtwdev, "failed to remove key from sec cam\n"); - goto out; + return ret; } break; } -out: - return ret; } @@ -976,20 +961,20 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_txq *txq = sta->txq[tid]; struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + switch (params->action) { case IEEE80211_AMPDU_TX_START: return IEEE80211_AMPDU_TX_START_IMMEDIATE; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - lockdep_assert_wiphy(rtwdev->hw->wiphy); clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); clear_bit(tid, rtwsta->ampdu_map); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - lockdep_assert_wiphy(rtwdev->hw->wiphy); set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); rtwsta->ampdu_params[tid].agg_num = params->buf_size; rtwsta->ampdu_params[tid].amsdu = params->amsdu; @@ -998,11 +983,9 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); break; case IEEE80211_AMPDU_RX_START: - lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); break; case IEEE80211_AMPDU_RX_STOP: - lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params); break; default: @@ -1018,6 +1001,7 @@ static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value) struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) rtw89_mac_update_rts_threshold(rtwdev); @@ -1061,6 +1045,7 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_lps(rtwdev); rtw89_hci_flush_queues(rtwdev, queues, drop); @@ -1115,6 +1100,7 @@ static int rtw89_ops_set_bitrate_mask(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_phy_rate_pattern_vif(rtwdev, vif, mask); rtw89_ra_mask_info_update(rtwdev, vif, mask); @@ -1127,6 +1113,8 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct rtw89_dev *rtwdev = hw->priv; struct rtw89_hal *hal = &rtwdev->hal; + lockdep_assert_wiphy(hw->wiphy); + if (hal->ant_diversity) { if (tx_ant != rx_ant || hweight32(tx_ant) != 1) return -EINVAL; @@ -1134,7 +1122,6 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) return -EINVAL; } - lockdep_assert_wiphy(hw->wiphy); hal->antenna_tx = tx_ant; hal->antenna_rx = rx_ant; hal->tx_path_diversity = false; @@ -1168,12 +1155,10 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw, rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "sw scan start: find no link on HW-0\n"); - goto out; + return; } rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false); - -out: } static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, @@ -1188,12 +1173,10 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "sw scan complete: find no link on HW-0\n"); - goto out; + return; } rtw89_core_scan_complete(rtwdev, rtwvif_link, false); - -out: } static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw, @@ -1213,21 +1196,18 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_vif_link *rtwvif_link; int ret; + lockdep_assert_wiphy(hw->wiphy); + if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; - lockdep_assert_wiphy(hw->wiphy); - - if (rtwdev->scanning || rtwvif->offchan) { - ret = -EBUSY; - goto out; - } + if (rtwdev->scanning || rtwvif->offchan) + return -EBUSY; rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "hw scan: find no link on HW-0\n"); - ret = -ENOLINK; - goto out; + return -ENOLINK; } rtw89_hw_scan_start(rtwdev, rtwvif_link, req); @@ -1237,8 +1217,6 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_err(rtwdev, "HW scan failed with status: %d\n", ret); } -out: - return ret; } @@ -1249,23 +1227,21 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; + lockdep_assert_wiphy(hw->wiphy); + if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return; - lockdep_assert_wiphy(hw->wiphy); - if (!rtwdev->scanning) - goto out; + return; rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "cancel hw scan: find no link on HW-0\n"); - goto out; + return; } rtw89_hw_scan_abort(rtwdev, rtwvif_link); - -out: } static void rtw89_ops_sta_rc_update(struct ieee80211_hw *hw, @@ -1288,12 +1264,10 @@ static int rtw89_ops_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct rtw89_dev *rtwdev = hw->priv; - int ret; lockdep_assert_wiphy(hw->wiphy); - ret = rtw89_chanctx_ops_add(rtwdev, ctx); - return ret; + return rtw89_chanctx_ops_add(rtwdev, ctx); } static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, @@ -1302,6 +1276,7 @@ static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_chanctx_ops_remove(rtwdev, ctx); } @@ -1312,6 +1287,7 @@ static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + rtw89_chanctx_ops_change(rtwdev, ctx, changed); } @@ -1323,7 +1299,6 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - int ret; lockdep_assert_wiphy(hw->wiphy); @@ -1332,15 +1307,10 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } - ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); - -out: - - return ret; + return rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); } static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, @@ -1375,11 +1345,11 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); struct rtw89_roc *roc = &rtwvif->roc; + lockdep_assert_wiphy(hw->wiphy); + if (!rtwvif) return -EINVAL; - lockdep_assert_wiphy(hw->wiphy); - if (roc->state != RTW89_ROC_IDLE) { return -EBUSY; } @@ -1407,12 +1377,13 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); + lockdep_assert_wiphy(hw->wiphy); + if (!rtwvif) return -EINVAL; wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); - lockdep_assert_wiphy(hw->wiphy); rtw89_roc_end(rtwdev, rtwvif); return 0; @@ -1435,6 +1406,7 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; lockdep_assert_wiphy(hw->wiphy); + if (sta) rtw89_core_set_tid_config(rtwdev, sta, tid_config); else @@ -1705,12 +1677,12 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; int ret; + lockdep_assert_wiphy(hw->wiphy); + set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work); - lockdep_assert_wiphy(hw->wiphy); ret = rtw89_wow_suspend(rtwdev, wowlan); - if (ret) { rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret); clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); @@ -1726,6 +1698,7 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw) int ret; lockdep_assert_wiphy(hw->wiphy); + ret = rtw89_wow_resume(rtwdev); if (ret) rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret); @@ -1752,14 +1725,14 @@ static void rtw89_set_rekey_data(struct ieee80211_hw *hw, struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct rtw89_wow_gtk_info *gtk_info = &rtw_wow->gtk_info; + lockdep_assert_wiphy(hw->wiphy); + if (data->kek_len > sizeof(gtk_info->kek) || data->kck_len > sizeof(gtk_info->kck)) { rtw89_warn(rtwdev, "kek or kck length over fw limit\n"); return; } - lockdep_assert_wiphy(hw->wiphy); - memcpy(gtk_info->kek, data->kek, data->kek_len); memcpy(gtk_info->kck, data->kck, data->kck_len); } @@ -1773,11 +1746,9 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) /* wl_disable GPIO get floating when entering LPS */ if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) - goto out; + return; rtw89_core_rfkill_poll(rtwdev, false); - -out: } const struct ieee80211_ops rtw89_ops = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 26370eadf77f..e4b690a5febc 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4662,13 +4662,13 @@ void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; lockdep_assert_wiphy(wiphy); + if (!cfo->cfo_trig_by_timer_en) - goto out; + return; rtw89_leave_ps_mode(rtwdev); rtw89_phy_cfo_dm(rtwdev); wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work, msecs_to_jiffies(cfo->cfo_timer_ms)); -out: } static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index cc4b080ecc3c..505a3d0767c6 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -745,7 +745,6 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request struct rtw89_dev *rtwdev = hw->priv; wiphy_lock(wiphy); - lockdep_assert_wiphy(wiphy); rtw89_leave_ps_mode(rtwdev); if (wiphy->regd) { diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 949edb4bd4df..24a32dc4e67d 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -99,7 +99,7 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { typeof(_dev) _d = (_dev); \ BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ - lockdep_assert_held(&_d->mutex); \ + lockdep_assert_wiphy(_d->hw->wiphy); \ _d->sar._cfg_name = *(_cfg_data); \ _d->sar.src = _s; \ } while (0) @@ -239,22 +239,19 @@ static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, const struct rtw89_sar_cfg_common *sar) { enum rtw89_sar_sources src; - int ret = 0; lockdep_assert_wiphy(rtwdev->hw->wiphy); src = rtwdev->sar.src; if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { rtw89_warn(rtwdev, "SAR source: %d is in use", src); - ret = -EBUSY; - goto exit; + return -EBUSY; } rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); rtw89_core_set_chip_txpwr(rtwdev); -exit: - return ret; + return 0; } static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { @@ -290,6 +287,8 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, s32 power; u32 i, idx; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + if (sar->type != NL80211_SAR_TYPE_POWER) return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index b9607bdca6c8..0740e303680c 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -157,7 +157,6 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt) ser_st_name(ser), ser_ev_name(ser, evt)); wiphy_lock(rtwdev->hw->wiphy); - lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_leave_lps(rtwdev); wiphy_unlock(rtwdev->hw->wiphy); @@ -713,7 +712,6 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: wiphy_lock(rtwdev->hw->wiphy); - lockdep_assert_wiphy(rtwdev->hw->wiphy); ser_l2_reset_st_pre_hdl(ser); wiphy_unlock(rtwdev->hw->wiphy); diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index df283a858046..80441e8da60b 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -12,7 +12,7 @@ ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) -/* call this function with rtwdev->mutex is held */ +/* call this function with wiphy mutex is held */ #define rtw89_for_each_rtwvif(rtwdev, rtwvif) \ list_for_each_entry(rtwvif, &(rtwdev)->rtwvifs_list, list) diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 6031633b2709..17eee58503cb 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -604,6 +604,8 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev, struct ieee80211_key_conf *key; u8 sz; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + cipher_info = rtw89_cipher_alg_recognize(cipher); sz = struct_size(rekey_conf, key, cipher_info->len); rekey_conf = kmalloc(sz, GFP_KERNEL); @@ -616,10 +618,6 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev, memcpy(rekey_conf->key, gtk, flex_array_size(rekey_conf, key, cipher_info->len)); - /* ieee80211_gtk_rekey_add() will call set_key(), therefore we - * need to unlock mutex - */ - lockdep_assert_wiphy(rtwdev->hw->wiphy); if (ieee80211_vif_is_mld(wow_vif)) key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, rtwvif_link->link_id); else From 96e395e85b14730ace74a949c03d24e03a95081f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 071/465] wifi: rtw89: remove definition of driver mutex JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2345f351c7f55b97c17d8387a9ff38d4327baf4b Author: Ping-Ke Shih Date: Wed Jan 22 14:03:09 2025 +0800 wifi: rtw89: remove definition of driver mutex No consumers of driver mutex now, so remove definition eventually. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-10-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 2 -- drivers/net/wireless/realtek/rtw89/core.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 4e6d117439a5..559191a49c5a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4831,7 +4831,6 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) return -ENOMEM; spin_lock_init(&rtwdev->ba_lock); spin_lock_init(&rtwdev->rpwm_lock); - mutex_init(&rtwdev->mutex); mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; @@ -4890,7 +4889,6 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev) destroy_workqueue(rtwdev->txq_wq); mutex_destroy(&rtwdev->rf_mutex); - mutex_destroy(&rtwdev->mutex); } EXPORT_SYMBOL(rtw89_core_deinit); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 6970e4d4e73f..61fe2705cac6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5653,8 +5653,6 @@ struct rtw89_dev { struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM]; refcount_t refcount_ap_info; - /* ensures exclusive access from mac80211 callbacks */ - struct mutex mutex; struct list_head rtwvifs_list; /* used to protect rf read write */ struct mutex rf_mutex; From c9d3ff258ae3f1e8b360fbde14cf7c8e40153bd4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 072/465] wifi: rtw89: pci: not assert wiphy_lock to free early_h2c for PCI probe/remove JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8afa4ff99a00cd3f3c8aedd6b92ad6fc077021b7 Author: Ping-Ke Shih Date: Wed Jan 22 14:03:10 2025 +0800 wifi: rtw89: pci: not assert wiphy_lock to free early_h2c for PCI probe/remove Except probe/remove flow, the consumers of early_h2c list are interface start and debugfs. There must be no race between probe/remove flow and interface start. The failed probe flow is to free early_h2c list as well as remove flow, at these two moments debugfs doesn't present. Thus, it is safe to free early_h2c list without held wiphy_lock in these situations. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122060310.31976-11-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/fw.c | 11 ++++++++--- drivers/net/wireless/realtek/rtw89/fw.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 559191a49c5a..da41a417dd7d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4885,7 +4885,7 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev) { rtw89_ser_deinit(rtwdev); rtw89_unload_firmware(rtwdev); - rtw89_fw_free_all_early_h2c(rtwdev); + __rtw89_fw_free_all_early_h2c(rtwdev); destroy_workqueue(rtwdev->txq_wq); mutex_destroy(&rtwdev->rf_mutex); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2982ec367d9d..1afce0a0b905 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6103,12 +6103,10 @@ void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev) } } -void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) { struct rtw89_early_h2c *early_h2c, *tmp; - lockdep_assert_wiphy(rtwdev->hw->wiphy); - list_for_each_entry_safe(early_h2c, tmp, &rtwdev->early_h2c_list, list) { list_del(&early_h2c->list); kfree(early_h2c->h2c); @@ -6116,6 +6114,13 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) } } +void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +{ + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + __rtw89_fw_free_all_early_h2c(rtwdev); +} + static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) { const struct rtw89_c2h_hdr *hdr = (const struct rtw89_c2h_hdr *)c2h->data; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index b621dbed2c61..fb107c0a29d7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4665,6 +4665,7 @@ int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, bool rack, bool dack); int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len); void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev); +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev); void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, u8 macid); From 1ba408b6e6bd55da7a6597eb9640bde029be97c8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 073/465] wifi: rtl8xxxu: Enable AP mode for RTL8192CU (RTL8188CUS) JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4828f572b20bc406f580c66b67c05c1b9ae75085 Author: Ezra Buehler Date: Wed Jan 22 15:15:11 2025 +0800 wifi: rtl8xxxu: Enable AP mode for RTL8192CU (RTL8188CUS) This allows the driver to be used in wireless access point mode on the AT91SAM9G25-based GARDENA smart Gateway. Unfortunately, the data throughput in AP mode appears to be lower than with the vendor driver (or in STA mode). Especially when sending, the data rate is significantly lower. My measurements performed with iperf3 and an Edimax EW-7811Un (VID: 7392, PID: 7811) showed a maximum TX rate of about 4 Mbits/sec compared to the ~24 Mbits/sec measured with the rtl8192cu driver. Although the performance might be good enough for our use case, this is something that should be further looked into. Signed-off-by: Ezra Buehler Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250122071512.10165-1-ezra@easyb.ch Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtl8xxxu/8192c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 0abb1b092bc2..73034e7e41d1 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -644,6 +644,8 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .supports_ap = 1, + .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, From 9d0d92ece9074c256d18b880b7e8df55d60c520b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 074/465] wifi: rtw88: Don't use static local variable in rtw8822b_set_tx_power_index_by_rate JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 00451eb3bec763f708e7e58326468c1e575e5a66 Author: Bitterblue Smith Date: Sun Jan 26 16:03:11 2025 +0200 wifi: rtw88: Don't use static local variable in rtw8822b_set_tx_power_index_by_rate Some users want to plug two identical USB devices at the same time. This static variable could theoretically cause them to use incorrect TX power values. Move the variable to the caller and pass a pointer to it to rtw8822b_set_tx_power_index_by_rate(). Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/8a60f581-0ab5-4d98-a97d-dd83b605008f@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 7f03903ddf4b..23a29019752d 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -935,11 +935,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, } static void -rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, + u8 rs, u32 *phy_pwr_idx) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; - static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; @@ -947,12 +947,12 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; - phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); + *phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, - phy_pwr_idx); - phy_pwr_idx = 0; + *phy_pwr_idx); + *phy_pwr_idx = 0; } } } @@ -960,11 +960,13 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; + u32 phy_pwr_idx = 0; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) - rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs); + rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs, + &phy_pwr_idx); } } From 4c5fbf6c417fd9a98216fa5fb42a6d96476ba007 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 075/465] wifi: rtw88: Don't use static local variable in rtw8821c_set_tx_power_index_by_rate JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0d1d165eff9d6cfad51113e18d9d8c9a8de27d6d Author: Bitterblue Smith Date: Sun Jan 26 16:04:21 2025 +0200 wifi: rtw88: Don't use static local variable in rtw8821c_set_tx_power_index_by_rate Some users want to plug two identical USB devices at the same time. This static variable could theoretically cause them to use incorrect TX power values. Move the variable to the caller and pass a pointer to it to rtw8821c_set_tx_power_index_by_rate(). Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/fe42858c-9b9f-4f03-9aaa-737472c2cd90@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/rtw8821c.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index eb7e34c545d0..cc152248407c 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -680,11 +680,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, } static void -rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, + u8 rs, u32 *phy_pwr_idx) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; - static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; @@ -692,12 +692,12 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; - phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); + *phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3 || rate == DESC_RATEVHT1SS_MCS9) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, - phy_pwr_idx); - phy_pwr_idx = 0; + *phy_pwr_idx); + *phy_pwr_idx = 0; } } } @@ -705,6 +705,7 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; + u32 phy_pwr_idx = 0; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { @@ -712,7 +713,8 @@ static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) if (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S) continue; - rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs); + rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs, + &phy_pwr_idx); } } } From 399fa6d78226aa41209a92de818c7bbf484f32de Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 076/465] wifi: rtl8xxxu: retry firmware download on error JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3d3e28feca7ac8c6cf2a390dbbe1f97e3feb7f36 Author: Soeren Moch Date: Mon Jan 27 20:48:28 2025 +0100 wifi: rtl8xxxu: retry firmware download on error Occasionally there is an EPROTO error during firmware download. This error is converted to EAGAIN in the download function. But nobody tries again and so device probe fails. Implement download retry to fix this. This error was observed (and fix tested) on a tbs2910 board [1] with an embedded RTL8188EU (0bda:8179) device behind a USB hub. [1] arch/arm/boot/dts/nxp/imx/imx6q-tbs2910.dts Signed-off-by: Soeren Moch Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250127194828.599379-1-smoch@web.de Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 4ce0c05c5129..569856ca677f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -860,9 +860,10 @@ rtl8xxxu_writeN(struct rtl8xxxu_priv *priv, u16 addr, u8 *buf, u16 len) return len; write_error: - dev_info(&udev->dev, - "%s: Failed to write block at addr: %04x size: %04x\n", - __func__, addr, blocksize); + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE) + dev_info(&udev->dev, + "%s: Failed to write block at addr: %04x size: %04x\n", + __func__, addr, blocksize); return -EAGAIN; } @@ -4064,8 +4065,14 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); - ret = rtl8xxxu_download_firmware(priv); - dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); + for (int retry = 5; retry >= 0 ; retry--) { + ret = rtl8xxxu_download_firmware(priv); + dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); + if (ret != -EAGAIN) + break; + if (retry) + dev_dbg(dev, "%s: retry firmware download\n", __func__); + } if (ret) goto exit; ret = rtl8xxxu_start_firmware(priv); From 57080139680f50f9b91ebfeffb8d48d67caa43d0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 077/465] wifi: ath12k: update beacon template function to use arvif structure JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4094445969740028f9b637977781b73b64c5bc7c Author: Aditya Kumar Singh Date: Fri Jan 24 11:46:35 2025 +0530 wifi: ath12k: update beacon template function to use arvif structure The current code has ath12k_wmi_bcn_tmpl() accepting separate ar and vdev_id parameters. However, ath12k_link_vif structure can be used to derive both of these. Hence, simplify the function signature. Later change needs arvif pointer access within the function hence it is better if arvif is directly passed now. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124-ath12k_mlo_csa-v2-1-420c42fcfecf@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 5 ++--- drivers/net/wireless/ath/ath12k/wmi.c | 4 +++- drivers/net/wireless/ath/ath12k/wmi.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index e9663c6ac72c..a70b8bdda855 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1671,8 +1671,7 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) ema_args.bcn_cnt = beacons->cnt; ema_args.bcn_index = i; - ret = ath12k_wmi_bcn_tmpl(tx_arvif->ar, tx_arvif->vdev_id, - &beacons->bcn[i].offs, + ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs, beacons->bcn[i].skb, &ema_args); if (ret) { ath12k_warn(tx_arvif->ar->ab, @@ -1766,7 +1765,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) } } - ret = ath12k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, NULL); + ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL); if (ret) ath12k_warn(ab, "failed to submit beacon template command: %d\n", diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 61aa5f509338..7222b82a66bc 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1927,14 +1927,16 @@ int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, return ret; } -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args) { + struct ath12k *ar = arvif->ar; struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_bcn_tmpl_cmd *cmd; struct ath12k_wmi_bcn_prb_info_params *bcn_prb_info; + u32 vdev_id = arvif->vdev_id; struct wmi_tlv *tlv; struct sk_buff *skb; u32 ema_params = 0; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 2934d9589007..02ffc3fa24ee 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5779,7 +5779,7 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, struct sk_buff *frame); int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, const u8 *p2p_ie); -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args); From f6cd7adb456948da59d16ec5993c08f0810eb073 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 078/465] wifi: ath12k: fix handling of CSA offsets in beacon template command JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f9c88d65e805ca06f26806ac99f569a3dfac2229 Author: Aditya Kumar Singh Date: Fri Jan 24 11:46:36 2025 +0530 wifi: ath12k: fix handling of CSA offsets in beacon template command The driver is informed of the counter offsets in the beacon during CSA through the ieee80211_mutable_offsets structure. According to the documentation for the cntdwn_counter_offs member, "This array can contain zero values which should be ignored." However, the current implementation uses these values unconditionally, without checking for zeros. Whenever CSA is active, these offsets are guaranteed to be set. Therefore, add a check for CSA active status before setting the CSA switch count offsets. This ensures that the offsets are only set when CSA is active, preventing incorrect configurations. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124-ath12k_mlo_csa-v2-2-420c42fcfecf@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 2 +- drivers/net/wireless/ath/ath12k/mac.h | 1 + drivers/net/wireless/ath/ath12k/wmi.c | 23 ++++++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a70b8bdda855..cf8b6a1f7ae9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -579,7 +579,7 @@ static int ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id, return 0; } -static struct ieee80211_bss_conf * +struct ieee80211_bss_conf * ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif) { struct ieee80211_vif *vif = arvif->ahvif->vif; diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 1acaf3f68292..5a6e3c3316be 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -111,5 +111,6 @@ void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw, u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones); enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones); enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi); +struct ieee80211_bss_conf *ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif); #endif diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 7222b82a66bc..abffad4fb446 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1934,8 +1934,11 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, { struct ath12k *ar = arvif->ar; struct ath12k_wmi_pdev *wmi = ar->wmi; + struct ath12k_base *ab = ar->ab; struct wmi_bcn_tmpl_cmd *cmd; struct ath12k_wmi_bcn_prb_info_params *bcn_prb_info; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *conf; u32 vdev_id = arvif->vdev_id; struct wmi_tlv *tlv; struct sk_buff *skb; @@ -1944,6 +1947,14 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, int ret, len; size_t aligned_len = roundup(bcn->len, 4); + conf = ath12k_mac_get_link_bss_conf(arvif); + if (!conf) { + ath12k_warn(ab, + "unable to access bss link conf in beacon template command for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + return -EINVAL; + } + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -1955,8 +1966,14 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, sizeof(*cmd)); cmd->vdev_id = cpu_to_le32(vdev_id); cmd->tim_ie_offset = cpu_to_le32(offs->tim_offset); - cmd->csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[0]); - cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]); + + if (conf->csa_active) { + cmd->csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[0]); + cmd->ext_csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[1]); + } + cmd->buf_len = cpu_to_le32(bcn->len); cmd->mbssid_ie_offset = cpu_to_le32(offs->mbssid_off); if (ema_args) { @@ -1986,7 +2003,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, ret = ath12k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID); if (ret) { - ath12k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n"); + ath12k_warn(ab, "failed to send WMI_BCN_TMPL_CMDID\n"); dev_kfree_skb(skb); } From 5793305f2b3db9f7a761e1dccc6e4361de0491c4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 079/465] wifi: ath12k: update the latest CSA counter JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e26a6989b10a3dfc97b8c206023a6bf3a3ccb94e Author: Aditya Kumar Singh Date: Fri Jan 24 11:46:37 2025 +0530 wifi: ath12k: update the latest CSA counter At present, the driver configures the firmware to send the Channel Switch (CS) count event only when the count reaches zero during a Channel Switch Announcement (CSA). For frames managed by the upper layer, where the driver does not update the counter, the CS count in these frames remains unchanged throughout the entire CSA period. This is because the upper layer is not aware of the latest ongoing count. Indicating same count value throughout the CSA time is wrong and could lead to connection instabilities. Fix this by configuring firmware to send CS count event for every count and then accordingly decrementing the count in mac80211. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124-ath12k_mlo_csa-v2-3-420c42fcfecf@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/wmi.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index abffad4fb446..8a6cac6c409f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1972,6 +1972,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, cpu_to_le32(offs->cntdwn_counter_offs[0]); cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]); + cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF); } cmd->buf_len = cpu_to_le32(bcn->len); @@ -7525,17 +7526,15 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, const struct ath12k_wmi_pdev_csa_event *ev, const u32 *vdev_ids) { - int i; + u32 current_switch_count = le32_to_cpu(ev->current_switch_count); + u32 num_vdevs = le32_to_cpu(ev->num_vdevs); struct ieee80211_bss_conf *conf; struct ath12k_link_vif *arvif; struct ath12k_vif *ahvif; - - /* Finish CSA once the switch count becomes NULL */ - if (ev->current_switch_count) - return; + int i; rcu_read_lock(); - for (i = 0; i < le32_to_cpu(ev->num_vdevs); i++) { + for (i = 0; i < num_vdevs; i++) { arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); if (!arvif) { @@ -7558,8 +7557,14 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, continue; } - if (arvif->is_up && conf->csa_active) - ieee80211_csa_finish(ahvif->vif, 0); + if (!arvif->is_up || !conf->csa_active) + continue; + + /* Finish CSA when counter reaches zero */ + if (!current_switch_count) + ieee80211_csa_finish(ahvif->vif, arvif->link_id); + else if (current_switch_count > 1) + ieee80211_beacon_update_cntdwn(ahvif->vif, arvif->link_id); } rcu_read_unlock(); } From 4d211283a07b01b9a058eb429a2075bd0c05896c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:30 +0200 Subject: [PATCH 080/465] wifi: ath12k: prevent CSA counter to reach 0 and hit WARN_ON_ONCE JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 36f002a3e36efd3370f5b6abfc157e7481ecb1e0 Author: Aditya Kumar Singh Date: Fri Jan 24 11:46:38 2025 +0530 wifi: ath12k: prevent CSA counter to reach 0 and hit WARN_ON_ONCE Currently, when the driver receives a channel switch count WMI event from the firmware with a count greater than 1, it calls ieee80211_beacon_update_cntdwn(). If the beacon transmission fails, the event will be received again with the previous count value. In this scenario, the host decrements the mac80211 counter again, causing it to move ahead of the firmware counter. Ultimately, when the firmware count reaches 1, the mac80211 counter will reach zero, triggering a WARN_ON_ONCE(). Therefore, there is a need to check the count value in the event. Hence to fix this, maintain the current ongoing counter in arvif. If the count in the event does not match the expected value, silently discard the event. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124-ath12k_mlo_csa-v2-4-420c42fcfecf@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 2 ++ drivers/net/wireless/ath/ath12k/wmi.c | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 28db100cfac0..407404e7abb7 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -301,6 +301,8 @@ struct ath12k_link_vif { u8 link_id; struct ath12k_vif *ahvif; struct ath12k_rekey_data rekey_data; + + u8 current_cntdown_counter; }; struct ath12k_vif { diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 8a6cac6c409f..b41bafd5143d 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1973,6 +1973,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]); cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF); + arvif->current_cntdown_counter = bcn->data[offs->cntdwn_counter_offs[0]]; } cmd->buf_len = cpu_to_le32(bcn->len); @@ -7561,10 +7562,22 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, continue; /* Finish CSA when counter reaches zero */ - if (!current_switch_count) + if (!current_switch_count) { ieee80211_csa_finish(ahvif->vif, arvif->link_id); - else if (current_switch_count > 1) - ieee80211_beacon_update_cntdwn(ahvif->vif, arvif->link_id); + arvif->current_cntdown_counter = 0; + } else if (current_switch_count > 1) { + /* If the count in event is not what we expect, don't update the + * mac80211 count. Since during beacon Tx failure, count in the + * firmware will not decrement and this event will come with the + * previous count value again + */ + if (current_switch_count != arvif->current_cntdown_counter) + continue; + + arvif->current_cntdown_counter = + ieee80211_beacon_update_cntdwn(ahvif->vif, + arvif->link_id); + } } rcu_read_unlock(); } From 12a9cfee80ea6e0fd2c71d744c14d7baec67d319 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 081/465] wifi: ath12k: Avoid napi_sync() before napi_enable() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 268c73d470a5790a492a2fc2ded084b909d144f3 Author: Avula Sri Charan Date: Fri Jan 24 14:30:58 2025 +0530 wifi: ath12k: Avoid napi_sync() before napi_enable() In case of MHI error a reset work will be queued which will try napi_disable() after napi_synchronize(). As the napi will be only enabled after qmi_firmware_ready event, trying napi_synchronize() before napi_enable() will result in indefinite sleep in case of a firmware crash in QMI init sequence. To avoid this, introduce napi_enabled flag to check if napi is enabled or not before calling napi_synchronize(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Avula Sri Charan Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250124090058.3194299-1-quic_tamizhr@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/pci.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 407404e7abb7..983a5d735ceb 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -168,6 +168,7 @@ struct ath12k_ext_irq_grp { u32 num_irq; u32 grp_id; u64 timestamp; + bool napi_enabled; struct napi_struct napi; struct net_device *napi_ndev; }; diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 6c6a3cabab32..7126cc361def 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -483,8 +483,11 @@ static void __ath12k_pci_ext_irq_disable(struct ath12k_base *ab) ath12k_pci_ext_grp_disable(irq_grp); - napi_synchronize(&irq_grp->napi); - napi_disable(&irq_grp->napi); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } } } @@ -1114,7 +1117,11 @@ void ath12k_pci_ext_irq_enable(struct ath12k_base *ab) for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; - napi_enable(&irq_grp->napi); + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } + ath12k_pci_ext_grp_enable(irq_grp); } From bda3084e64ad567fc7285e0bab1cc63b19d3322b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 082/465] wifi: ath12k: relocate ath12k_mac_ieee80211_sta_bw_to_wmi() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 090c645b2acc835cccf52a8876dccec55629f479 Author: Aditya Kumar Singh Date: Fri Jan 10 00:13:12 2025 +0530 wifi: ath12k: relocate ath12k_mac_ieee80211_sta_bw_to_wmi() An upcoming change will invoke ath12k_mac_ieee80211_sta_bw_to_wmi() from a line located above its current definition. Hence, relocate it to above so that it can be invoked later on. No functionality changes. Compile tested only. Signed-off-by: Aditya Kumar Singh Acked-by: Kalle Valo Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250110-fix_link_sta_bandwidth_update-v1-1-61b6f3ef2ea3@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 62 +++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index cf8b6a1f7ae9..55d4f3fb5cd1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3218,6 +3218,37 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_link_vif *arv ath12k_smps_map[smps]); } +static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, + struct ieee80211_sta *sta) +{ + u32 bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + case IEEE80211_STA_RX_BW_320: + bw = WMI_PEER_CHWIDTH_320MHZ; + break; + default: + ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", + sta->deflink.bandwidth, sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + return bw; +} + static void ath12k_bss_assoc(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *bss_conf) @@ -5495,37 +5526,6 @@ exit: return ret; } -static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, - struct ieee80211_sta *sta) -{ - u32 bw = WMI_PEER_CHWIDTH_20MHZ; - - switch (sta->deflink.bandwidth) { - case IEEE80211_STA_RX_BW_20: - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - case IEEE80211_STA_RX_BW_40: - bw = WMI_PEER_CHWIDTH_40MHZ; - break; - case IEEE80211_STA_RX_BW_80: - bw = WMI_PEER_CHWIDTH_80MHZ; - break; - case IEEE80211_STA_RX_BW_160: - bw = WMI_PEER_CHWIDTH_160MHZ; - break; - case IEEE80211_STA_RX_BW_320: - bw = WMI_PEER_CHWIDTH_320MHZ; - break; - default: - ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", - sta->deflink.bandwidth, sta->addr); - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - } - - return bw; -} - static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, struct ath12k_sta *ahsta, struct ath12k_link_sta *arsta, From 4e6a589aefa1f6867ac77c6088e8ed720e504c0a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 083/465] wifi: ath12k: handle ath12k_mac_ieee80211_sta_bw_to_wmi() for link sta JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2fc98b24adb93457254ee16418cffa89de038ad9 Author: Aditya Kumar Singh Date: Fri Jan 10 00:13:13 2025 +0530 wifi: ath12k: handle ath12k_mac_ieee80211_sta_bw_to_wmi() for link sta Currently ath12k_mac_ieee80211_sta_bw_to_wmi() handles the bandwidth from sta's deflink member. This works only for non-ML station. Now that MLO support is there, extend this function to use link sta instead of deflink. Additionally, in ath12k_mac_handle_link_sta_state(), the link sta structure is not accessible, making it difficult to fetch the bandwidth there. However, ath12k_mac_station_assoc() does reference the link sta structure. Therefore, move the initial assignment of the arsta bandwidth member to ath12k_mac_station_assoc(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250110-fix_link_sta_bandwidth_update-v1-2-61b6f3ef2ea3@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 55d4f3fb5cd1..aadace85dc11 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3219,11 +3219,11 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_link_vif *arv } static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { - u32 bw = WMI_PEER_CHWIDTH_20MHZ; + u32 bw; - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_20: bw = WMI_PEER_CHWIDTH_20MHZ; break; @@ -3240,8 +3240,8 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, bw = WMI_PEER_CHWIDTH_320MHZ; break; default: - ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", - sta->deflink.bandwidth, sta->addr); + ath12k_warn(ar->ab, "Invalid bandwidth %d for link station %pM\n", + link_sta->bandwidth, link_sta->addr); bw = WMI_PEER_CHWIDTH_20MHZ; break; } @@ -5020,6 +5020,11 @@ static int ath12k_mac_station_assoc(struct ath12k *ar, return -EINVAL; } + spin_lock_bh(&ar->data_lock); + arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); + arsta->bw_prev = link_sta->bandwidth; + spin_unlock_bh(&ar->data_lock); + if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) { ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask, band); @@ -5609,7 +5614,6 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, enum ieee80211_sta_state new_state) { struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); - struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ath12k *ar = arvif->ar; int ret = 0; @@ -5652,13 +5656,6 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, ath12k_warn(ar->ab, "Failed to associate station: %pM\n", arsta->addr); - spin_lock_bh(&ar->data_lock); - - arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); - arsta->bw_prev = sta->deflink.bandwidth; - - spin_unlock_bh(&ar->data_lock); - /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as * authorized */ @@ -5926,7 +5923,7 @@ static void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); if (changed & IEEE80211_RC_BW_CHANGED) { - bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); arsta->bw_prev = arsta->bw; arsta->bw = bw; } From 3410a02455bee2132115dc344dceaebaee8073b9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 084/465] wifi: ath12k: Add support for obtaining the buffer type ACPI function bitmap JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b59d1f8207de4e2ec763a5e58f95811d0eb2272c Author: Lingbo Kong Date: Mon Jan 13 15:48:07 2025 +0800 wifi: ath12k: Add support for obtaining the buffer type ACPI function bitmap Currently, ath12k does not support obtaining the buffer type ACPI function bitmap. To solve this issue, change the code to support obtaining the buffer type ACPI function bitmap. This patch will not affect QCN9274, because only WCN7850 supports ACPI. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250113074810.29729-2-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/acpi.c | 18 ++++++++++++++++-- drivers/net/wireless/ath/ath12k/acpi.h | 5 ++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index 0555d35aab47..b6935ab1655e 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -12,7 +12,7 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) { union acpi_object *obj; acpi_handle root_handle; - int ret; + int ret, i; root_handle = ACPI_HANDLE(ab->dev); if (!root_handle) { @@ -32,6 +32,20 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) ab->acpi.func_bit = obj->integer.value; } else if (obj->type == ACPI_TYPE_BUFFER) { switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + if (obj->buffer.length < ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE || + obj->buffer.length > ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM func size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + ab->acpi.func_bit = 0; + for (i = 0; i < obj->buffer.length; i++) + ab->acpi.func_bit += obj->buffer.pointer[i] << (i * 8); + + break; case ATH12K_ACPI_DSM_FUNC_TAS_CFG: if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_CFG_SIZE) { ath12k_warn(ab, "invalid ACPI DSM TAS config size: %d\n", diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 39e003d86a48..6ccc26250aaf 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_ACPI_H #define ATH12K_ACPI_H @@ -48,6 +48,9 @@ #define ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE 100 #define ATH12K_ACPI_DSM_TAS_CFG_SIZE 108 +#define ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE 1 +#define ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE 4 + #define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \ ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN) #define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \ From ff490de5d5f858d3d0abad890b15539f5a4311fe Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 085/465] wifi: ath12k: Add Support for enabling or disabling specific features based on ACPI bitflag JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c6a7c0b09d5f430c36f860af1032e9dfa2dfcdc5 Author: Lingbo Kong Date: Mon Jan 13 15:48:08 2025 +0800 wifi: ath12k: Add Support for enabling or disabling specific features based on ACPI bitflag Currently, ath12k does not support enable or disable specific features by ACPI bitflag. To address this issue, obtain the ACPI bitflag value and use it to selectively enable or disable specific features. This patch will not affect QCN9274, because only WCN7850 supports ACPI. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250113074810.29729-3-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/acpi.c | 41 ++++++++++++++++++++++++-- drivers/net/wireless/ath/ath12k/acpi.h | 18 +++++++++++ drivers/net/wireless/ath/ath12k/core.c | 3 ++ drivers/net/wireless/ath/ath12k/core.h | 3 ++ drivers/net/wireless/ath/ath12k/mac.c | 3 +- 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index b6935ab1655e..186ef5d62813 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -29,7 +29,14 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) } if (obj->type == ACPI_TYPE_INTEGER) { - ab->acpi.func_bit = obj->integer.value; + switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + ab->acpi.func_bit = obj->integer.value; + break; + case ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG: + ab->acpi.bit_flag = obj->integer.value; + break; + } } else if (obj->type == ACPI_TYPE_BUFFER) { switch (func) { case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: @@ -261,24 +268,52 @@ static int ath12k_acpi_set_tas_params(struct ath12k_base *ab) return 0; } +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_rfkill; +} + +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_11be; +} + int ath12k_acpi_start(struct ath12k_base *ab) { acpi_status status; u8 *buf; int ret; + ab->acpi.acpi_tas_enable = false; + ab->acpi.acpi_disable_11be = false; + ab->acpi.acpi_disable_rfkill = false; + if (!ab->hw_params->acpi_guid) /* not supported with this hardware */ return 0; - ab->acpi.acpi_tas_enable = false; - ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS); if (ret) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to get ACPI DSM data: %d\n", ret); return ret; } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG); + if (ret) { + ath12k_warn(ab, "failed to get ACPI DISABLE FLAG: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_11BE_BIT)) + ab->acpi.acpi_disable_11be = true; + + if (!ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT)) + ab->acpi.acpi_disable_rfkill = true; + } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) { ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG); if (ret) { diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 6ccc26250aaf..242ea88fb6c2 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -9,6 +9,7 @@ #include #define ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS 0 +#define ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG 2 #define ATH12K_ACPI_DSM_FUNC_BIOS_SAR 4 #define ATH12K_ACPI_DSM_FUNC_GEO_OFFSET 5 #define ATH12K_ACPI_DSM_FUNC_INDEX_CCA 6 @@ -16,6 +17,7 @@ #define ATH12K_ACPI_DSM_FUNC_TAS_DATA 9 #define ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE 10 +#define ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG BIT(1) #define ATH12K_ACPI_FUNC_BIT_BIOS_SAR BIT(3) #define ATH12K_ACPI_FUNC_BIT_GEO_OFFSET BIT(4) #define ATH12K_ACPI_FUNC_BIT_CCA BIT(5) @@ -25,6 +27,7 @@ #define ATH12K_ACPI_NOTIFY_EVENT 0x86 #define ATH12K_ACPI_FUNC_BIT_VALID(_acdata, _func) (((_acdata).func_bit) & (_func)) +#define ATH12K_ACPI_CHEK_BIT_VALID(_acdata, _func) (((_acdata).bit_flag) & (_func)) #define ATH12K_ACPI_TAS_DATA_VERSION 0x1 #define ATH12K_ACPI_TAS_DATA_ENABLE 0x1 @@ -51,6 +54,9 @@ #define ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE 1 #define ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE 4 +#define ATH12K_ACPI_DSM_DISABLE_11BE_BIT BIT(0) +#define ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT BIT(2) + #define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \ ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN) #define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \ @@ -62,6 +68,8 @@ int ath12k_acpi_start(struct ath12k_base *ab); void ath12k_acpi_stop(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab); #else @@ -74,6 +82,16 @@ static inline void ath12k_acpi_stop(struct ath12k_base *ab) { } +static inline bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return false; +} + +static inline bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return false; +} + #endif /* CONFIG_ACPI */ #endif /* ATH12K_ACPI_H */ diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 60c077b016b4..6d09a51a1571 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -40,6 +40,9 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) return 0; + if (ath12k_acpi_get_disable_rfkill(ab)) + return 0; + for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 983a5d735ceb..fd13c4e53ef2 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1058,6 +1058,9 @@ struct ath12k_base { u32 func_bit; bool acpi_tas_enable; bool acpi_bios_sar_enable; + bool acpi_disable_11be; + bool acpi_disable_rfkill; + u32 bit_flag; u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE]; u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE]; u8 bios_sar_data[ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE]; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index aadace85dc11..16e6f2fae943 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6751,7 +6751,8 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar, memset(eht_cap, 0, sizeof(struct ieee80211_sta_eht_cap)); - if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map))) + if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)) || + ath12k_acpi_get_disable_11be(ar->ab)) return; eht_cap->has_eht = true; From 68afc72b42bd87c180aa63203db25491d47cfa0f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 086/465] wifi: ath12k: Adjust the timing to access ACPI table JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 33fdeb544ea5966abe247b9eb96a8017f7b9b2f2 Author: Lingbo Kong Date: Mon Jan 13 15:48:09 2025 +0800 wifi: ath12k: Adjust the timing to access ACPI table Currently, the timing for accessing the ACPI table is inappropriate. Due to special ACPI requirements, the ACPI table must be obtained before downloading the board data file. Therefore, adjust the timing for accessing the ACPI table accordingly. This patch will not affect QCN9274, because only WCN7850 supports ACPI. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250113074810.29729-4-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/acpi.c | 98 ++++++++++++++++---------- drivers/net/wireless/ath/ath12k/acpi.h | 5 ++ drivers/net/wireless/ath/ath12k/core.c | 5 +- drivers/net/wireless/ath/ath12k/core.h | 2 + drivers/net/wireless/ath/ath12k/qmi.c | 7 +- 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index 186ef5d62813..231b4eb92a5d 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -278,15 +278,69 @@ bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) return ab->acpi.acpi_disable_11be; } +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ + int ret; + u8 *buf; + + if (!ab->hw_params->acpi_guid) + /* not supported with this hardware */ + return; + + if (ab->acpi.acpi_tas_enable) { + ret = ath12k_acpi_set_tas_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS parameters: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_bios_sar_enable) { + ret = ath12k_acpi_set_bios_sar_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI BIOS SAR: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_cca_enable) { + buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET; + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE, + buf, + ATH12K_ACPI_CCA_THR_OFFSET_LEN); + if (ret) { + ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n", + ret); + return; + } + } + + if (ab->acpi.acpi_band_edge_enable) { + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_TYPE_BANDEDGE, + ab->acpi.band_edge_power, + sizeof(ab->acpi.band_edge_power)); + if (ret) { + ath12k_warn(ab, + "failed to set ACPI DSM band edge channel power: %d\n", + ret); + return; + } + } +} + int ath12k_acpi_start(struct ath12k_base *ab) { acpi_status status; - u8 *buf; int ret; ab->acpi.acpi_tas_enable = false; ab->acpi.acpi_disable_11be = false; ab->acpi.acpi_disable_rfkill = false; + ab->acpi.acpi_bios_sar_enable = false; + ab->acpi.acpi_cca_enable = false; + ab->acpi.acpi_band_edge_enable = false; if (!ab->hw_params->acpi_guid) /* not supported with this hardware */ @@ -357,20 +411,6 @@ int ath12k_acpi_start(struct ath12k_base *ab) ab->acpi.acpi_bios_sar_enable = true; } - if (ab->acpi.acpi_tas_enable) { - ret = ath12k_acpi_set_tas_params(ab); - if (ret) { - ath12k_warn(ab, "failed to send ACPI parameters: %d\n", ret); - return ret; - } - } - - if (ab->acpi.acpi_bios_sar_enable) { - ret = ath12k_acpi_set_bios_sar_params(ab); - if (ret) - return ret; - } - if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_CCA)) { ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_CCA); if (ret) { @@ -381,18 +421,8 @@ int ath12k_acpi_start(struct ath12k_base *ab) if (ab->acpi.cca_data[0] == ATH12K_ACPI_CCA_THR_VERSION && ab->acpi.cca_data[ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET] == - ATH12K_ACPI_CCA_THR_ENABLE_FLAG) { - buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET; - ret = ath12k_wmi_set_bios_cmd(ab, - WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE, - buf, - ATH12K_ACPI_CCA_THR_OFFSET_LEN); - if (ret) { - ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n", - ret); - return ret; - } - } + ATH12K_ACPI_CCA_THR_ENABLE_FLAG) + ab->acpi.acpi_cca_enable = true; } if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, @@ -405,18 +435,8 @@ int ath12k_acpi_start(struct ath12k_base *ab) } if (ab->acpi.band_edge_power[0] == ATH12K_ACPI_BAND_EDGE_VERSION && - ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) { - ret = ath12k_wmi_set_bios_cmd(ab, - WMI_BIOS_PARAM_TYPE_BANDEDGE, - ab->acpi.band_edge_power, - sizeof(ab->acpi.band_edge_power)); - if (ret) { - ath12k_warn(ab, - "failed to set ACPI DSM band edge channel power: %d\n", - ret); - return ret; - } - } + ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) + ab->acpi.acpi_band_edge_enable = true; } status = acpi_install_notify_handler(ACPI_HANDLE(ab->dev), diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 242ea88fb6c2..19a1a1118e82 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -70,6 +70,7 @@ int ath12k_acpi_start(struct ath12k_base *ab); void ath12k_acpi_stop(struct ath12k_base *ab); bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab); bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab); +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab); #else @@ -92,6 +93,10 @@ static inline bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) return false; } +static inline void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ +} + #endif /* CONFIG_ACPI */ #endif /* ATH12K_ACPI_H */ diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 6d09a51a1571..323bd5082a7d 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -849,10 +849,7 @@ static int ath12k_core_start(struct ath12k_base *ab) goto err_reo_cleanup; } - ret = ath12k_acpi_start(ab); - if (ret) - /* ACPI is optional so continue in case of an error */ - ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", ret); + ath12k_acpi_set_dsm_func(ab); if (!test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) /* Indicate the core start in the appropriate group */ diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index fd13c4e53ef2..79ffaa62bd2f 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1060,6 +1060,8 @@ struct ath12k_base { bool acpi_bios_sar_enable; bool acpi_disable_11be; bool acpi_disable_rfkill; + bool acpi_cca_enable; + bool acpi_band_edge_enable; u32 bit_flag; u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE]; u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE]; diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 5c3563383fab..c43f773c66ba 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2740,6 +2740,11 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) if (r) ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); + r = ath12k_acpi_start(ab); + if (r) + /* ACPI is optional so continue in case of an error */ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r); + out: return ret; } From a9c2a6b64eafd6bf1844eab0b82026dd9d0237b1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 087/465] wifi: ath12k: Add support for reading variant from ACPI to download board data file JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0a43c3a520e96d086d0aee492bbcf73ba737a637 Author: Lingbo Kong Date: Mon Jan 13 15:48:10 2025 +0800 wifi: ath12k: Add support for reading variant from ACPI to download board data file Currently, ath12k does not support reading variant from ACPI board data filename extension for downloading board data file. To address this issue, obtain the string of the ACPI data filename extension and use it as part of the string to search for the board data file from board-2.bin. This patch will not affect QCN9274, because only WCN7850 supports ACPI. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Link: https://patch.msgid.link/20250113074810.29729-5-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/acpi.c | 45 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/acpi.h | 12 +++++++ drivers/net/wireless/ath/ath12k/core.h | 2 ++ drivers/net/wireless/ath/ath12k/qmi.c | 4 +++ 4 files changed, 63 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index 231b4eb92a5d..d81367ce6929 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -37,6 +37,24 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) ab->acpi.bit_flag = obj->integer.value; break; } + } else if (obj->type == ACPI_TYPE_STRING) { + switch (func) { + case ATH12K_ACPI_DSM_FUNC_BDF_EXT: + if (obj->string.length <= ATH12K_ACPI_BDF_ANCHOR_STRING_LEN || + obj->string.length > ATH12K_ACPI_BDF_MAX_LEN || + memcmp(obj->string.pointer, ATH12K_ACPI_BDF_ANCHOR_STRING, + ATH12K_ACPI_BDF_ANCHOR_STRING_LEN)) { + ath12k_warn(ab, "invalid ACPI DSM BDF size: %d\n", + obj->string.length); + ret = -EINVAL; + goto out; + } + + memcpy(ab->acpi.bdf_string, obj->string.pointer, + obj->buffer.length); + + break; + } } else if (obj->type == ACPI_TYPE_BUFFER) { switch (func) { case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: @@ -341,6 +359,8 @@ int ath12k_acpi_start(struct ath12k_base *ab) ab->acpi.acpi_bios_sar_enable = false; ab->acpi.acpi_cca_enable = false; ab->acpi.acpi_band_edge_enable = false; + ab->acpi.acpi_enable_bdf = false; + ab->acpi.bdf_string[0] = '\0'; if (!ab->hw_params->acpi_guid) /* not supported with this hardware */ @@ -368,6 +388,16 @@ int ath12k_acpi_start(struct ath12k_base *ab) ab->acpi.acpi_disable_rfkill = true; } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BDF_EXT)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BDF_EXT); + if (ret || ab->acpi.bdf_string[0] == '\0') { + ath12k_warn(ab, "failed to get ACPI BDF EXT: %d\n", ret); + return ret; + } + + ab->acpi.acpi_enable_bdf = true; + } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) { ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG); if (ret) { @@ -452,6 +482,21 @@ int ath12k_acpi_start(struct ath12k_base *ab) return 0; } +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + size_t max_len = sizeof(ab->qmi.target.bdf_ext); + + if (!ab->acpi.acpi_enable_bdf) + return -ENODATA; + + if (strscpy(ab->qmi.target.bdf_ext, ab->acpi.bdf_string + 4, max_len) < 0) + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "acpi bdf variant longer than the buffer (variant: %s)\n", + ab->acpi.bdf_string); + + return 0; +} + void ath12k_acpi_stop(struct ath12k_base *ab) { if (!ab->acpi.started) diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 19a1a1118e82..3a26fea6af1a 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -10,6 +10,7 @@ #define ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS 0 #define ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG 2 +#define ATH12K_ACPI_DSM_FUNC_BDF_EXT 3 #define ATH12K_ACPI_DSM_FUNC_BIOS_SAR 4 #define ATH12K_ACPI_DSM_FUNC_GEO_OFFSET 5 #define ATH12K_ACPI_DSM_FUNC_INDEX_CCA 6 @@ -18,6 +19,7 @@ #define ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE 10 #define ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG BIT(1) +#define ATH12K_ACPI_FUNC_BIT_BDF_EXT BIT(2) #define ATH12K_ACPI_FUNC_BIT_BIOS_SAR BIT(3) #define ATH12K_ACPI_FUNC_BIT_GEO_OFFSET BIT(4) #define ATH12K_ACPI_FUNC_BIT_CCA BIT(5) @@ -57,6 +59,10 @@ #define ATH12K_ACPI_DSM_DISABLE_11BE_BIT BIT(0) #define ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT BIT(2) +#define ATH12K_ACPI_BDF_ANCHOR_STRING_LEN 3 +#define ATH12K_ACPI_BDF_ANCHOR_STRING "BDF" +#define ATH12K_ACPI_BDF_MAX_LEN 100 + #define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \ ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN) #define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \ @@ -71,6 +77,7 @@ void ath12k_acpi_stop(struct ath12k_base *ab); bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab); bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab); void ath12k_acpi_set_dsm_func(struct ath12k_base *ab); +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab); #else @@ -97,6 +104,11 @@ static inline void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) { } +static inline int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + return 0; +} + #endif /* CONFIG_ACPI */ #endif /* ATH12K_ACPI_H */ diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 79ffaa62bd2f..c34a14404865 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1062,7 +1062,9 @@ struct ath12k_base { bool acpi_disable_rfkill; bool acpi_cca_enable; bool acpi_band_edge_enable; + bool acpi_enable_bdf; u32 bit_flag; + char bdf_string[ATH12K_ACPI_BDF_MAX_LEN]; u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE]; u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE]; u8 bios_sar_data[ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE]; diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index c43f773c66ba..ef537c6bf0da 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2745,6 +2745,10 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) /* ACPI is optional so continue in case of an error */ ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r); + r = ath12k_acpi_check_bdf_variant_name(ab); + if (r) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "ACPI bdf variant name not set.\n"); + out: return ret; } From 6c7ba7d0e4026305af8d9965db42bcf4a0dc5a50 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 088/465] wifi: ath11k: add support for MU EDCA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b78c02f7c7104f1e77ade12ebde267e6fb388ca9 Author: Yu Zhang(Yuriy) Date: Fri Jan 24 14:13:43 2025 +0800 wifi: ath11k: add support for MU EDCA The current code does not have the MU EDCA feature, so it cannot support the use of EDCA by STA in specific UL MU HE TB PPDU transmissions. Refer to IEEE Std 802.11ax-2021 "9.4.2.251 MU EDCA Parameter Set element", "26.2.7 EDCA operation using MU EDCA parameters". Add ath11k_mac_op_conf_tx_mu_edca() to construct the MU EDCA parameters received from mac80211 into WMI WMM parameters,and send to the firmware according to the different WMM type flags. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04523-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Signed-off-by: Yu Zhang (Yuriy) Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250124061343.2263467-1-quic_yuzha@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/core.h | 3 +- drivers/net/wireless/ath/ath11k/mac.c | 53 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath11k/wmi.c | 11 +++--- drivers/net/wireless/ath/ath11k/wmi.h | 10 ++++- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index a9dc7fe7765a..92b14fc43cb6 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_CORE_H @@ -370,6 +370,7 @@ struct ath11k_vif { struct ieee80211_vif *vif; struct wmi_wmm_params_all_arg wmm_params; + struct wmi_wmm_params_all_arg muedca_params; struct list_head list; union { struct { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 1556392f7ad4..b76c44cdabbc 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -5204,6 +5204,45 @@ exit: return ret; } +static int ath11k_mac_op_conf_tx_mu_edca(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k *ar = hw->priv; + struct wmi_wmm_params_arg *p; + int ret; + + switch (ac) { + case IEEE80211_AC_VO: + p = &arvif->muedca_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &arvif->muedca_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &arvif->muedca_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &arvif->muedca_params.ac_bk; + break; + default: + ath11k_warn(ar->ab, "error ac: %d", ac); + return -EINVAL; + } + + p->cwmin = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(3, 0)); + p->cwmax = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(7, 4)); + p->aifs = u8_get_bits(params->mu_edca_param_rec.aifsn, GENMASK(3, 0)); + p->txop = params->mu_edca_param_rec.mu_edca_timer; + + ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, + &arvif->muedca_params, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA); + return ret; +} + static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, u16 ac, @@ -5242,12 +5281,22 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, p->txop = params->txop; ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, - &arvif->wmm_params); + &arvif->wmm_params, + WMI_WMM_PARAM_TYPE_LEGACY); if (ret) { ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret); goto exit; } + if (params->mu_edca) { + ret = ath11k_mac_op_conf_tx_mu_edca(hw, vif, link_id, ac, + params); + if (ret) { + ath11k_warn(ar->ab, "failed to set mu_edca params: %d\n", ret); + goto exit; + } + } + ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 87abfa547529..d7f852bebf4a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -2662,7 +2662,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, } int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param) + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type) { struct ath11k_pdev_wmi *wmi = ar->wmi; struct wmi_vdev_set_wmm_params_cmd *cmd; @@ -2681,7 +2682,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); cmd->vdev_id = vdev_id; - cmd->wmm_param_type = 0; + cmd->wmm_param_type = wmm_param_type; for (ac = 0; ac < WME_NUM_AC; ac++) { switch (ac) { @@ -2714,8 +2715,8 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, wmm_param->no_ack = wmi_wmm_arg->no_ack; ath11k_dbg(ar->ab, ATH11K_DBG_WMI, - "wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", - ac, wmm_param->aifs, wmm_param->cwmin, + "wmm set type %d ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", + wmm_param_type, ac, wmm_param->aifs, wmm_param->cwmin, wmm_param->cwmax, wmm_param->txoplimit, wmm_param->acm, wmm_param->no_ack); } diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 8982b909c821..508bd496488a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_WMI_H @@ -6346,6 +6346,11 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +enum wmi_wmm_params_type { + WMI_WMM_PARAM_TYPE_LEGACY = 0, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA = 1, +}; + const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, struct sk_buff *skb, gfp_t gfp); int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, @@ -6402,7 +6407,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct scan_cancel_param *param); int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param); + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type); int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, u32 pdev_id); int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id); From 678f641d00307935c008fb560e43bae611318b31 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:31 +0200 Subject: [PATCH 089/465] wifi: ath11k: fix wrong overriding for VHT Beamformee STS Capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9d13950acb2a51342c93c69f1a5bf285adb90d88 Author: Yu Zhang(Yuriy) Date: Fri Jan 24 15:59:53 2025 +0800 wifi: ath11k: fix wrong overriding for VHT Beamformee STS Capability Current code in ath11k_mac_set_txbf_conf overrides nsts, which is incorrect as it confuses nss and nsts. nss is Number of Spatial Streams,nsts is Number of Space-Time Streams. As mentioned in Fixes: 55b5ee3357d7, the nss used when acting as a beamformee in VHT mode should be reported by the firmware and should not be greater than the number of receiving antennas - 1. The num_rx_chains related nss rather than nsts. If STBC is enabled, nsts is greater than nss. About nss are mapped to nsts, refer to IEEE Std 802.11-2020: 19.3.11.9.2 Space-time block coding (STBC), Table 19-18—Constellation mapper output to spatial mapper input for STBC. Remove wrong overriding for nsts of VHT Beamformee STS Capability, acting DL MU-MIMO in VHT mode is working properly. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04479-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Fixes: 55b5ee3357d7 ("wifi: ath11k: fix number of VHT beamformee spatial streams") Signed-off-by: Yu Zhang (Yuriy) Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250124075953.2282354-1-quic_yuzha@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/mac.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index b76c44cdabbc..586898e5318f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5385,8 +5385,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); } @@ -5470,9 +5468,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) /* Enable Beamformee STS Field only if SU BF is enabled */ if (subfee) { - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; - nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; *vht_cap |= nsts; From 919ee53384f2b9db52cdf72b4377889ef32ed535 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 090/465] wifi: ath12k: Dump PDEV transmit rate HTT stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ba42b22aa336c3ea0bd6f9c606f70a35a278bd61 Author: Lingbo Kong Date: Mon Jan 13 15:17:56 2025 +0800 wifi: ath12k: Dump PDEV transmit rate HTT stats Support to dump PDEV transmit rate stats through HTT debugfs stats type 9. Sample output: ------------------------- echo 9 > /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats HTT_TX_PDEV_RATE_STATS_TLV: mac_id = 0 tx_ldpc = 1088 ac_mu_mimo_tx_ldpc = 0 ax_mu_mimo_tx_ldpc = 0 ofdma_tx_ldpc = 0 rts_cnt = 941 rts_success = 180 ack_rssi = 4294967168 Legacy CCK Rates: 1 Mbps: 830, 2 Mbps: 0, 5.5 Mbps: 0, 12 Mbps: 0 Legacy OFDM Rates: 6 Mbps: 942, 9 Mbps: 0, 12 Mbps: 0, 18 Mbps: 0 24 Mbps: 0, 36 Mbps: 0, 48 Mbps: 0, 54 Mbps: 0 HE LTF: 1x: 0, 2x: 957, 4x: 132 tx_mcs = 0:342, 1:260, 2:171, 3:148, 4:31, 5:34, 6:93, 7:10, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 ax_mu_mimo_tx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ofdma_tx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_nss = 1:754, 2:335, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ac_mu_mimo_tx_nss = 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ax_mu_mimo_tx_nss = 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ofdma_tx_nss = 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 tx_bw = 0:1089, 1:0, 2:0, 3:0, 4:0 tx_stbc = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_gi[0] = 0:210, 1:260, 2:171, 3:148, 4:31, 5:34, 6:93, 7:10, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_gi[3] = 0:132, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ac_mu_mimo_tx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ac_mu_mimo_tx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ac_mu_mimo_tx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ac_mu_mimo_tx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ax_mu_mimo_tx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ax_mu_mimo_tx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ax_mu_mimo_tx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ax_mu_mimo_tx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ofdma_tx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ofdma_tx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ofdma_tx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ofdma_tx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 tx_su_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 tx_mu_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ac_mu_mimo_tx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ac_mu_mimo_tx_bw = 0:0, 1:0, 2:0, 3:0 ax_mu_mimo_tx_bw = 0:0, 1:0, 2:0, 3:0 ofdma_tx_bw = 0:0, 1:0, 2:0, 3:0 tx_pream = 0:942, 1:830, 2:0, 3:0, 4:1850, 5:0, 6:0 tx_dcm = 0:131, 1:0, 2:0, 3:0, 4:0 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250113071758.19589-2-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 206 +++++++++++++++++- .../wireless/ath/ath12k/debugfs_htt_stats.h | 70 +++++- 2 files changed, 274 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 41e4ef2ef3af..29a79c800d54 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -3812,6 +3812,207 @@ ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(const void *tag_buf, u16 tag_l stats_req->buf_len = len; } +static inline void +ath12k_htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ofdma_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rts_success = %u\n", + le32_to_cpu(htt_stats_buf->rts_success)); + len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", + le32_to_cpu(htt_stats_buf->ack_rssi)); + len += scnprintf(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 12 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[3])); + len += scnprintf(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[3]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[4]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[5]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[6]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[7])); + len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n", + le32_to_cpu(htt_stats_buf->tx_he_ltf[1]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[2]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[3])); + + len += print_array_to_buf(buf, len, "tx_mcs", htt_stats_buf->tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext[j])); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS + + ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_mcs", + htt_stats_buf->ax_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ofdma_tx_mcs", + htt_stats_buf->ofdma_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofdma_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ofdma_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "tx_bw", htt_stats_buf->tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, NULL); + len += scnprintf(buf + len, buf_len - len, ", %u:%u\n", + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_bw_320mhz)); + + len += print_array_to_buf(buf, len, "tx_stbc", + htt_stats_buf->tx_stbc, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_stbc_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ac_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ac_mu_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ax_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ax_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ofdma_tx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ofdma_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofd_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + len += print_array_to_buf(buf, len, "tx_su_mcs", htt_stats_buf->tx_su_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_mu_mcs", htt_stats_buf->tx_mu_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_mcs", + htt_stats_buf->ac_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_bw", + htt_stats_buf->ac_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_bw", + htt_stats_buf->ax_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ofdma_tx_bw", + htt_stats_buf->ofdma_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_pream", htt_stats_buf->tx_pream, + ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "tx_dcm", htt_stats_buf->tx_dcm, + ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -4047,6 +4248,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_tx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 4b59976fbc35..510d347242d2 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef DEBUG_HTT_STATS_H @@ -129,6 +129,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, @@ -173,6 +174,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, HTT_STATS_SFM_CMN_TAG = 26, HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, HTT_STATS_TX_SCHED_CMN_TAG = 37, HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, @@ -387,6 +389,72 @@ struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv { __le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; } __packed; +#define ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LTF 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES 6 + +struct ath12k_htt_tx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 tx_ldpc; + __le32 rts_cnt; + __le32 ack_rssi; + __le32 tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_mu_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 tx_stbc[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_pream[ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + __le32 tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_dcm[ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rts_success; + __le32 tx_legacy_cck_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 tx_legacy_ofdm_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 ac_mu_mimo_tx_ldpc; + __le32 ax_mu_mimo_tx_ldpc; + __le32 ofdma_tx_ldpc; + __le32 tx_he_ltf[ATH12K_HTT_TX_PDEV_STATS_NUM_LTF]; + __le32 ac_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ac_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ax_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ofdma_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ac_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ax_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ofdma_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ac_mu_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 trigger_type_11ax[ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES]; + __le32 tx_11ax_su_ext; + __le32 tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_stbc_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofdma_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofd_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_mcs_ext_2[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 tx_bw_320mhz; +}; + #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) From 17682d129273ead3b43ece0b4a5259f1e830dcbd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 091/465] wifi: ath12k: Dump PDEV receive rate HTT stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a24cd7583003824f8bd0034c02987e5da26088f6 Author: Lingbo Kong Date: Mon Jan 13 15:17:57 2025 +0800 wifi: ath12k: Dump PDEV receive rate HTT stats Support to dump PDEV receive rate stats through HTT debugfs stats type 10. Sample output: ----------------- echo 10 > /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats HTT_RX_PDEV_RATE_STATS_TLV: mac_id = 0 nsts = 0 rx_ldpc = 96 rts_cnt = 0 rssi_mgmt = 4294967240 rssi_data = 4294967250 rssi_comb = 4294967239 rssi_in_dbm = -46 rx_evm_nss_count = 0 rx_evm_pilot_count = 0 rx_11ax_su_ext = 0 rx_11ac_mumimo = 0 rx_11ax_mumimo = 0 rx_11ax_ofdma = 0 txbf = 0 rx_su_ndpa = 0 rx_mu_ndpa = 0 rx_br_poll = 0 rx_active_dur_us_low = 1000106 rx_active_dur_us_high = 0 rx_11ax_ul_ofdma = 0 ul_ofdma_rx_stbc = 0 ul_ofdma_rx_ldpc = 0 per_chain_rssi_pkt_type = 0x88 rx_nss = 0:40, 1:56, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_dcm = 0:0, 1:0, 2:0, 3:0, 4:0 rx_stbc = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_bw = 0:1175, 1:0, 2:0, 3:0 rx_pream = 0:435, 1:644, 2:0, 3:0, 4:96, 5:0, 6:0 rx_11ax_su_txbf_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_11ax_mu_txbf_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_legacy_cck_rate = 0:641, 1:0, 2:3, 3:0 rx_legacy_ofdm_rate = 0:267, 1:0, 2:72, 3:0, 4:96, 5:0, 6:0, 7:0 ul_ofdma_rx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_nss = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 ul_ofdma_rx_bw = 0:0, 1:0, 2:0, 3:0 rx_ulofdma_non_data_ppdu = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulofdma_data_ppdu = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulofdma_mpdu_ok = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulofdma_mpdu_fail = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulofdma_non_data_nusers = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulofdma_data_nusers = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_11ax_dl_ofdma_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_11ax_dl_ofdma_ru = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0 rx_ulmumimo_non_data_ppdu = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulmumimo_data_ppdu = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulmumimo_mpdu_ok = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulmumimo_mpdu_fail = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_mcs = 0:0, 1:0, 2:0, 3:0, 4:28, 5:8, 6:37, 7:21, 8:2, 9:0, 10:0, 11:0, 12:0, 13:0 pilot_evm_db[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[4] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[5] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[6] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db[7] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 pilot_evm_db_mean = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rssi_chain_in_db[0] = 0: 196, 1: 29, 2: 29, 3: 29 rssi_chain_in_db[1] = 0: 196, 1: 29, 2: 29, 3: 29 rssi_chain_in_db[2] = 0: 128, 1: 128, 2: 128, 3: 128 rssi_chain_in_db[3] = 0: 128, 1: 128, 2: 128, 3: 128 rssi_chain_in_db[4] = 0: 128, 1: 128, 2: 128, 3: 128 rssi_chain_in_db[5] = 0: 128, 1: 128, 2: 128, 3: 128 rssi_chain_in_db[6] = 0: 128, 1: 128, 2: 128, 3: 128 rssi_chain_in_db[7] = 0: 128, 1: 128, 2: 128, 3: 128 rx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:28, 5:8, 6:37, 7:21, 8:2, 9:0, 10:0, 11:0 rx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 rx_ul_fd_rssi: nss[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[4] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[5] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[6] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_fd_rssi: nss[7] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_per_chain_rssi_in_dbm[0] = 0:-60, 1:29, 2:29, 3:29 rx_per_chain_rssi_in_dbm[1] = 0:-60, 1:29, 2:29, 3:29 rx_per_chain_rssi_in_dbm[2] = 0:-128, 1:-128, 2:-128, 3:-128 rx_per_chain_rssi_in_dbm[3] = 0:-128, 1:-128, 2:-128, 3:-128 rx_per_chain_rssi_in_dbm[4] = 0:-128, 1:-128, 2:-128, 3:-128 rx_per_chain_rssi_in_dbm[5] = 0:-128, 1:-128, 2:-128, 3:-128 rx_per_chain_rssi_in_dbm[6] = 0:-128, 1:-128, 2:-128, 3:-128 rx_per_chain_rssi_in_dbm[7] = 0:-128, 1:-128, 2:-128, 3:-128 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250113071758.19589-3-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 222 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 82 +++++++ 2 files changed, 304 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 29a79c800d54..c3a53d84f8b1 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -4013,6 +4013,225 @@ ath12k_htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static inline void +ath12k_htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", + le32_to_cpu(htt_stats_buf->nsts)); + len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt)); + len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n", + le32_to_cpu(htt_stats_buf->rssi_data)); + len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", + le32_to_cpu(htt_stats_buf->rssi_comb)); + len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_in_dbm)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n", + le32_to_cpu(htt_stats_buf->nss_count)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_pilot_count = %u\n", + le32_to_cpu(htt_stats_buf->pilot_count)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_su_ext)); + len += scnprintf(buf + len, buf_len - len, "rx_11ac_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ac_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ofdma)); + len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", + le32_to_cpu(htt_stats_buf->txbf)); + len += scnprintf(buf + len, buf_len - len, "rx_su_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_mu_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_mu_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_br_poll = %u\n", + le32_to_cpu(htt_stats_buf->rx_br_poll)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_low)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_high)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", + le32_to_cpu(htt_stats_buf->per_chain_rssi_pkt_type)); + + len += print_array_to_buf(buf, len, "rx_nss", htt_stats_buf->rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "rx_dcm", htt_stats_buf->rx_dcm, + ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_stbc", htt_stats_buf->rx_stbc, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_bw", htt_stats_buf->rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_pream", htt_stats_buf->rx_pream, + ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs", + htt_stats_buf->rx_11ax_su_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs", + htt_stats_buf->rx_11ax_mu_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_cck_rate", + htt_stats_buf->rx_legacy_cck_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_ofdm_rate", + htt_stats_buf->rx_legacy_ofdm_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_nss", + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_ppdu", + htt_stats_buf->rx_ulofdma_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_ppdu", + htt_stats_buf->rx_ulofdma_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_ok", + htt_stats_buf->rx_ulofdma_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_fail", + htt_stats_buf->rx_ulofdma_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_nusers", + htt_stats_buf->rx_ulofdma_non_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_nusers", + htt_stats_buf->rx_ulofdma_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs", + htt_stats_buf->rx_11ax_dl_ofdma_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_ru", + htt_stats_buf->rx_11ax_dl_ofdma_ru, + ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_non_data_ppdu", + htt_stats_buf->rx_ulmumimo_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_data_ppdu", + htt_stats_buf->rx_ulmumimo_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_ok", + htt_stats_buf->rx_ulmumimo_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_fail", + htt_stats_buf->rx_ulmumimo_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs", + htt_stats_buf->rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->rx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "pilot_evm_db[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_pil_evm_db[j], + ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS, + "\n"); + } + + len += scnprintf(buf + len, buf_len - len, "pilot_evm_db_mean ="); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", i, + le32_to_cpu(htt_stats_buf->rx_pilot_evm_db_mean[i])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rssi_chain_in_db[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u: %d,", i, + htt_stats_buf->rssi_chain_in_db[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_fd_rssi: nss[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, htt_stats_buf->rx_ul_fd_rssi[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_per_chain_rssi_in_dbm[%u] =", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, + htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -4251,6 +4470,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_TX_PDEV_RATE_STATS_TAG: ath12k_htt_print_tx_pdev_rate_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 510d347242d2..13bafa793764 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -130,6 +130,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, @@ -175,6 +176,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_SFM_CMN_TAG = 26, HTT_STATS_SRING_STATS_TAG = 27, HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, HTT_STATS_TX_SCHED_CMN_TAG = 37, HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, @@ -455,6 +457,86 @@ struct ath12k_htt_tx_pdev_rate_stats_tlv { __le32 tx_bw_320mhz; }; +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS 16 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS 6 +#define ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 + +struct ath12k_htt_rx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 nsts; + __le32 rx_ldpc; + __le32 rts_cnt; + __le32 rssi_mgmt; + __le32 rssi_data; + __le32 rssi_comb; + __le32 rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_nss[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 rx_dcm[ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rx_stbc[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_bw[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_pream[ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain_in_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_gi[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rssi_in_dbm; + __le32 rx_11ax_su_ext; + __le32 rx_11ac_mumimo; + __le32 rx_11ax_mumimo; + __le32 rx_11ax_ofdma; + __le32 txbf; + __le32 rx_legacy_cck_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 rx_legacy_ofdm_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 rx_active_dur_us_low; + __le32 rx_active_dur_us_high; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 rx_ulofdma_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 nss_count; + __le32 pilot_count; + __le32 rx_pil_evm_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS]; + __le32 rx_pilot_evm_db_mean[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + s8 rx_ul_fd_rssi[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 per_chain_rssi_pkt_type; + s8 rx_per_chain_rssi_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_su_ndpa; + __le32 rx_11ax_su_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_mu_ndpa; + __le32 rx_11ax_mu_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_br_poll; + __le32 rx_11ax_dl_ofdma_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_11ax_dl_ofdma_ru[ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS]; + __le32 rx_ulmumimo_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulofdma_non_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; +}; + #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) From ca7fc2b4a1f52270980ad90b4ffea6479f9f9b70 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 092/465] wifi: ath12k: Dump additional PDEV receive rate HTT stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7a3e8eec8d183d7b293bfb69caab9f213e0a6bbd Author: Lingbo Kong Date: Mon Jan 13 15:17:58 2025 +0800 wifi: ath12k: Dump additional PDEV receive rate HTT stats Support to dump additional PDEV receive rate stats through HTT debugfs stats type 30. Sample output: ------------------ echo 30 > /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:03\:00.0/mac0/htt_stats HTT_RX_PDEV_RATE_EXT_STATS_TLV: rssi_mgmt_in_dbm = -48 rx_stbc_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_ofdma_rx_mcs_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 rx_11ax_su_txbf_mcs_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:9, 6:72, 7:41, 8:1, 9:0, 10:0, 11:0, 12:0, 13:0 rx_11ax_mu_txbf_mcs_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 rx_11ax_dl_ofdma_mcs_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 rx_bw_ext = 0:1395, 1:0, 2:0, 3:0, 4:0 rx_su_punctured_mode = 0:0, 1:0, 2:0, 3:0, 4:0 rx_mcs_ext = 0:0, 1:0, 2:0, 3:0, 4:0, 5:14, 6:149, 7:44, 8:1, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0 rx_gi_ext[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:14, 6:149, 7:44, 8:1, 9:0, 10:0, 11:0, 12:0, 13:0 rx_gi_ext[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 rx_gi_ext[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 rx_gi_ext[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_ofdma_rx_gi_ext[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_ofdma_rx_gi_ext[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_ofdma_rx_gi_ext[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_ofdma_rx_gi_ext[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250113071758.19589-4-quic_lingbok@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 74 +++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 32 ++++++++ 2 files changed, 106 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index c3a53d84f8b1..bad1bd21d67d 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -4232,6 +4232,77 @@ ath12k_htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static inline void +ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_ext_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_EXT_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt_in_dbm)); + + len += print_array_to_buf(buf, len, "rx_stbc_ext", + htt_stats_buf->rx_stbc_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs_ext", + htt_stats_buf->ul_ofdma_rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs_ext", + htt_stats_buf->rx_11ax_su_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs_ext", + htt_stats_buf->rx_11ax_mu_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs_ext", + htt_stats_buf->rx_11ax_dl_ofdma_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_bw_ext", + htt_stats_buf->rx_bw_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_su_punctured_mode", + htt_stats_buf->rx_su_punctured_mode, + ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS, + "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs_ext", + htt_stats_buf->rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + NULL); + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + le32_to_cpu(htt_stats_buf->rx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -4473,6 +4544,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_RX_PDEV_RATE_STATS_TAG: ath12k_htt_print_rx_pdev_rate_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 13bafa793764..e557a5807bc6 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -137,6 +137,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, @@ -205,6 +206,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_HW_WAR_TAG = 89, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, + HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG = 108, HTT_STATS_TX_SELFGEN_AC_SCHED_STATUS_STATS_TAG = 111, HTT_STATS_TX_SELFGEN_AX_SCHED_STATUS_STATS_TAG = 112, @@ -537,6 +539,36 @@ struct ath12k_htt_rx_pdev_rate_stats_tlv { __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; }; +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT 14 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS 5 + +struct ath12k_htt_rx_pdev_rate_ext_stats_tlv { + u8 rssi_chain_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + s8 rx_per_chain_rssi_ext_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + __le32 rssi_mcast_in_dbm; + __le32 rssi_mgmt_in_dbm; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_stbc_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_gi_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_su_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_mu_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_dl_ofdma_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_mcs_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_bw_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS]; + __le32 rx_gi_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_su_punctured_mode[ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS]; +}; + #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) From 4838451ac0dc407e6a96cc20809160f4161c5a45 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 093/465] wifi: ath12k: Add Support to Parse TPC Event from Firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f0c3bb78e42f2f67403b2314486e42f40737b15c Author: Sowmiya Sree Elavalagan Date: Thu Jan 30 11:41:03 2025 +0530 wifi: ath12k: Add Support to Parse TPC Event from Firmware Host receives four Transmit Power Control(TPC) events from firmware on sending TPC request. Fixed param TLV is present as part of all event to indicate the event count and end of event. TPC config parameters along with regulatory power array comes as first event. Rates array comes as second and third event as it cannot be packed in single event. Conformance Test Limit (CTL) power array comes as the fourth event. Firmware packs different sets of array params which includes array length and type inside master TLV as different subtlvs. And the actual content of array is packed one after the other inside a separate TLV as single buffer. Parse various events and save it in local structures. Create tpc_stats file using debugfs to store these local structures. Create function to handle TPC stats read to relay the information to the user. Command usage: cat > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/tpc_stats Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sowmiya Sree Elavalagan Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Co-developed-by: Roopni Devanathan Signed-off-by: Roopni Devanathan Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250130061104.962124-2-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 4 + drivers/net/wireless/ath/ath12k/debugfs.c | 96 +++++ drivers/net/wireless/ath/ath12k/debugfs.h | 6 + drivers/net/wireless/ath/ath12k/wmi.c | 451 ++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 139 +++++++ 5 files changed, 696 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index c34a14404865..57e71ccbbb67 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -575,6 +575,10 @@ struct ath12k_debug { struct dentry *debugfs_pdev; struct dentry *debugfs_pdev_symlink; struct ath12k_dbg_htt_stats htt_stats; + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type; + bool tpc_request; + struct completion tpc_complete; + struct wmi_tpc_stats_arg *tpc_stats; }; struct ath12k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 6d6708486d14..8ddcae01a2a1 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -32,6 +32,99 @@ static const struct file_operations fops_simulate_radar = { .open = simple_open }; +static int ath12k_debug_tpc_stats_request(struct ath12k *ar) +{ + enum wmi_halphy_ctrl_path_stats_id tpc_stats_sub_id; + struct ath12k_base *ab = ar->ab; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + reinit_completion(&ar->debug.tpc_complete); + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = true; + tpc_stats_sub_id = ar->debug.tpc_stats_type; + spin_unlock_bh(&ar->data_lock); + + ret = ath12k_wmi_send_tpc_stats_request(ar, tpc_stats_sub_id); + if (ret) { + ath12k_warn(ab, "failed to request pdev tpc stats: %d\n", ret); + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return ret; + } + + return 0; +} + +static int ath12k_open_tpc_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah->state != ATH12K_HW_STATE_ON) { + ath12k_warn(ar->ab, "Interface not up\n"); + return -ENETDOWN; + } + + void *buf __free(kfree) = kzalloc(ATH12K_TPC_STATS_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ath12k_debug_tpc_stats_request(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to request tpc stats: %d\n", + ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->debug.tpc_complete, TPC_STATS_WAIT_TIME)) { + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return -ETIMEDOUT; + } + + file->private_data = no_free_ptr(buf); + + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static ssize_t ath12k_read_tpc_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static int ath12k_release_tpc_stats(struct inode *inode, + struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations fops_tpc_stats = { + .open = ath12k_open_tpc_stats, + .release = ath12k_release_tpc_stats, + .read = ath12k_read_tpc_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath12k_debugfs_soc_create(struct ath12k_base *ab) { bool dput_needed; @@ -468,6 +561,9 @@ void ath12k_debugfs_register(struct ath12k *ar) &fops_simulate_radar); } + debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar, + &fops_tpc_stats); + ath12k_debugfs_htt_stats_register(ar); ath12k_debugfs_fw_stats_register(ar); } diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 1c30745ee415..25b5219ffe49 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -15,6 +15,12 @@ void ath12k_debugfs_unregister(struct ath12k *ar); void ath12k_debugfs_fw_stats_process(struct ath12k *ar, struct ath12k_fw_stats *stats); void ath12k_debugfs_fw_stats_reset(struct ath12k *ar); + +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define TPC_STATS_TOT_ROW 700 +#define TPC_STATS_TOT_COLUMN 100 +#define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN) + #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b41bafd5143d..f934d49acee6 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -8129,6 +8129,386 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, kfree(tb); } +#ifdef CONFIG_ATH12K_DEBUGFS +static int ath12k_wmi_tpc_stats_copy_buffer(struct ath12k_base *ab, + const void *ptr, u16 tag, u16 len, + struct wmi_tpc_stats_arg *tpc_stats) +{ + u32 len1, len2, len3, len4; + s16 *dst_ptr; + s8 *dst_ptr_ctl; + + len1 = le32_to_cpu(tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len); + len2 = le32_to_cpu(tpc_stats->rates_array1.tpc_rates_array.rate_array_len); + len3 = le32_to_cpu(tpc_stats->rates_array2.tpc_rates_array.rate_array_len); + len4 = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len); + + switch (tpc_stats->event_count) { + case ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT: + if (len1 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_REG_PWR_ALLOWED) { + dst_ptr = tpc_stats->max_reg_allowed_power.reg_pwr_array; + memcpy(dst_ptr, ptr, len1); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT1: + if (len2 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY1) { + dst_ptr = tpc_stats->rates_array1.rate_array; + memcpy(dst_ptr, ptr, len2); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT2: + if (len3 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY2) { + dst_ptr = tpc_stats->rates_array2.rate_array; + memcpy(dst_ptr, ptr, len3); + } + break; + case ATH12K_TPC_STATS_CTL_TABLE_EVENT: + if (len4 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + dst_ptr_ctl = tpc_stats->ctl_array.ctl_pwr_table; + memcpy(dst_ptr_ctl, ptr, len4); + } + break; + } + return 0; +} + +static int ath12k_tpc_get_reg_pwr(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_max_reg_power_fixed_params *ev) +{ + struct wmi_max_reg_power_allowed_arg *reg_pwr; + u32 total_size; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received reg power array type %d length %d for tpc stats\n", + ev->reg_power_type, ev->reg_array_len); + + switch (le32_to_cpu(ev->reg_power_type)) { + case TPC_STATS_REG_PWR_ALLOWED_TYPE: + reg_pwr = &tpc_stats->max_reg_allowed_power; + break; + default: + return -EINVAL; + } + + /* Each entry is 2 byte hence multiplying the indices with 2 */ + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4) * 2; + if (le32_to_cpu(ev->reg_array_len) != total_size) { + ath12k_warn(ab, + "Total size and reg_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(®_pwr->tpc_reg_pwr, ev, sizeof(struct wmi_max_reg_power_fixed_params)); + + reg_pwr->reg_pwr_array = kzalloc(le32_to_cpu(reg_pwr->tpc_reg_pwr.reg_array_len), + GFP_ATOMIC); + if (!reg_pwr->reg_pwr_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= WMI_TPC_REG_PWR_ALLOWED; + + return 0; +} + +static int ath12k_tpc_get_rate_array(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_rates_array_fixed_params *ev) +{ + struct wmi_tpc_rates_array_arg *rates_array; + u32 flag = 0, rate_array_len; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received rates array type %d length %d for tpc stats\n", + ev->rate_array_type, ev->rate_array_len); + + switch (le32_to_cpu(ev->rate_array_type)) { + case ATH12K_TPC_STATS_RATES_ARRAY1: + rates_array = &tpc_stats->rates_array1; + flag = WMI_TPC_RATES_ARRAY1; + break; + case ATH12K_TPC_STATS_RATES_ARRAY2: + rates_array = &tpc_stats->rates_array2; + flag = WMI_TPC_RATES_ARRAY2; + break; + default: + ath12k_warn(ab, + "Received invalid type of rates array for tpc stats\n"); + return -EINVAL; + } + memcpy(&rates_array->tpc_rates_array, ev, + sizeof(struct wmi_tpc_rates_array_fixed_params)); + rate_array_len = le32_to_cpu(rates_array->tpc_rates_array.rate_array_len); + rates_array->rate_array = kzalloc(rate_array_len, GFP_ATOMIC); + if (!rates_array->rate_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_tpc_get_ctl_pwr_tbl(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_ctl_pwr_fixed_params *ev) +{ + struct wmi_tpc_ctl_pwr_table_arg *ctl_array; + u32 total_size, ctl_array_len, flag = 0; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received ctl array type %d length %d for tpc stats\n", + ev->ctl_array_type, ev->ctl_array_len); + + switch (le32_to_cpu(ev->ctl_array_type)) { + case ATH12K_TPC_STATS_CTL_ARRAY: + ctl_array = &tpc_stats->ctl_array; + flag = WMI_TPC_CTL_PWR_ARRAY; + break; + default: + ath12k_warn(ab, + "Received invalid type of ctl pwr table for tpc stats\n"); + return -EINVAL; + } + + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4); + if (le32_to_cpu(ev->ctl_array_len) != total_size) { + ath12k_warn(ab, + "Total size and ctl_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(&ctl_array->tpc_ctl_pwr, ev, sizeof(struct wmi_tpc_ctl_pwr_fixed_params)); + ctl_array_len = le32_to_cpu(ctl_array->tpc_ctl_pwr.ctl_array_len); + ctl_array->ctl_pwr_table = kzalloc(ctl_array_len, GFP_ATOMIC); + if (!ctl_array->ctl_pwr_table) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_wmi_tpc_stats_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_rates_array_fixed_params *tpc_rates_array; + struct wmi_max_reg_power_fixed_params *tpc_reg_pwr; + struct wmi_tpc_ctl_pwr_fixed_params *tpc_ctl_pwr; + struct wmi_tpc_stats_arg *tpc_stats = data; + struct wmi_tpc_config_params *tpc_config; + int ret = 0; + + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + return -EINVAL; + } + + switch (tag) { + case WMI_TAG_TPC_STATS_CONFIG_EVENT: + tpc_config = (struct wmi_tpc_config_params *)ptr; + memcpy(&tpc_stats->tpc_config, tpc_config, + sizeof(struct wmi_tpc_config_params)); + break; + case WMI_TAG_TPC_STATS_REG_PWR_ALLOWED: + tpc_reg_pwr = (struct wmi_max_reg_power_fixed_params *)ptr; + ret = ath12k_tpc_get_reg_pwr(ab, tpc_stats, tpc_reg_pwr); + break; + case WMI_TAG_TPC_STATS_RATES_ARRAY: + tpc_rates_array = (struct wmi_tpc_rates_array_fixed_params *)ptr; + ret = ath12k_tpc_get_rate_array(ab, tpc_stats, tpc_rates_array); + break; + case WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT: + tpc_ctl_pwr = (struct wmi_tpc_ctl_pwr_fixed_params *)ptr; + ret = ath12k_tpc_get_ctl_pwr_tbl(ab, tpc_stats, tpc_ctl_pwr); + break; + default: + ath12k_warn(ab, + "Received invalid tag for tpc stats in subtlvs\n"); + return -EINVAL; + } + return ret; +} + +static int ath12k_wmi_tpc_stats_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_stats_arg *tpc_stats = (struct wmi_tpc_stats_arg *)data; + int ret; + + switch (tag) { + case WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM: + ret = 0; + /* Fixed param is already processed*/ + break; + case WMI_TAG_ARRAY_STRUCT: + /* len 0 is expected for array of struct when there + * is no content of that type to pack inside that tlv + */ + if (len == 0) + return 0; + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_tpc_stats_subtlv_parser, + tpc_stats); + break; + case WMI_TAG_ARRAY_INT16: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_INT16, + len, tpc_stats); + break; + case WMI_TAG_ARRAY_BYTE: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_BYTE, + len, tpc_stats); + break; + default: + ath12k_warn(ab, "Received invalid tag for tpc stats\n"); + ret = -EINVAL; + break; + } + return ret; +} + +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + + lockdep_assert_held(&ar->data_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc stats mem free\n"); + if (tpc_stats) { + kfree(tpc_stats->max_reg_allowed_power.reg_pwr_array); + kfree(tpc_stats->rates_array1.rate_array); + kfree(tpc_stats->rates_array2.rate_array); + kfree(tpc_stats->ctl_array.ctl_pwr_table); + kfree(tpc_stats); + ar->debug.tpc_stats = NULL; + } +} + +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *fixed_param; + struct wmi_tpc_stats_arg *tpc_stats; + const struct wmi_tlv *tlv; + void *ptr = skb->data; + struct ath12k *ar; + u16 tlv_tag; + u32 event_count; + int ret; + + if (!skb->data) { + ath12k_warn(ab, "No data present in tpc stats event\n"); + return; + } + + if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "TPC stats event size invalid\n"); + return; + } + + tlv = (struct wmi_tlv *)ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) { + ath12k_warn(ab, "TPC stats without fixed param tlv at start\n"); + return; + } + + fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr; + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1); + if (!ar) { + ath12k_warn(ab, "Failed to get ar for tpc stats\n"); + rcu_read_unlock(); + return; + } + spin_lock_bh(&ar->data_lock); + if (!ar->debug.tpc_request) { + /* Event is received either without request or the + * timeout, if memory is already allocated free it + */ + if (ar->debug.tpc_stats) { + ath12k_warn(ab, "Freeing memory for tpc_stats\n"); + ath12k_wmi_free_tpc_stats_mem(ar); + } + goto unlock; + } + + event_count = le32_to_cpu(fixed_param->event_count); + if (event_count == 0) { + if (ar->debug.tpc_stats) { + ath12k_warn(ab, + "Invalid tpc memory present\n"); + goto unlock; + } + ar->debug.tpc_stats = + kzalloc(sizeof(struct wmi_tpc_stats_arg), + GFP_ATOMIC); + if (!ar->debug.tpc_stats) { + ath12k_warn(ab, + "Failed to allocate memory for tpc stats\n"); + goto unlock; + } + } + + tpc_stats = ar->debug.tpc_stats; + + if (!(event_count == 0)) { + if (event_count != tpc_stats->event_count + 1) { + ath12k_warn(ab, + "Invalid tpc event received\n"); + goto unlock; + } + } + tpc_stats->pdev_id = le32_to_cpu(fixed_param->pdev_id); + tpc_stats->end_of_event = le32_to_cpu(fixed_param->end_of_event); + tpc_stats->event_count = le32_to_cpu(fixed_param->event_count); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "tpc stats event_count %d\n", + tpc_stats->event_count); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tpc_stats_event_parser, + tpc_stats); + if (ret) { + if (tpc_stats) + ath12k_wmi_free_tpc_stats_mem(ar); + ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret); + goto unlock; + } + + if (tpc_stats && tpc_stats->end_of_event) + complete(&ar->debug.tpc_complete); + +unlock: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} +#else +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -8254,6 +8634,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_MLO_TEARDOWN_COMPLETE_EVENTID: ath12k_wmi_event_teardown_complete(ab, skb); break; + case WMI_HALPHY_STATS_CTRL_PATH_EVENTID: + ath12k_wmi_process_tpc_stats(ab, skb); + break; /* add Unsupported events (rare) here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: @@ -8409,6 +8792,74 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type) +{ + struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_tlv *tlv; + __le32 *pdev_id; + u32 buf_len; + void *ptr; + int ret; + + buf_len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(u32) + TLV_HDR_SIZE + TLV_HDR_SIZE; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) + return -ENOMEM; + cmd = (struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM, + sizeof(*cmd)); + + cmd->stats_id_mask = cpu_to_le32(WMI_REQ_CTRL_PATH_PDEV_TX_STAT); + cmd->action = cpu_to_le32(WMI_REQUEST_CTRL_PATH_STAT_GET); + cmd->subid = cpu_to_le32(tpc_stats_type); + + ptr = skb->data + sizeof(*cmd); + + /* The below TLV arrays optionally follow this fixed param TLV structure + * 1. ARRAY_UINT32 pdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the pdevs identified in the array. + * 2. ARRAY_UNIT32 vdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the vdevs identified in the array. + * 3. ath12k_wmi_mac_addr_params peer_macaddr[]; + * If this array is present and non-zero length, stats should only + * be provided from the peers with the MAC addresses specified + * in the array + */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + ptr += TLV_HDR_SIZE; + + pdev_id = ptr; + *pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar)); + ptr += sizeof(*pdev_id); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + ptr += TLV_HDR_SIZE; + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0); + ptr += TLV_HDR_SIZE; + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to submit WMI_REQUEST_STATS_CTRL_PATH_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI get TPC STATS sent on pdev %d\n", + ar->pdev->pdev_id); + + return ret; +} + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 02ffc3fa24ee..1b914dc552f7 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -517,6 +517,9 @@ enum wmi_tlv_cmd_id { WMI_REQUEST_RCPI_CMDID, WMI_REQUEST_PEER_STATS_INFO_CMDID, WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_REQUEST_WLM_STATS_CMDID, + WMI_REQUEST_CTRL_PATH_STATS_CMDID, + WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID = WMI_REQUEST_CTRL_PATH_STATS_CMDID + 3, WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, @@ -786,6 +789,9 @@ enum wmi_tlv_event_id { WMI_UPDATE_RCPI_EVENTID, WMI_PEER_STATS_INFO_EVENTID, WMI_RADIO_CHAN_STATS_EVENTID, + WMI_WLM_STATS_EVENTID, + WMI_CTRL_PATH_STATS_EVENTID, + WMI_HALPHY_STATS_CTRL_PATH_EVENTID, WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), WMI_NLO_SCAN_COMPLETE_EVENTID, WMI_APFIND_EVENTID, @@ -1192,6 +1198,7 @@ enum wmi_tlv_tag { WMI_TAG_ARRAY_BYTE, WMI_TAG_ARRAY_STRUCT, WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_ARRAY_INT16, WMI_TAG_LAST_ARRAY_ENUM = 31, WMI_TAG_SERVICE_READY_EVENT, WMI_TAG_HAL_REG_CAPABILITIES, @@ -1942,6 +1949,12 @@ enum wmi_tlv_tag { WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F, WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_TPC_STATS_GET_CMD = 0x38B, + WMI_TAG_TPC_STATS_EVENT_FIXED_PARAM, + WMI_TAG_TPC_STATS_CONFIG_EVENT, + WMI_TAG_TPC_STATS_REG_PWR_ALLOWED, + WMI_TAG_TPC_STATS_RATES_ARRAY, + WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT, WMI_TAG_EHT_RATE_SET = 0x3C4, WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, WMI_TAG_MLO_TX_SEND_PARAMS, @@ -1959,6 +1972,8 @@ enum wmi_tlv_tag { WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD = 0x3D9, WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD = 0x3FB, + WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM = 0x442, + WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM, WMI_TAG_MAX }; @@ -5768,6 +5783,126 @@ struct ath12k_fw_stats_req_params { u32 pdev_id; }; +#define WMI_REQ_CTRL_PATH_PDEV_TX_STAT 1 +#define WMI_REQUEST_CTRL_PATH_STAT_GET 1 + +#define WMI_TPC_CONFIG BIT(1) +#define WMI_TPC_REG_PWR_ALLOWED BIT(2) +#define WMI_TPC_RATES_ARRAY1 BIT(3) +#define WMI_TPC_RATES_ARRAY2 BIT(4) +#define WMI_TPC_RATES_DL_OFDMA_ARRAY BIT(5) +#define WMI_TPC_CTL_PWR_ARRAY BIT(6) +#define WMI_TPC_CONFIG_PARAM 0x1 +#define ATH12K_TPC_RATE_ARRAY_MU GENMASK(15, 8) +#define ATH12K_TPC_RATE_ARRAY_SU GENMASK(7, 0) +#define TPC_STATS_REG_PWR_ALLOWED_TYPE 0 + +enum wmi_halphy_ctrl_path_stats_id { + WMI_HALPHY_PDEV_TX_SU_STATS = 0, + WMI_HALPHY_PDEV_TX_SUTXBF_STATS, + WMI_HALPHY_PDEV_TX_MU_STATS, + WMI_HALPHY_PDEV_TX_MUTXBF_STATS, + WMI_HALPHY_PDEV_TX_STATS_MAX, +}; + +enum ath12k_wmi_tpc_stats_rates_array { + ATH12K_TPC_STATS_RATES_ARRAY1, + ATH12K_TPC_STATS_RATES_ARRAY2, +}; + +enum ath12k_wmi_tpc_stats_ctl_array { + ATH12K_TPC_STATS_CTL_ARRAY, + ATH12K_TPC_STATS_CTL_160ARRAY, +}; + +enum ath12k_wmi_tpc_stats_events { + ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT, + ATH12K_TPC_STATS_RATES_EVENT1, + ATH12K_TPC_STATS_RATES_EVENT2, + ATH12K_TPC_STATS_CTL_TABLE_EVENT +}; + +struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params { + __le32 tlv_header; + __le32 stats_id_mask; + __le32 request_id; + __le32 action; + __le32 subid; +} __packed; + +struct ath12k_wmi_pdev_tpc_stats_event_fixed_params { + __le32 pdev_id; + __le32 end_of_event; + __le32 event_count; +} __packed; + +struct wmi_tpc_config_params { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_reg_power; + __le32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + __le32 caps; +} __packed; + +struct wmi_max_reg_power_fixed_params { + __le32 reg_power_type; + __le32 reg_array_len; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_max_reg_power_allowed_arg { + struct wmi_max_reg_power_fixed_params tpc_reg_pwr; + s16 *reg_pwr_array; +}; + +struct wmi_tpc_rates_array_fixed_params { + __le32 rate_array_type; + __le32 rate_array_len; +} __packed; + +struct wmi_tpc_rates_array_arg { + struct wmi_tpc_rates_array_fixed_params tpc_rates_array; + s16 *rate_array; +}; + +struct wmi_tpc_ctl_pwr_fixed_params { + __le32 ctl_array_type; + __le32 ctl_array_len; + __le32 end_of_ctl_pwr; + __le32 ctl_pwr_count; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_tpc_ctl_pwr_table_arg { + struct wmi_tpc_ctl_pwr_fixed_params tpc_ctl_pwr; + s8 *ctl_pwr_table; +}; + +struct wmi_tpc_stats_arg { + u32 pdev_id; + u32 event_count; + u32 end_of_event; + u32 tlvs_rcvd; + struct wmi_max_reg_power_allowed_arg max_reg_allowed_power; + struct wmi_tpc_rates_array_arg rates_array1; + struct wmi_tpc_rates_array_arg rates_array2; + struct wmi_tpc_config_params tpc_config; + struct wmi_tpc_ctl_pwr_table_arg ctl_array; +}; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5897,6 +6032,10 @@ int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, u32 vdev_id, u32 pdev_id); __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); + static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) { From 9f008a0e4b9421920eed6c3d17b52ce8a0a34e8d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 094/465] wifi: ath12k: Add Support to Calculate and Display TPC Values JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f5c90ff80b4c0326e5fd1feecafd88718075b1b7 Author: Sowmiya Sree Elavalagan Date: Thu Jan 30 11:41:04 2025 +0530 wifi: ath12k: Add Support to Calculate and Display TPC Values Transmit Power Control(TPC) stats should display per chain TPC value per radio. Add debugfs support to read and display TPC stats type and TPC stats. Take power values for each preamble type, rate and NSS combination from a particular index from each power arrays based on number of chains, NSS, modes, MCS and tx beamforming enabled/disabled parameters. Minimum of the values taken from reg power table, rates and Conformance Test Limit(CTL) array table should give the TPC which is in 0.25 dBm steps. Sample Output: ------------- echo 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/tpc_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/tpc_stats *************** TPC config ************** * powers are in 0.25 dBm steps reg domain-22 chan freq-5955 power limit-126 max reg-domain Power-252 No.of tx chain-4 No.of rates-1164 **************** SU WITH TXBF **************** TPC values for Active chains Rate idx Preamble Rate code 1-Chain 2-Chain 3-Chain 4-Chain 4 OFDM 0x000 39 15 1 -9 5 OFDM 0x001 39 15 1 -9 ..... 12 HT20 0x200 40 16 2 -8 13 HT20 0x201 40 16 2 -8 ..... 44 HT40 0x200 88 88 88 88 45 HT40 0x201 88 88 88 88 ..... 76 VHT20 0x300 40 16 2 -8 77 VHT20 0x301 40 16 2 -8 ..... 172 VHT40 0x300 88 88 88 88 173 VHT40 0x301 88 88 88 88 ..... 412 HE20 0x400 88 88 88 88 413 HE20 0x401 88 88 88 88 ..... 508 HE40 0x400 76 76 76 76 509 HE40 0x401 76 76 76 76 ..... 748 EHT20 0x50e 88 88 88 88 749 EHT20 0x50f 88 88 88 88 ..... 812 EHT40 0x50e 88 88 88 88 813 EHT40 0x50f 88 88 88 88 ..... Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sowmiya Sree Elavalagan Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Co-developed-by: Roopni Devanathan Signed-off-by: Roopni Devanathan Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250130061104.962124-3-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/debugfs.c | 618 ++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/debugfs.h | 83 ++- drivers/net/wireless/ath/ath12k/reg.h | 5 +- drivers/net/wireless/ath/ath12k/wmi.h | 1 + 4 files changed, 703 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 8ddcae01a2a1..d38ece46f1d0 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -32,6 +32,28 @@ static const struct file_operations fops_simulate_radar = { .open = simple_open }; +static ssize_t ath12k_write_tpc_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= WMI_HALPHY_PDEV_TX_STATS_MAX) + return -EINVAL; + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_stats_type = type; + spin_unlock_bh(&ar->data_lock); + + return count; +} + static int ath12k_debug_tpc_stats_request(struct ath12k *ar) { enum wmi_halphy_ctrl_path_stats_id tpc_stats_sub_id; @@ -59,6 +81,592 @@ static int ath12k_debug_tpc_stats_request(struct ath12k *ar) return 0; } +static int ath12k_get_tpc_ctl_mode_idx(struct wmi_tpc_stats_arg *tpc_stats, + enum wmi_tpc_pream_bw pream_bw, int *mode_idx) +{ + u32 chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + u8 band; + + band = ((chan_freq > ATH12K_MIN_6G_FREQ) ? NL80211_BAND_6GHZ : + ((chan_freq > ATH12K_MIN_5G_FREQ) ? NL80211_BAND_5GHZ : + NL80211_BAND_2GHZ)); + + if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + switch (pream_bw) { + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT60: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_EHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE160: + case WMI_TPC_PREAM_EHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT200: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120; + break; + case WMI_TPC_PREAM_EHT240: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80; + break; + case WMI_TPC_PREAM_EHT280: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40; + break; + case WMI_TPC_PREAM_EHT320: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ; + break; + default: + /* for 5GHZ and 6GHZ, default case will be for OFDM */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ; + break; + } + } else { + switch (pream_bw) { + case WMI_TPC_PREAM_OFDM: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ; + break; + default: + /* for 2GHZ, default case will be CCK */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ; + break; + } + } + + return 0; +} + +static s16 ath12k_tpc_get_rate(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + u32 rate_idx, u32 num_chains, u32 rate_code, + enum wmi_tpc_pream_bw pream_bw, + enum wmi_halphy_ctrl_path_stats_id type, + u32 eht_rate_idx) +{ + u32 tot_nss, tot_modes, txbf_on_off, index_offset1, index_offset2, index_offset3; + u8 chain_idx, stm_idx, num_streams; + bool is_mu, txbf_enabled = 0; + s8 rates_ctl_min, tpc_ctl; + s16 rates, tpc, reg_pwr; + u16 rate1, rate2; + int mode, ret; + + num_streams = 1 + ATH12K_HW_NSS(rate_code); + chain_idx = num_chains - 1; + stm_idx = num_streams - 1; + mode = -1; + + ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode); + if (ret) { + ath12k_warn(ar->ab, "Invalid mode index received\n"); + tpc = TPC_INVAL; + goto out; + } + + if (num_chains < num_streams) { + tpc = TPC_INVAL; + goto out; + } + + if (le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) { + tpc = TPC_INVAL; + goto out; + } + + if (type == WMI_HALPHY_PDEV_TX_SUTXBF_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) + txbf_enabled = 1; + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + is_mu = true; + } else { + is_mu = false; + } + + /* Below is the min calculation of ctl array, rates array and + * regulator power table. tpc is minimum of all 3 + */ + if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) { + rate2 = tpc_stats->rates_array2.rate_array[eht_rate_idx]; + if (is_mu) + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_SU); + } else { + rate1 = tpc_stats->rates_array1.rate_array[rate_idx]; + if (is_mu) + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_SU); + } + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + tot_nss = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d1); + tot_modes = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d2); + txbf_on_off = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d3); + index_offset1 = txbf_on_off * tot_modes * tot_nss; + index_offset2 = tot_modes * tot_nss; + index_offset3 = tot_nss; + + tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table + + chain_idx * index_offset1 + txbf_enabled * index_offset2 + + mode * index_offset3 + stm_idx); + } else { + tpc_ctl = TPC_MAX; + ath12k_warn(ar->ab, + "ctl array for tpc stats not received from fw\n"); + } + + rates_ctl_min = min_t(s16, rates, tpc_ctl); + + reg_pwr = tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx]; + + if (reg_pwr < 0) + reg_pwr = TPC_INVAL; + + tpc = min_t(s16, rates_ctl_min, reg_pwr); + + /* MODULATION_LIMIT is the maximum power limit,tpc should not exceed + * modulation limit even if min tpc of all three array is greater + * modulation limit + */ + tpc = min_t(s16, tpc, MODULATION_LIMIT); + +out: + return tpc; +} + +static u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate) +{ + u16 mode_type = ~0; + + /* Below assignments are just for printing purpose only */ + switch (pream_idx) { + case WMI_TPC_PREAM_CCK: + mode_type = WMI_RATE_PREAMBLE_CCK; + break; + case WMI_TPC_PREAM_OFDM: + mode_type = WMI_RATE_PREAMBLE_OFDM; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_HT40: + mode_type = WMI_RATE_PREAMBLE_HT; + break; + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_VHT80: + case WMI_TPC_PREAM_VHT160: + mode_type = WMI_RATE_PREAMBLE_VHT; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_HE160: + mode_type = WMI_RATE_PREAMBLE_HE; + break; + case WMI_TPC_PREAM_EHT20: + case WMI_TPC_PREAM_EHT40: + case WMI_TPC_PREAM_EHT60: + case WMI_TPC_PREAM_EHT80: + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + case WMI_TPC_PREAM_EHT160: + case WMI_TPC_PREAM_EHT200: + case WMI_TPC_PREAM_EHT240: + case WMI_TPC_PREAM_EHT280: + case WMI_TPC_PREAM_EHT320: + mode_type = WMI_RATE_PREAMBLE_EHT; + if (mcs_rate == 0 || mcs_rate == 1) + mcs_rate += 14; + else + mcs_rate -= 2; + break; + default: + return mode_type; + } + return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F)); +} + +static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq) +{ + struct ath12k_pdev_cap *cap = &ar->pdev->cap; + struct ath12k_band_cap *cap_band; + bool extra_mcs_supported; + + if (freq <= ATH12K_2GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_2GHZ]; + else if (freq <= ATH12K_5GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_5GHZ]; + else + cap_band = &cap->band[NL80211_BAND_6GHZ]; + + extra_mcs_supported = u32_get_bits(cap_band->he_cap_info[1], + HE_EXTRA_MCS_SUPPORT); + return extra_mcs_supported; +} + +static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len, + enum wmi_tpc_pream_bw pream_bw, u32 max_rix, + int max_nss, int max_rates, int pream_type, + enum wmi_halphy_ctrl_path_stats_id tpc_type, + int rate_idx, int eht_rate_idx) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + int nss, rates, chains; + u8 active_tx_chains; + u16 rate_code; + s16 tpc; + + static const char *const pream_str[] = { + [WMI_TPC_PREAM_CCK] = "CCK", + [WMI_TPC_PREAM_OFDM] = "OFDM", + [WMI_TPC_PREAM_HT20] = "HT20", + [WMI_TPC_PREAM_HT40] = "HT40", + [WMI_TPC_PREAM_VHT20] = "VHT20", + [WMI_TPC_PREAM_VHT40] = "VHT40", + [WMI_TPC_PREAM_VHT80] = "VHT80", + [WMI_TPC_PREAM_VHT160] = "VHT160", + [WMI_TPC_PREAM_HE20] = "HE20", + [WMI_TPC_PREAM_HE40] = "HE40", + [WMI_TPC_PREAM_HE80] = "HE80", + [WMI_TPC_PREAM_HE160] = "HE160", + [WMI_TPC_PREAM_EHT20] = "EHT20", + [WMI_TPC_PREAM_EHT40] = "EHT40", + [WMI_TPC_PREAM_EHT60] = "EHT60", + [WMI_TPC_PREAM_EHT80] = "EHT80", + [WMI_TPC_PREAM_EHT120] = "EHT120", + [WMI_TPC_PREAM_EHT140] = "EHT140", + [WMI_TPC_PREAM_EHT160] = "EHT160", + [WMI_TPC_PREAM_EHT200] = "EHT200", + [WMI_TPC_PREAM_EHT240] = "EHT240", + [WMI_TPC_PREAM_EHT280] = "EHT280", + [WMI_TPC_PREAM_EHT320] = "EHT320"}; + + active_tx_chains = ar->num_tx_chains; + + for (nss = 0; nss < max_nss; nss++) { + for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) { + /* FW send extra MCS(10&11) for VHT and HE rates, + * this is not used. Hence skipping it here + */ + if (pream_type == WMI_RATE_PREAMBLE_VHT && + rates > ATH12K_VHT_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_HE && + rates > ATH12K_HE_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_EHT && + rates > ATH12K_EHT_MCS_MAX) + continue; + + rate_code = ath12k_get_ratecode(pream_bw, nss, rates); + len += scnprintf(buf + len, buf_len - len, + "%d\t %s\t 0x%03x\t", max_rix, + pream_str[pream_bw], rate_code); + + for (chains = 0; chains < active_tx_chains; chains++) { + if (nss > chains) { + len += scnprintf(buf + len, + buf_len - len, + "\t%s", "NA"); + } else { + tpc = ath12k_tpc_get_rate(ar, tpc_stats, + rate_idx, chains + 1, + rate_code, pream_bw, + tpc_type, + eht_rate_idx); + + if (tpc == TPC_INVAL) { + len += scnprintf(buf + len, + buf_len - len, "\tNA"); + } else { + len += scnprintf(buf + len, + buf_len - len, "\t%d", + tpc); + } + } + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (pream_type == WMI_RATE_PREAMBLE_EHT) + /*For fetching the next eht rates pwr from rates array2*/ + ++eht_rate_idx; + } + } + + return len; +} + +static int ath12k_tpc_stats_print(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf, size_t len, + enum wmi_halphy_ctrl_path_stats_id type) +{ + u32 eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0; + u32 chan_freq, num_tx_chain, caps, i, j = 1; + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + u8 nss, active_tx_chains; + bool he_ext_mcs; + static const char *const type_str[WMI_HALPHY_PDEV_TX_STATS_MAX] = { + [WMI_HALPHY_PDEV_TX_SU_STATS] = "SU", + [WMI_HALPHY_PDEV_TX_SUTXBF_STATS] = "SU WITH TXBF", + [WMI_HALPHY_PDEV_TX_MU_STATS] = "MU", + [WMI_HALPHY_PDEV_TX_MUTXBF_STATS] = "MU WITH TXBF"}; + + u8 max_rates[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_CCK_RATES, + [WMI_TPC_PREAM_OFDM] = ATH12K_OFDM_RATES, + [WMI_TPC_PREAM_HT20] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_HT40] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_VHT20] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT40] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT80] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT160] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_HE20] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE40] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE80] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE160] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_EHT20] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT40] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT60] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT80] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT120] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT140] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT160] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT200] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT240] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT280] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT320] = ATH12K_EHT_RATES}; + static const u8 max_nss[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_NSS_1, + [WMI_TPC_PREAM_OFDM] = ATH12K_NSS_1, + [WMI_TPC_PREAM_HT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_VHT20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HE20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT60] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT80] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT120] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT140] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT200] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT240] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT280] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT320] = ATH12K_NSS_4}; + + u16 rate_idx[WMI_TPC_PREAM_MAX] = {}, eht_rate_idx[WMI_TPC_PREAM_MAX] = {}; + static const u8 pream_type[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = WMI_RATE_PREAMBLE_CCK, + [WMI_TPC_PREAM_OFDM] = WMI_RATE_PREAMBLE_OFDM, + [WMI_TPC_PREAM_HT20] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_HT40] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_VHT20] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT40] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT80] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT160] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_HE20] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE40] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE80] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE160] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_EHT20] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT40] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT60] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT80] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT120] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT140] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT160] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT200] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT240] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT280] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT320] = WMI_RATE_PREAMBLE_EHT}; + + chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + num_tx_chain = le32_to_cpu(tpc_stats->tpc_config.num_tx_chain); + caps = le32_to_cpu(tpc_stats->tpc_config.caps); + + active_tx_chains = ar->num_tx_chains; + he_ext_mcs = ath12k_he_supports_extra_mcs(ar, chan_freq); + + /* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as + * it is not supported + */ + if (he_ext_mcs) { + for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160; ++i) + max_rates[i] = ATH12K_HE_RATES; + } + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + pream_idx = WMI_TPC_PREAM_VHT20; + + for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i) + max_rix += max_nss[i] * max_rates[i]; + } + /* Enumerate all the rate indices */ + for (i = rate_pream_idx + 1; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i - 1] < num_tx_chain ? + max_nss[i - 1] : num_tx_chain); + + rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss; + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) { + eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss; + ++j; + } + } + + for (i = 0; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i] < num_tx_chain ? + max_nss[i] : num_tx_chain); + total_rates += max_rates[i] * nss; + } + + len += scnprintf(buf + len, buf_len - len, + "No.of rates-%d\n", total_rates); + + len += scnprintf(buf + len, buf_len - len, + "**************** %s ****************\n", + type_str[type]); + len += scnprintf(buf + len, buf_len - len, + "\t\t\t\tTPC values for Active chains\n"); + len += scnprintf(buf + len, buf_len - len, + "Rate idx Preamble Rate code"); + + for (i = 1; i <= active_tx_chains; ++i) { + len += scnprintf(buf + len, buf_len - len, + "\t%d-Chain", i); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) { + if (chan_freq <= 2483) { + if (i == WMI_TPC_PREAM_VHT80 || + i == WMI_TPC_PREAM_VHT160 || + i == WMI_TPC_PREAM_HE80 || + i == WMI_TPC_PREAM_HE160 || + (i >= WMI_TPC_PREAM_EHT60 && + i <= WMI_TPC_PREAM_EHT320)) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } else { + if (i == WMI_TPC_PREAM_CCK) { + max_rix += max_rates[i]; + continue; + } + } + + nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains); + + if (!(caps & + (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) { + if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 || + i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 || + i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } + + len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss, + max_rates[i], pream_type[i], + type, rate_idx[i], eht_rate_idx[eht_idx]); + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) + /*For fetch the next index eht rates from rates array2*/ + ++eht_idx; + + max_rix += max_nss[i] * max_rates[i]; + } + return len; +} + +static void ath12k_tpc_stats_fill(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf) +{ + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + struct wmi_tpc_config_params *tpc; + size_t len = 0; + + if (!tpc_stats) { + ath12k_warn(ar->ab, "failed to find tpc stats\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + tpc = &tpc_stats->tpc_config; + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, + "*************** TPC config **************\n"); + len += scnprintf(buf + len, buf_len - len, + "* powers are in 0.25 dBm steps\n"); + len += scnprintf(buf + len, buf_len - len, + "reg domain-%d\t\tchan freq-%d\n", + tpc->reg_domain, tpc->chan_freq); + len += scnprintf(buf + len, buf_len - len, + "power limit-%d\t\tmax reg-domain Power-%d\n", + le32_to_cpu(tpc->twice_max_reg_power) / 2, tpc->power_limit); + len += scnprintf(buf + len, buf_len - len, + "No.of tx chain-%d\t", + ar->num_tx_chains); + + ath12k_tpc_stats_print(ar, tpc_stats, buf, len, + ar->debug.tpc_stats_type); + + spin_unlock_bh(&ar->data_lock); +} + static int ath12k_open_tpc_stats(struct inode *inode, struct file *file) { struct ath12k *ar = inode->i_private; @@ -91,6 +699,7 @@ static int ath12k_open_tpc_stats(struct inode *inode, struct file *file) return -ETIMEDOUT; } + ath12k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); file->private_data = no_free_ptr(buf); spin_lock_bh(&ar->data_lock); @@ -125,6 +734,12 @@ static const struct file_operations fops_tpc_stats = { .llseek = default_llseek, }; +static const struct file_operations fops_tpc_stats_type = { + .write = ath12k_write_tpc_stats_type, + .open = simple_open, + .llseek = default_llseek, +}; + void ath12k_debugfs_soc_create(struct ath12k_base *ab) { bool dput_needed; @@ -563,6 +1178,9 @@ void ath12k_debugfs_register(struct ath12k *ar) debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar, &fops_tpc_stats); + debugfs_create_file("tpc_stats_type", 0200, ar->debug.debugfs_pdev, + ar, &fops_tpc_stats_type); + init_completion(&ar->debug.tpc_complete); ath12k_debugfs_htt_stats_register(ar); ath12k_debugfs_fw_stats_register(ar); diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 25b5219ffe49..fb85113fc625 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -16,11 +16,88 @@ void ath12k_debugfs_fw_stats_process(struct ath12k *ar, struct ath12k_fw_stats *stats); void ath12k_debugfs_fw_stats_reset(struct ath12k *ar); -#define TPC_STATS_WAIT_TIME (1 * HZ) -#define TPC_STATS_TOT_ROW 700 -#define TPC_STATS_TOT_COLUMN 100 +#define ATH12K_CCK_RATES 4 +#define ATH12K_OFDM_RATES 8 +#define ATH12K_HT_RATES 8 +#define ATH12K_VHT_RATES 12 +#define ATH12K_HE_RATES 12 +#define ATH12K_HE_RATES_WITH_EXTRA_MCS 14 +#define ATH12K_EHT_RATES 16 +#define HE_EXTRA_MCS_SUPPORT GENMASK(31, 16) +#define ATH12K_NSS_1 1 +#define ATH12K_NSS_4 4 +#define ATH12K_NSS_8 8 +#define ATH12K_HW_NSS(_rcode) (((_rcode) >> 5) & 0x7) +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define MAX_TPC_PREAM_STR_LEN 7 +#define TPC_INVAL -128 +#define TPC_MAX 127 +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define TPC_STATS_TOT_ROW 700 +#define TPC_STATS_TOT_COLUMN 100 +#define MODULATION_LIMIT 126 + #define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN) +enum wmi_tpc_pream_bw { + WMI_TPC_PREAM_CCK, + WMI_TPC_PREAM_OFDM, + WMI_TPC_PREAM_HT20, + WMI_TPC_PREAM_HT40, + WMI_TPC_PREAM_VHT20, + WMI_TPC_PREAM_VHT40, + WMI_TPC_PREAM_VHT80, + WMI_TPC_PREAM_VHT160, + WMI_TPC_PREAM_HE20, + WMI_TPC_PREAM_HE40, + WMI_TPC_PREAM_HE80, + WMI_TPC_PREAM_HE160, + WMI_TPC_PREAM_EHT20, + WMI_TPC_PREAM_EHT40, + WMI_TPC_PREAM_EHT60, + WMI_TPC_PREAM_EHT80, + WMI_TPC_PREAM_EHT120, + WMI_TPC_PREAM_EHT140, + WMI_TPC_PREAM_EHT160, + WMI_TPC_PREAM_EHT200, + WMI_TPC_PREAM_EHT240, + WMI_TPC_PREAM_EHT280, + WMI_TPC_PREAM_EHT320, + WMI_TPC_PREAM_MAX +}; + +enum ath12k_debug_tpc_stats_ctl_mode { + ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ, + + ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23, + ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120 +}; + +enum ath12k_debug_tpc_stats_support_modes { + ATH12K_TPC_STATS_SUPPORT_160 = 0, + ATH12K_TPC_STATS_SUPPORT_320, + ATH12K_TPC_STATS_SUPPORT_AX, + ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS, + ATH12K_TPC_STATS_SUPPORT_BE, + ATH12K_TPC_STATS_SUPPORT_BE_PUNC, +}; + #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index 29c7ec3260da..75f80df2aa0c 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_REG_H @@ -13,6 +13,9 @@ struct ath12k_base; struct ath12k; +#define ATH12K_2GHZ_MAX_FREQUENCY 2495 +#define ATH12K_5GHZ_MAX_FREQUENCY 5920 + /* DFS regdomains supported by Firmware */ enum ath12k_dfs_region { ATH12K_DFS_REG_UNSET, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 1b914dc552f7..1ba33e30ddd2 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -4640,6 +4640,7 @@ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_HT, WMI_RATE_PREAMBLE_VHT, WMI_RATE_PREAMBLE_HE, + WMI_RATE_PREAMBLE_EHT, }; /** From 33051d3fd2471b0965b95b58657bf2a4b5c1a013 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 095/465] wifi: ath11k: update channel list in reg notifier instead reg worker JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-23133 commit 933ab187e679e6fbdeea1835ae39efcc59c022d2 Author: Wen Gong Date: Fri Jan 17 14:17:36 2025 +0800 wifi: ath11k: update channel list in reg notifier instead reg worker Currently when ath11k gets a new channel list, it will be processed according to the following steps: 1. update new channel list to cfg80211 and queue reg_work. 2. cfg80211 handles new channel list during reg_work. 3. update cfg80211's handled channel list to firmware by ath11k_reg_update_chan_list(). But ath11k will immediately execute step 3 after reg_work is just queued. Since step 2 is asynchronous, cfg80211 may not have completed handling the new channel list, which may leading to an out-of-bounds write error: BUG: KASAN: slab-out-of-bounds in ath11k_reg_update_chan_list Call Trace: ath11k_reg_update_chan_list+0xbfe/0xfe0 [ath11k] kfree+0x109/0x3a0 ath11k_regd_update+0x1cf/0x350 [ath11k] ath11k_regd_update_work+0x14/0x20 [ath11k] process_one_work+0xe35/0x14c0 Should ensure step 2 is completely done before executing step 3. Thus Wen raised patch[1]. When flag NL80211_REGDOM_SET_BY_DRIVER is set, cfg80211 will notify ath11k after step 2 is done. So enable the flag NL80211_REGDOM_SET_BY_DRIVER then cfg80211 will notify ath11k after step 2 is done. At this time, there will be no KASAN bug during the execution of the step 3. [1] https://patchwork.kernel.org/project/linux-wireless/patch/20230201065313.27203-1-quic_wgong@quicinc.com/ Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Fixes: f45cb6b29cd3 ("wifi: ath11k: avoid deadlock during regulatory update in ath11k_regd_update()") Signed-off-by: Wen Gong Signed-off-by: Kang Yang Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250117061737.1921-2-quic_kangyang@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/reg.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index b0f289784dd3..7bfe47ad62a0 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -55,6 +55,19 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "driver initiated regd update\n"); + if (ar->state != ATH11K_STATE_ON) + return; + + ret = ath11k_reg_update_chan_list(ar, true); + if (ret) + ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret); + + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -293,12 +306,6 @@ int ath11k_regd_update(struct ath11k *ar) if (ret) goto err; - if (ar->state == ATH11K_STATE_ON) { - ret = ath11k_reg_update_chan_list(ar, true); - if (ret) - goto err; - } - return 0; err: ath11k_warn(ab, "failed to perform regd update : %d\n", ret); @@ -977,6 +984,7 @@ void ath11k_regd_update_work(struct work_struct *work) void ath11k_reg_init(struct ath11k *ar) { ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; } From c3f24104e35d1f94f63a30613535dddeaecad6f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 096/465] wifi: ath11k: update channel list in worker when wait flag is set JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 Author: Wen Gong Date: Fri Jan 17 14:17:37 2025 +0800 wifi: ath11k: update channel list in worker when wait flag is set With previous patch "wifi: ath11k: move update channel list from update reg worker to reg notifier", ath11k_reg_update_chan_list() will be called during reg_process_self_managed_hint(). reg_process_self_managed_hint() will hold rtnl_lock all the time. But ath11k_reg_update_chan_list() may increase the occupation time of rtnl_lock, because when wait flag is set, wait_for_completion_timeout() will be called during 11d/hw scan. Should minimize the occupation time of rtnl_lock as much as possible to avoid interfering with rest of the system. So move the update channel list operation to a new worker, so that wait_for_completion_timeout() won't be called and will not increase the occupation time of rtnl_lock. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Signed-off-by: Wen Gong Co-developed-by: Kang Yang Signed-off-by: Kang Yang Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250117061737.1921-3-quic_kangyang@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/core.c | 1 + drivers/net/wireless/ath/ath11k/core.h | 5 +- drivers/net/wireless/ath/ath11k/mac.c | 14 +++++ drivers/net/wireless/ath/ath11k/reg.c | 85 ++++++++++++++++++-------- drivers/net/wireless/ath/ath11k/reg.h | 3 +- drivers/net/wireless/ath/ath11k/wmi.h | 1 + 6 files changed, 81 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 85077247b025..12dd37c2e904 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -2056,6 +2056,7 @@ void ath11k_core_halt(struct ath11k *ar) ath11k_mac_scan_finish(ar); ath11k_mac_peer_cleanup_all(ar); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->update_11d_work); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 92b14fc43cb6..9842a4d65c7b 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -686,7 +686,7 @@ struct ath11k { struct mutex conf_mutex; /* protects the radio specific data like debug stats, ppdu_stats_info stats, * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, - * channel context data, survey info, test mode data. + * channel context data, survey info, test mode data, channel_update_queue. */ spinlock_t data_lock; @@ -744,6 +744,9 @@ struct ath11k { struct completion bss_survey_done; struct work_struct regd_update_work; + struct work_struct channel_update_work; + /* protected with data_lock */ + struct list_head channel_update_queue; struct work_struct wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 586898e5318f..14ed53836589 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6332,6 +6332,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) { struct ath11k *ar = hw->priv; struct htt_ppdu_stats_info *ppdu_stats, *tmp; + struct scan_chan_list_params *params; int ret; ath11k_mac_drain_tx(ar); @@ -6347,6 +6348,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->update_11d_work); @@ -6356,10 +6358,19 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) } spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { list_del(&ppdu_stats->list); kfree(ppdu_stats); } + + while ((params = list_first_entry_or_null(&ar->channel_update_queue, + struct scan_chan_list_params, + list))) { + list_del(¶ms->list); + kfree(params); + } + spin_unlock_bh(&ar->data_lock); rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); @@ -10063,6 +10074,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { static void __ath11k_mac_unregister(struct ath11k *ar) { + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); ieee80211_unregister_hw(ar->hw); @@ -10462,6 +10474,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab) init_completion(&ar->thermal.wmi_sync); INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); + INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work); + INIT_LIST_HEAD(&ar->channel_update_queue); INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 7bfe47ad62a0..d62a2014315a 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -124,32 +124,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) struct channel_param *ch; enum nl80211_band band; int num_channels = 0; - int i, ret, left; - - if (wait && ar->state_11d != ATH11K_11D_IDLE) { - left = wait_for_completion_timeout(&ar->completed_11d_scan, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) { - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive 11d scan complete: timed out\n"); - ar->state_11d = ATH11K_11D_IDLE; - } - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "11d scan wait left time %d\n", left); - } - - if (wait && - (ar->scan.state == ATH11K_SCAN_STARTING || - ar->scan.state == ATH11K_SCAN_RUNNING)) { - left = wait_for_completion_timeout(&ar->scan.completed, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive hw scan complete: timed out\n"); - - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "hw scan wait left time %d\n", left); - } + int i, ret = 0; if (ar->state == ATH11K_STATE_RESTARTING) return 0; @@ -231,6 +206,16 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) } } + if (wait) { + spin_lock_bh(&ar->data_lock); + list_add_tail(¶ms->list, &ar->channel_update_queue); + spin_unlock_bh(&ar->data_lock); + + queue_work(ar->ab->workqueue, &ar->channel_update_work); + + return 0; + } + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); kfree(params); @@ -811,6 +796,54 @@ ret: return new_regd; } +void ath11k_regd_update_chan_list_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + channel_update_work); + struct scan_chan_list_params *params; + struct list_head local_update_list; + int left; + + INIT_LIST_HEAD(&local_update_list); + + spin_lock_bh(&ar->data_lock); + list_splice_tail_init(&ar->channel_update_queue, &local_update_list); + spin_unlock_bh(&ar->data_lock); + + while ((params = list_first_entry_or_null(&local_update_list, + struct scan_chan_list_params, + list))) { + if (ar->state_11d != ATH11K_11D_IDLE) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH11K_11D_IDLE; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if ((ar->scan.state == ATH11K_SCAN_STARTING || + ar->scan.state == ATH11K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + ath11k_wmi_send_scan_chan_list_cmd(ar, params); + list_del(¶ms->list); + kfree(params); + } +} + static bool ath11k_reg_is_world_alpha(char *alpha) { if (alpha[0] == '0' && alpha[1] == '0') diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 263ea9061948..72b483594015 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_REG_H @@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar); void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); +void ath11k_regd_update_chan_list_work(struct work_struct *work); struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, struct cur_regulatory_info *reg_info, bool intersect, diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 508bd496488a..9fcffaa2f383 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3817,6 +3817,7 @@ struct wmi_stop_scan_cmd { }; struct scan_chan_list_params { + struct list_head list; u32 pdev_id; u16 nallchans; struct channel_param ch_param[]; From 80f6dddb5c9546cd38a78c571f2168f9d18be9c7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 097/465] wifi: rtw89: call power_on ahead before selecting firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d078f5857a00c06fa0ddee26d3cb722e938e1688 Author: Ping-Ke Shih Date: Mon Feb 3 15:29:08 2025 +0800 wifi: rtw89: call power_on ahead before selecting firmware Driver selects firmware by hardware version, which normally can be read from registers before selecting firmware. However, certain chips such as RTL8851B, it needs to read hardware version from efuse while doing power_on, but do power_on after selecting firmware in current flow. To resolve this flow problem, move power_on out from rtw89_mac_partial_init(), and call rtw89_mac_pwr_on() separately at proper places to have expected flow. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250203072911.47313-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 23 +++++++++++------- drivers/net/wireless/realtek/rtw89/mac.c | 29 ++++++++++++++++------- drivers/net/wireless/realtek/rtw89/mac.h | 1 + 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index da41a417dd7d..ad28f11f2d24 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5040,8 +5040,6 @@ static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) rtw89_hci_mac_pre_deinit(rtwdev); - rtw89_mac_pwr_off(rtwdev); - return 0; } @@ -5122,36 +5120,45 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev) rtw89_read_chip_ver(rtwdev); + ret = rtw89_mac_pwr_on(rtwdev); + if (ret) { + rtw89_err(rtwdev, "failed to power on\n"); + return ret; + } + ret = rtw89_wait_firmware_completion(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to wait firmware completion\n"); - return ret; + goto out; } ret = rtw89_fw_recognize(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to recognize firmware\n"); - return ret; + goto out; } ret = rtw89_chip_efuse_info_setup(rtwdev); if (ret) - return ret; + goto out; ret = rtw89_fw_recognize_elements(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to recognize firmware elements\n"); - return ret; + goto out; } ret = rtw89_chip_board_info_setup(rtwdev); if (ret) - return ret; + goto out; rtw89_core_setup_rfe_parms(rtwdev); rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); - return 0; +out: + rtw89_mac_pwr_off(rtwdev); + + return ret; } EXPORT_SYMBOL(rtw89_chip_info_setup); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index f698807790ff..3f38f8ed6876 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1495,6 +1495,21 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) #undef PWR_ACT } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_power_switch(rtwdev, true); + if (ret) { + rtw89_mac_power_switch(rtwdev, false); + ret = rtw89_mac_power_switch(rtwdev, true); + if (ret) + return ret; + } + + return 0; +} + void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev) { rtw89_mac_power_switch(rtwdev, false); @@ -3996,14 +4011,6 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) { int ret; - ret = rtw89_mac_power_switch(rtwdev, true); - if (ret) { - rtw89_mac_power_switch(rtwdev, false); - ret = rtw89_mac_power_switch(rtwdev, true); - if (ret) - return ret; - } - rtw89_mac_ctrl_hci_dma_trx(rtwdev, true); if (include_bb) { @@ -4036,6 +4043,10 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) bool include_bb = !!chip->bbmcu_nr; int ret; + ret = rtw89_mac_pwr_on(rtwdev); + if (ret) + return ret; + ret = rtw89_mac_partial_init(rtwdev, include_bb); if (ret) goto fail; @@ -4067,7 +4078,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) return ret; fail: - rtw89_mac_power_switch(rtwdev, false); + rtw89_mac_pwr_off(rtwdev); return ret; } diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 8edea96d037f..6389f54264e5 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1145,6 +1145,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l rtw89_write32_set(rtwdev, reg, bit); } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev); void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev); int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb); int rtw89_mac_init(struct rtw89_dev *rtwdev); From 9e8ae679c4a1739ebd11c71fd4acd6f5b363d96a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:32 +0200 Subject: [PATCH 098/465] wifi: rtw89: ps: update H2C command with more info for PS JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5dde1a569c13d1c97cde8445c2abcd3114f6e6ca Author: Eric Huang Date: Mon Feb 3 15:29:09 2025 +0800 wifi: rtw89: ps: update H2C command with more info for PS Adding beacon BW offset, OP1dB table and rfe_type to lps_ml_cmn_info. These information will help FW to configure RX more accurately. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250203072911.47313-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 5 ++++- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 18 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 6 +++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index ad28f11f2d24..8d5dc22fb508 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2192,8 +2192,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, } pkt_stat->beacon_nr++; - if (phy_ppdu) + if (phy_ppdu) { ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); + if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) + rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; + } pkt_stat->beacon_rate = desc_info->data_rate; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 61fe2705cac6..b3fdd8eded21 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3501,6 +3501,7 @@ struct rtw89_vif_link { u8 self_role; u8 wmm; u8 bcn_hit_cond; + u8 bcn_bw_idx; u8 hit_rule; u8 last_noa_nr; u64 sync_bcn_tsf; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 1afce0a0b905..c86a0d328435 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2795,7 +2795,9 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, { const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12}; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_h2c_lps_ml_cmn_info *h2c; struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; @@ -2803,6 +2805,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, u32 len = sizeof(*h2c); unsigned int link_id; struct sk_buff *skb; + u8 beacon_bw_ofst; u8 gain_band; u32 done; u8 path; @@ -2820,9 +2823,10 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, skb_put(skb, len); h2c = (struct rtw89_h2c_lps_ml_cmn_info *)skb->data; - h2c->fmt_id = 0x1; + h2c->fmt_id = 0x3; h2c->mlo_dbcc_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode); + h2c->rfe_type = efuse->rfe_type; rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) { path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; @@ -2843,9 +2847,21 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, h2c->tia_gain[rtwvif_link->phy_idx][i] = cpu_to_le16(gain->tia_gain[gain_band][bw_idx][path][i]); } + + if (rtwvif_link->bcn_bw_idx < ARRAY_SIZE(bcn_bw_ofst)) { + beacon_bw_ofst = bcn_bw_ofst[rtwvif_link->bcn_bw_idx]; + h2c->dup_bcn_ofst[rtwvif_link->phy_idx] = beacon_bw_ofst; + } + memcpy(h2c->lna_gain[rtwvif_link->phy_idx], gain->lna_gain[gain_band][bw_idx][path], LNA_GAIN_NUM); + memcpy(h2c->tia_lna_op1db[rtwvif_link->phy_idx], + gain->tia_lna_op1db[gain_band][bw_idx][path], + LNA_GAIN_NUM + 1); + memcpy(h2c->lna_op1db[rtwvif_link->phy_idx], + gain->lna_op1db[gain_band][bw_idx][path], + LNA_GAIN_NUM); } rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index fb107c0a29d7..994d109a9c3c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1801,7 +1801,8 @@ struct rtw89_h2c_lps_ch_info { struct rtw89_h2c_lps_ml_cmn_info { u8 fmt_id; - u8 rsvd0[3]; + u8 rfe_type; + u8 rsvd0[2]; __le32 mlo_dbcc_mode; u8 central_ch[RTW89_PHY_NUM]; u8 pri_ch[RTW89_PHY_NUM]; @@ -1812,6 +1813,9 @@ struct rtw89_h2c_lps_ml_cmn_info { __le16 tia_gain[RTW89_PHY_NUM][TIA_GAIN_NUM]; u8 lna_gain[RTW89_PHY_NUM][LNA_GAIN_NUM]; u8 rsvd2[2]; + u8 tia_lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM + 1]; + u8 lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM]; + u8 dup_bcn_ofst[RTW89_PHY_NUM]; } __packed; static inline void RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(void *cmd, u32 val) From 40455f65672cb5ad95fc75c852ca61391d87ac2b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 099/465] wifi: rtw89: fw: validate multi-firmware header before accessing JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1f0efffd597893404aea5c3d4f1bdaa1c61d4434 Author: Ping-Ke Shih Date: Mon Feb 3 15:29:10 2025 +0800 wifi: rtw89: fw: validate multi-firmware header before accessing A firmeware file contains multi-firmware with a header to represent contents. The mfw_hdr->fw_nr is to define number of firmware in file. +-----+-------+------+---------+--------------+ | sig | fw_nr | rsvd | version | reserved | +---------------------------------------------+ -- fw 0 | cv | type | mp | rsvd | shift | size | rsvd | \ +---------------------------------------------+ | fw 1 | cv | type | mp | rsvd | shift | size | rsvd | | mfw_hdr->fw_nr +---------------------------------------------+ | fw N-1 | ... | / +=============================================+ -- | fw 0 content | | (pointed by fw0 shift/size) | +=============================================+ To avoid Coverity warning, validate header is in range of firmware size, and also validate the range of actual firmware content is in range. Addresses-Coverity-ID: 1494046 ("Untrusted loop bound") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250203072911.47313-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c86a0d328435..68e80e54ab5f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -489,6 +489,30 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, } } +static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev, + const struct firmware *firmware, + const struct rtw89_mfw_hdr *mfw_hdr) +{ + const void *mfw = firmware->data; + u32 mfw_len = firmware->size; + u8 fw_nr = mfw_hdr->fw_nr; + const void *ptr; + + if (fw_nr == 0) { + rtw89_err(rtwdev, "mfw header has no fw entry\n"); + return -ENOENT; + } + + ptr = &mfw_hdr->info[fw_nr]; + + if (ptr > mfw + mfw_len) { + rtw89_err(rtwdev, "mfw header out of address\n"); + return -EFAULT; + } + + return 0; +} + static int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, struct rtw89_fw_suit *fw_suit, bool nowarn) @@ -499,6 +523,7 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, u32 mfw_len = firmware->size; const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw; const struct rtw89_mfw_info *mfw_info = NULL, *tmp; + int ret; int i; if (mfw_hdr->sig != RTW89_MFW_SIG) { @@ -511,6 +536,10 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, return 0; } + ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); + if (ret) + return ret; + for (i = 0; i < mfw_hdr->fw_nr; i++) { tmp = &mfw_hdr->info[i]; if (tmp->type != type) @@ -540,6 +569,12 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, found: fw_suit->data = mfw + le32_to_cpu(mfw_info->shift); fw_suit->size = le32_to_cpu(mfw_info->size); + + if (fw_suit->data + fw_suit->size > mfw + mfw_len) { + rtw89_err(rtwdev, "fw_suit %d out of address\n", type); + return -EFAULT; + } + return 0; } From 33742da55ac4bc31f9e030aaf797d95f216b4b0c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 100/465] wifi: rtw89: fw: validate multi-firmware header before getting its size JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2b8bdc5237014cc61784b3676cbaca5325959f3d Author: Ping-Ke Shih Date: Mon Feb 3 15:29:11 2025 +0800 wifi: rtw89: fw: validate multi-firmware header before getting its size To access firmware elements appended after multi-firmware, add its size as offset to get start address of firmware elements. +-----+-------+------+---------+--------------+ -- | sig | fw_nr | rsvd | version | reserved | \ +---------------------------------------------+ | fw 0 | cv | type | mp | rsvd | shift | size | rsvd | | +---------------------------------------------+ | fw 1 | cv | type | mp | rsvd | shift | size | rsvd | | +---------------------------------------------+ | fw N-1 | ... | | +=============================================+ | mfw size | fw 0 content | | +=============================================+ | | fw 1 content | | +=============================================+ | | ... | | +=============================================+ | | fw N -1 content | | +=============================================+ --/ | fw element TLV X | +=============================================+ | fw element TLV Y | +=============================================+ | fw element TLV Z | +=============================================+ To avoid Coverity warning when getting mfw size, validate it header ahead. Addresses-Coverity-ID: 1544385 ("Untrusted array index read") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250203072911.47313-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 68e80e54ab5f..35b86970db2a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -586,12 +586,17 @@ static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev) (const struct rtw89_mfw_hdr *)firmware->data; const struct rtw89_mfw_info *mfw_info; u32 size; + int ret; if (mfw_hdr->sig != RTW89_MFW_SIG) { rtw89_warn(rtwdev, "not mfw format\n"); return 0; } + ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); + if (ret) + return ret; + mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1]; size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size); From c6411a6eef10e9e816bf5da7d657d3043a2f5f3f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 101/465] wifi: rtw89: coex: Assign value over than 0 to avoid firmware timer hang JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2e4c4717b3f6f019c71af984564b6e4d0ae8d0bd Author: Ching-Te Ku Date: Wed Feb 5 09:32:31 2025 +0800 wifi: rtw89: coex: Assign value over than 0 to avoid firmware timer hang If the slot duration is 0, the firmware timer will trigger timer hang at the timer initializing state in a low rate due to hardware algorithm. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250205013233.10945-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 2c0ccf2f8bd9..806a51782467 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -89,10 +89,10 @@ static const struct rtw89_btc_fbtc_slot s_def[] = { [CXST_B4] = __DEF_FBTC_SLOT(50, 0xe5555555, SLOT_MIX), [CXST_LK] = __DEF_FBTC_SLOT(20, 0xea5a5a5a, SLOT_ISO), [CXST_BLK] = __DEF_FBTC_SLOT(500, 0x55555555, SLOT_MIX), - [CXST_E2G] = __DEF_FBTC_SLOT(0, 0xea5a5a5a, SLOT_MIX), - [CXST_E5G] = __DEF_FBTC_SLOT(0, 0xffffffff, SLOT_ISO), + [CXST_E2G] = __DEF_FBTC_SLOT(5, 0xea5a5a5a, SLOT_MIX), + [CXST_E5G] = __DEF_FBTC_SLOT(5, 0xffffffff, SLOT_ISO), [CXST_EBT] = __DEF_FBTC_SLOT(5, 0xe5555555, SLOT_MIX), - [CXST_ENULL] = __DEF_FBTC_SLOT(0, 0xaaaaaaaa, SLOT_ISO), + [CXST_ENULL] = __DEF_FBTC_SLOT(5, 0xaaaaaaaa, SLOT_ISO), [CXST_WLK] = __DEF_FBTC_SLOT(250, 0xea5a5a5a, SLOT_MIX), [CXST_W1FDD] = __DEF_FBTC_SLOT(50, 0xffffffff, SLOT_ISO), [CXST_B1FDD] = __DEF_FBTC_SLOT(50, 0xffffdfff, SLOT_ISO), From 849e7e10d9ad7535f45b29fa10101b955a76cb32 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 102/465] wifi: rtw89: coex: To avoid TWS serials A2DP lag, adjust slot arrangement JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e53aa85e4b8a839a8a0daf283339357bdb0bcf1a Author: Ching-Te Ku Date: Wed Feb 5 09:32:32 2025 +0800 wifi: rtw89: coex: To avoid TWS serials A2DP lag, adjust slot arrangement The TWS(True wireless stereo) serials Bluetooth audio device need to keep packet traffic not only with DUT, it also need to synchronize packet with its assistant earbud. And all the BR/EDR Bluetooth device need to do page scan to keep re-connect event sensitively between different DUT. These behavior will make TWS Bluetooth device cost more time slot to keep the good audio performance. This patch decrease half of Wi-Fi slot(from 40ms to 20ms) in a single cycle. Make the slot more flexible to prevent audio lag. The single cycle will be shorter, then it will bring some slot protection cost make Wi-Fi throughput decrease about 5%. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250205013233.10945-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 806a51782467..28d29fa17316 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -4583,17 +4583,11 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev) static void _action_bt_a2dp(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; - struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_dm *dm = &btc->dm; _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - if (a2dp.vendor_id == 0x4c || dm->leak_ap || bt_linfo->slave_role) - dm->slot_dur[CXST_W1] = 20; - else - dm->slot_dur[CXST_W1] = 40; - + dm->slot_dur[CXST_W1] = 20; dm->slot_dur[CXST_B1] = BTC_B1_MAX; switch (btc->cx.state_map) { @@ -4690,17 +4684,11 @@ static void _action_bt_pan(struct rtw89_dev *rtwdev) static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; - struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_dm *dm = &btc->dm; _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - if (a2dp.vendor_id == 0x4c || dm->leak_ap || bt_linfo->slave_role) - dm->slot_dur[CXST_W1] = 20; - else - dm->slot_dur[CXST_W1] = 40; - + dm->slot_dur[CXST_W1] = 20; dm->slot_dur[CXST_B1] = BTC_B1_MAX; switch (btc->cx.state_map) { From 108ca12f89af674afde7e6b175df348aa59ba128 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 103/465] wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.3 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f94ba3c640f6e95653c6e20661c0dd12ee5e2b66 Author: Ching-Te Ku Date: Wed Feb 5 09:32:33 2025 +0800 wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.3 Refine the TWS Bluetooth related coexistence mechanism. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250205013233.10945-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 28d29fa17316..858ff0cd1a23 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -10,7 +10,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x07000213 +#define RTW89_COEX_VERSION 0x07000313 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 From 27a65cc5d17871dfafda6bf53305df57fc61188c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 104/465] wifi: rtw89: regd: avoid using BITMAP_FROM_U64() to assign function bitmap JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 85c726b20f59f3069f6f243335be54afd5b46d9c Author: Ping-Ke Shih Date: Wed Feb 5 09:40:51 2025 +0800 wifi: rtw89: regd: avoid using BITMAP_FROM_U64() to assign function bitmap Since there are two function features for now, func_bitmap[] has single one element, which BITMAP_FROM_U64() generating two elements is exceeded. Change to assign function bitmap barely. With i386-allmodconfig (a 32 bit system), it throws >> include/linux/bitmap.h:736:33: warning: excess elements in array initializer 736 | ((unsigned long) ((u64)(n) >> 32)) | ^ drivers/net/wireless/realtek/rtw89/regd.c:16:34: note: in expansion of macro 'BITMAP_FROM_U64' 16 | .func_bitmap = { BITMAP_FROM_U64(_fmap), }, \ | ^~~~~~~~~~~~~~~ drivers/net/wireless/realtek/rtw89/regd.c:20:9: note: in expansion of macro 'COUNTRY_REGD' 20 | COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0); | ^~~~~~~~~~~~ Fixes: 79a36fc56bea ("wifi: rtw89: regd: handle supported regulatory functions by country") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202502031932.BMQ4lhJT-lkp@intel.com/ Cc: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250205014051.13765-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/regd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 505a3d0767c6..0e67d0f128dd 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -16,9 +16,11 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request .txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \ .txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \ .txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \ - .func_bitmap = { BITMAP_FROM_U64(_fmap), }, \ + .func_bitmap = { _fmap, }, \ } +static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC); + static const struct rtw89_regd rtw89_ww_regd = COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0); From df0d6626371ec95f7f8c0b71861ac319be551c31 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 105/465] wifi: rtw88: Fix a typo of debug message in rtw8723d_iqk_check_tx_failed() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 105dc94233e48ff30e572a50fb39d7e3dec810fa Author: Andrew Kreimer Date: Mon Feb 3 20:08:27 2025 +0200 wifi: rtw88: Fix a typo of debug message in rtw8723d_iqk_check_tx_failed() There is a typo in debug messages: - afer -> after Fix it via codespell. Signed-off-by: Andrew Kreimer Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250203180913.5435-1-algonell@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/rtw8723d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c index eeca31bf71f1..87715bd54860 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c @@ -444,7 +444,7 @@ static u8 rtw8723d_iqk_check_tx_failed(struct rtw_dev *rtwdev, rtw_read32(rtwdev, REG_IQK_RES_TX), rtw_read32(rtwdev, REG_IQK_RES_TY)); rtw_dbg(rtwdev, RTW_DBG_RFK, - "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(afer IQK) = 0x%x\n", + "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(after IQK) = 0x%x\n", rtw_read32(rtwdev, 0xe90), rtw_read32(rtwdev, 0xe98)); @@ -472,7 +472,7 @@ static u8 rtw8723d_iqk_check_rx_failed(struct rtw_dev *rtwdev, rtw_read32(rtwdev, REG_IQK_RES_RY)); rtw_dbg(rtwdev, RTW_DBG_RFK, - "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(afer IQK) = 0x%x\n", + "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(after IQK) = 0x%x\n", rtw_read32(rtwdev, 0xea0), rtw_read32(rtwdev, 0xea8)); From 2e24f73ac4e939192d302b4519ddc2a275a62fb6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 106/465] wifi: rtw88: Fix __rtw_download_firmware() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8425f5c8f04dbcf11ade78f984a494fc0b90e7a0 Author: Bitterblue Smith Date: Tue Feb 4 20:36:56 2025 +0200 wifi: rtw88: Fix __rtw_download_firmware() for RTL8814AU Don't call ltecoex_read_reg() and ltecoex_reg_write() when the ltecoex_addr member of struct rtw_chip_info is NULL. The RTL8814AU doesn't have this feature. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/55b5641f-094e-4f94-9f79-ac053733f2cf@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/mac.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index cae9cca6dca3..63edf6461de8 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -784,7 +784,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev, if (!check_firmware_size(data, size)) return -EINVAL; - if (!ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) + if (rtwdev->chip->ltecoex_addr && + !ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) return -EBUSY; wlan_cpu_enable(rtwdev, false); @@ -802,7 +803,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev, wlan_cpu_enable(rtwdev, true); - if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) { + if (rtwdev->chip->ltecoex_addr && + !ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) { ret = -EBUSY; goto dlfw_fail; } From c241929c9c94be28ac75ed298cec25c37b8aae65 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:33 +0200 Subject: [PATCH 107/465] wifi: rtw88: Fix download_firmware_validate() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9e8243025cc06abc975c876dffda052073207ab3 Author: Bitterblue Smith Date: Tue Feb 4 20:37:36 2025 +0200 wifi: rtw88: Fix download_firmware_validate() for RTL8814AU After the firmware is uploaded, download_firmware_validate() checks some bits in REG_MCUFW_CTRL to see if everything went okay. The RTL8814AU power on sequence sets bits 13 and 12 to 2, which this function does not expect, so it thinks the firmware upload failed. Make download_firmware_validate() ignore bits 13 and 12. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/049d2887-22fc-47b7-9e59-62627cb525f8@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/reg.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h index e438405fba56..209b6fc08a73 100644 --- a/drivers/net/wireless/realtek/rtw88/reg.h +++ b/drivers/net/wireless/realtek/rtw88/reg.h @@ -130,6 +130,7 @@ #define BIT_SHIFT_ROM_PGE 16 #define BIT_FW_INIT_RDY BIT(15) #define BIT_FW_DW_RDY BIT(14) +#define BIT_CPU_CLK_SEL (BIT(12) | BIT(13)) #define BIT_RPWM_TOGGLE BIT(7) #define BIT_RAM_DL_SEL BIT(7) /* legacy only */ #define BIT_DMEM_CHKSUM_OK BIT(6) @@ -147,7 +148,7 @@ BIT_CHECK_SUM_OK) #define FW_READY_LEGACY (BIT_MCUFWDL_RDY | BIT_FWDL_CHK_RPT | \ BIT_WINTINI_RDY | BIT_RAM_DL_SEL) -#define FW_READY_MASK 0xffff +#define FW_READY_MASK (0xffff & ~BIT_CPU_CLK_SEL) #define REG_MCU_TST_CFG 0x84 #define VAL_FW_TRIGGER 0x1 From 86b88281ea066300559e63ce235d9daf1e225ae8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 108/465] wifi: rtw88: Extend struct rtw_pwr_track_tbl for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 62f726848da42554e6d270dfda17ed19bfa3456f Author: Bitterblue Smith Date: Tue Feb 4 20:38:17 2025 +0200 wifi: rtw88: Extend struct rtw_pwr_track_tbl for RTL8814AU Currently this struct has the members required for chips with 2 RF paths. Add more members to support chips with 4 RF paths, like the RTL8814AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/be5a73f4-a0fe-43d6-9457-930cde199284@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 62cd4c526301..e40e62302984 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1130,14 +1130,26 @@ struct rtw_rfe_def { * For 2G there are cck rate and ofdm rate with different settings. */ struct rtw_pwr_track_tbl { + const u8 *pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_2gd_n; + const u8 *pwrtrk_2gd_p; + const u8 *pwrtrk_2gc_n; + const u8 *pwrtrk_2gc_p; const u8 *pwrtrk_2gb_n; const u8 *pwrtrk_2gb_p; const u8 *pwrtrk_2ga_n; const u8 *pwrtrk_2ga_p; + const u8 *pwrtrk_2g_cckd_n; + const u8 *pwrtrk_2g_cckd_p; + const u8 *pwrtrk_2g_cckc_n; + const u8 *pwrtrk_2g_cckc_p; const u8 *pwrtrk_2g_cckb_n; const u8 *pwrtrk_2g_cckb_p; const u8 *pwrtrk_2g_ccka_n; From 9f581a9ba9db956937d31f4518cf720c1808f238 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 109/465] wifi: rtw88: Extend rf_base_addr and rf_sipi_addr for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d80e7d9b6ba38102f92559dbb647330216ea290b Author: Bitterblue Smith Date: Tue Feb 4 20:38:43 2025 +0200 wifi: rtw88: Extend rf_base_addr and rf_sipi_addr for RTL8814AU These members of struct rtw_chip_info each have a size of 2. Increase their size to 4, which is the number of RF paths the RTL8814AU has. This is required to read and write the RF registers of the RTL8814AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/7a4d8209-b8af-4943-b5de-f53d6edf591a@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index e40e62302984..ba64d269521a 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1239,8 +1239,8 @@ struct rtw_chip_info { const struct rtw_hw_reg *dig; const struct rtw_hw_reg *dig_cck; - u32 rf_base_addr[2]; - u32 rf_sipi_addr[2]; + u32 rf_base_addr[RTW_RF_PATH_MAX]; + u32 rf_sipi_addr[RTW_RF_PATH_MAX]; const struct rtw_rf_sipi_addr *rf_sipi_read_addr; u8 fix_rf_phy_num; const struct rtw_ltecoex_addr *ltecoex_addr; From 413a74c27afc9f6b1b8cc625e307db6af2f58e05 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 110/465] wifi: rtw88: Extend rtw_fw_send_ra_info() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8f0076726b66a70727a1bef5c087c60291e90ad8 Author: Bitterblue Smith Date: Tue Feb 4 20:39:29 2025 +0200 wifi: rtw88: Extend rtw_fw_send_ra_info() for RTL8814AU The existing code is suitable for chips with up to 2 spatial streams. Inform the firmware about the rates it's allowed to use when transmitting 3 spatial streams. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/08e2f328-1aab-4e50-93ac-c1e5dd9541ac@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/fw.c | 15 +++++++++++++++ drivers/net/wireless/realtek/rtw88/fw.h | 1 + drivers/net/wireless/realtek/rtw88/main.h | 1 + 3 files changed, 17 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 02389b7c6876..6b563ac489a7 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -735,6 +735,7 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; bool disable_pt = true; + u32 mask_hi; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO); @@ -755,6 +756,20 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, si->init_ra_lv = 0; rtw_fw_send_h2c_command(rtwdev, h2c_pkt); + + if (rtwdev->chip->id != RTW_CHIP_TYPE_8814A) + return; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO_HI); + + mask_hi = si->ra_mask >> 32; + + SET_RA_INFO_RA_MASK0(h2c_pkt, (mask_hi & 0xff)); + SET_RA_INFO_RA_MASK1(h2c_pkt, (mask_hi & 0xff00) >> 8); + SET_RA_INFO_RA_MASK2(h2c_pkt, (mask_hi & 0xff0000) >> 16); + SET_RA_INFO_RA_MASK3(h2c_pkt, (mask_hi & 0xff000000) >> 24); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect) diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index 404de1b0c407..48ad9ceab6ea 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -557,6 +557,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define H2C_CMD_DEFAULT_PORT 0x2c #define H2C_CMD_RA_INFO 0x40 #define H2C_CMD_RSSI_MONITOR 0x42 +#define H2C_CMD_RA_INFO_HI 0x46 #define H2C_CMD_BCN_FILTER_OFFLOAD_P0 0x56 #define H2C_CMD_BCN_FILTER_OFFLOAD_P1 0x57 #define H2C_CMD_WL_PHY_INFO 0x58 diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index ba64d269521a..993c381e734b 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -191,6 +191,7 @@ enum rtw_chip_type { RTW_CHIP_TYPE_8703B, RTW_CHIP_TYPE_8821A, RTW_CHIP_TYPE_8812A, + RTW_CHIP_TYPE_8814A, }; enum rtw_tx_queue_type { From f93a95879c59372ca22f36c47397a64db4551313 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 111/465] wifi: rtw88: Constify some more structs and arrays JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e66f3b5c7535bb508e9c561a047b32de4ddc1cda Author: Bitterblue Smith Date: Tue Feb 4 20:40:22 2025 +0200 wifi: rtw88: Constify some more structs and arrays These structs and arrays are never modified, so make them const: rtw_band_2ghz rtw_band_5ghz rtw_pci_tx_queue_idx_addr rtw_pci_ops rtw_cck_rates rtw_ofdm_rates rtw_ht_1s_rates rtw_ht_2s_rates rtw_vht_1s_rates rtw_vht_2s_rates rtw_rate_section rtw_rate_size rtw_sdio_ops rtw_usb_ops Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/502f124e-ccf3-4c09-80a4-1e5c5304822b@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.c | 4 ++-- drivers/net/wireless/realtek/rtw88/main.h | 2 +- drivers/net/wireless/realtek/rtw88/pci.c | 4 ++-- drivers/net/wireless/realtek/rtw88/phy.c | 26 ++++++++++++++--------- drivers/net/wireless/realtek/rtw88/phy.h | 16 +++++++------- drivers/net/wireless/realtek/rtw88/sdio.c | 2 +- drivers/net/wireless/realtek/rtw88/usb.c | 2 +- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 0cee0fd8c0ef..8d22df293e78 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -136,7 +136,7 @@ u16 rtw_desc_to_bitrate(u8 desc_rate) return rate.bitrate; } -static struct ieee80211_supported_band rtw_band_2ghz = { +static const struct ieee80211_supported_band rtw_band_2ghz = { .band = NL80211_BAND_2GHZ, .channels = rtw_channeltable_2g, @@ -149,7 +149,7 @@ static struct ieee80211_supported_band rtw_band_2ghz = { .vht_cap = {0}, }; -static struct ieee80211_supported_band rtw_band_5ghz = { +static const struct ieee80211_supported_band rtw_band_5ghz = { .band = NL80211_BAND_5GHZ, .channels = rtw_channeltable_5g, diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 993c381e734b..c982d9d3c5d7 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -61,7 +61,7 @@ enum rtw_hci_type { }; struct rtw_hci { - struct rtw_hci_ops *ops; + const struct rtw_hci_ops *ops; enum rtw_hci_type type; u32 rpwm_addr; diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index 0ecaefc4c83d..bb4c4ccb31d4 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -20,7 +20,7 @@ module_param_named(disable_aspm, rtw_pci_disable_aspm, bool, 0644); MODULE_PARM_DESC(disable_msi, "Set Y to disable MSI interrupt support"); MODULE_PARM_DESC(disable_aspm, "Set Y to disable PCI ASPM support"); -static u32 rtw_pci_tx_queue_idx_addr[] = { +static const u32 rtw_pci_tx_queue_idx_addr[] = { [RTW_TX_QUEUE_BK] = RTK_PCI_TXBD_IDX_BKQ, [RTW_TX_QUEUE_BE] = RTK_PCI_TXBD_IDX_BEQ, [RTW_TX_QUEUE_VI] = RTK_PCI_TXBD_IDX_VIQ, @@ -1591,7 +1591,7 @@ static void rtw_pci_destroy(struct rtw_dev *rtwdev, struct pci_dev *pdev) rtw_pci_io_unmapping(rtwdev, pdev); } -static struct rtw_hci_ops rtw_pci_ops = { +static const struct rtw_hci_ops rtw_pci_ops = { .tx_write = rtw_pci_tx_write, .tx_kick_off = rtw_pci_tx_kick_off, .flush_queues = rtw_pci_flush_queues, diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 8ed20c89d216..f6528016973d 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -52,44 +52,50 @@ static const u32 db_invert_table[12][8] = { 1995262315, 2511886432U, 3162277660U, 3981071706U} }; -u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; -u8 rtw_ofdm_rates[] = { +const u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; + +const u8 rtw_ofdm_rates[] = { DESC_RATE6M, DESC_RATE9M, DESC_RATE12M, DESC_RATE18M, DESC_RATE24M, DESC_RATE36M, DESC_RATE48M, DESC_RATE54M }; -u8 rtw_ht_1s_rates[] = { + +const u8 rtw_ht_1s_rates[] = { DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2, DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5, DESC_RATEMCS6, DESC_RATEMCS7 }; -u8 rtw_ht_2s_rates[] = { + +const u8 rtw_ht_2s_rates[] = { DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10, DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13, DESC_RATEMCS14, DESC_RATEMCS15 }; -u8 rtw_vht_1s_rates[] = { + +const u8 rtw_vht_1s_rates[] = { DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1, DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3, DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5, DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7, DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9 }; -u8 rtw_vht_2s_rates[] = { + +const u8 rtw_vht_2s_rates[] = { DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1, DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3, DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5, DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7, DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 }; -u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { + +const u8 * const rtw_rate_section[RTW_RATE_SECTION_MAX] = { rtw_cck_rates, rtw_ofdm_rates, rtw_ht_1s_rates, rtw_ht_2s_rates, rtw_vht_1s_rates, rtw_vht_2s_rates }; EXPORT_SYMBOL(rtw_rate_section); -u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { +const u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { ARRAY_SIZE(rtw_cck_rates), ARRAY_SIZE(rtw_ofdm_rates), ARRAY_SIZE(rtw_ht_1s_rates), @@ -2214,7 +2220,7 @@ static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev, { struct rtw_hal *hal = &rtwdev->hal; u8 regd = rtw_regd_get(rtwdev); - u8 *rates; + const u8 *rates; u8 size; u8 rate; u8 pwr_idx; @@ -2274,7 +2280,7 @@ EXPORT_SYMBOL(rtw_phy_set_tx_power_level); static void rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, - u8 rs, u8 size, u8 *rates) + u8 rs, u8 size, const u8 *rates) { u8 rate; u8 base_idx, rate_idx; diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h index ccfcbd3ced03..ce6ee16a77dc 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.h +++ b/drivers/net/wireless/realtek/rtw88/phy.h @@ -7,14 +7,14 @@ #include "debug.h" -extern u8 rtw_cck_rates[]; -extern u8 rtw_ofdm_rates[]; -extern u8 rtw_ht_1s_rates[]; -extern u8 rtw_ht_2s_rates[]; -extern u8 rtw_vht_1s_rates[]; -extern u8 rtw_vht_2s_rates[]; -extern u8 *rtw_rate_section[]; -extern u8 rtw_rate_size[]; +extern const u8 rtw_cck_rates[]; +extern const u8 rtw_ofdm_rates[]; +extern const u8 rtw_ht_1s_rates[]; +extern const u8 rtw_ht_2s_rates[]; +extern const u8 rtw_vht_1s_rates[]; +extern const u8 rtw_vht_2s_rates[]; +extern const u8 * const rtw_rate_section[]; +extern const u8 rtw_rate_size[]; void rtw_phy_init(struct rtw_dev *rtwdev); void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index e024061bdbf7..6209a49312f1 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -1147,7 +1147,7 @@ static void rtw_sdio_declaim(struct rtw_dev *rtwdev, sdio_release_host(sdio_func); } -static struct rtw_hci_ops rtw_sdio_ops = { +static const struct rtw_hci_ops rtw_sdio_ops = { .tx_write = rtw_sdio_tx_write, .tx_kick_off = rtw_sdio_tx_kick_off, .setup = rtw_sdio_setup, diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index c4908db4ff0e..c8092fa0d9f1 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -881,7 +881,7 @@ static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable) } } -static struct rtw_hci_ops rtw_usb_ops = { +static const struct rtw_hci_ops rtw_usb_ops = { .tx_write = rtw_usb_tx_write, .tx_kick_off = rtw_usb_tx_kick_off, .setup = rtw_usb_setup, From 30f0abfe809a71f9665b600ed80f7e440c362340 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 112/465] wifi: rtw88: Rename RTW_RATE_SECTION_MAX to RTW_RATE_SECTION_NUM JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ad815f3920035a0c5b6ffe45bddc9fb308194b49 Author: Bitterblue Smith Date: Tue Feb 4 20:40:58 2025 +0200 wifi: rtw88: Rename RTW_RATE_SECTION_MAX to RTW_RATE_SECTION_NUM It fits the meaning of the enum better. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5a1c5a46-8ebb-43b0-9ab1-b78e2a22b3d2@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 12 +++++----- drivers/net/wireless/realtek/rtw88/phy.c | 24 +++++++++---------- drivers/net/wireless/realtek/rtw88/rtw8821c.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 2 +- drivers/net/wireless/realtek/rtw88/sar.c | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index c982d9d3c5d7..057141e196e6 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -168,7 +168,7 @@ enum rtw_rate_section { RTW_RATE_SECTION_VHT_2S, /* keep last */ - RTW_RATE_SECTION_MAX, + RTW_RATE_SECTION_NUM, }; enum rtw_wireless_set { @@ -1937,7 +1937,7 @@ union rtw_sar_cfg { struct rtw_sar { enum rtw_sar_sources src; - union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX]; + union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_NUM]; }; struct rtw_hal { @@ -1981,16 +1981,16 @@ struct rtw_hal { s8 tx_pwr_by_rate_offset_5g[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; s8 tx_pwr_by_rate_base_2g[RTW_RF_PATH_MAX] - [RTW_RATE_SECTION_MAX]; + [RTW_RATE_SECTION_NUM]; s8 tx_pwr_by_rate_base_5g[RTW_RF_PATH_MAX] - [RTW_RATE_SECTION_MAX]; + [RTW_RATE_SECTION_NUM]; s8 tx_pwr_limit_2g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] - [RTW_RATE_SECTION_MAX] + [RTW_RATE_SECTION_NUM] [RTW_MAX_CHANNEL_NUM_2G]; s8 tx_pwr_limit_5g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] - [RTW_RATE_SECTION_MAX] + [RTW_RATE_SECTION_NUM] [RTW_MAX_CHANNEL_NUM_5G]; s8 tx_pwr_tbl[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index f6528016973d..e3a5f8e1e30a 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -88,14 +88,14 @@ const u8 rtw_vht_2s_rates[] = { DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 }; -const u8 * const rtw_rate_section[RTW_RATE_SECTION_MAX] = { +const u8 * const rtw_rate_section[RTW_RATE_SECTION_NUM] = { rtw_cck_rates, rtw_ofdm_rates, rtw_ht_1s_rates, rtw_ht_2s_rates, rtw_vht_1s_rates, rtw_vht_2s_rates }; EXPORT_SYMBOL(rtw_rate_section); -const u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { +const u8 rtw_rate_size[RTW_RATE_SECTION_NUM] = { ARRAY_SIZE(rtw_cck_rates), ARRAY_SIZE(rtw_ofdm_rates), ARRAY_SIZE(rtw_ht_1s_rates), @@ -1596,7 +1596,7 @@ static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, ch_idx = rtw_channel_to_idx(band, ch); if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX || - rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) { + rs >= RTW_RATE_SECTION_NUM || ch_idx < 0) { WARN(1, "wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n", regd, band, bw, rs, ch_idx, pwr_limit); @@ -1701,7 +1701,7 @@ rtw_cfg_txpwr_lmt_by_alt(struct rtw_dev *rtwdev, u8 regd, u8 regd_alt) u8 bw, rs; for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) __cfg_txpwr_lmt_by_alt(&rtwdev->hal, regd, regd_alt, bw, rs); } @@ -2060,7 +2060,7 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, return tx_power; } -/* return RTW_RATE_SECTION_MAX to indicate rate is invalid */ +/* return RTW_RATE_SECTION_NUM to indicate rate is invalid */ static u8 rtw_phy_rate_to_rate_section(u8 rate) { if (rate >= DESC_RATE1M && rate <= DESC_RATE11M) @@ -2076,7 +2076,7 @@ static u8 rtw_phy_rate_to_rate_section(u8 rate) else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) return RTW_RATE_SECTION_VHT_2S; else - return RTW_RATE_SECTION_MAX; + return RTW_RATE_SECTION_NUM; } static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, @@ -2094,7 +2094,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, if (regd > RTW_REGD_WW) return power_limit; - if (rs == RTW_RATE_SECTION_MAX) + if (rs == RTW_RATE_SECTION_NUM) goto err; /* only 20M BW with cck and ofdm */ @@ -2138,7 +2138,7 @@ static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band, .rs = rs, }; - if (rs == RTW_RATE_SECTION_MAX) + if (rs == RTW_RATE_SECTION_NUM) goto err; return rtw_query_sar(rtwdev, &arg); @@ -2227,7 +2227,7 @@ static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev, u8 bw; int i; - if (rs >= RTW_RATE_SECTION_MAX) + if (rs >= RTW_RATE_SECTION_NUM) return; rates = rtw_rate_section[rs]; @@ -2258,7 +2258,7 @@ static void rtw_phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, else rs = RTW_RATE_SECTION_OFDM; - for (; rs < RTW_RATE_SECTION_MAX; rs++) + for (; rs < RTW_RATE_SECTION_NUM; rs++) rtw_phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs); } @@ -2353,7 +2353,7 @@ void rtw_phy_tx_power_limit_config(struct rtw_hal *hal) for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) __rtw_phy_tx_power_limit_config(hal, regd, bw, rs); } @@ -2389,7 +2389,7 @@ void rtw_phy_init_tx_power(struct rtw_dev *rtwdev) /* init tx power limit */ for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) rtw_phy_init_tx_power_limit(rtwdev, regd, bw, rs); } diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index cc152248407c..6abb21067aed 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -709,7 +709,7 @@ static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { if (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S) continue; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 23a29019752d..742a2a05632e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -964,7 +964,7 @@ static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs, &phy_pwr_idx); } diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index ec362a817f5f..2314d160292a 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2746,7 +2746,7 @@ static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev) s8 diff_idx[4]; rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm); - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate]; diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c index 71e61b9c0bec..109ff42eda82 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c @@ -1637,7 +1637,7 @@ void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { if (hal->rf_path_num == 1 && (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S)) diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c index c472f1502b82..50b9c2412bb1 100644 --- a/drivers/net/wireless/realtek/rtw88/sar.c +++ b/drivers/net/wireless/realtek/rtw88/sar.c @@ -97,7 +97,7 @@ int rtw_set_sar_specs(struct rtw_dev *rtwdev, power, BIT(RTW_COMMON_SAR_FCT)); for (j = 0; j < RTW_RF_PATH_MAX; j++) { - for (k = 0; k < RTW_RATE_SECTION_MAX; k++) { + for (k = 0; k < RTW_RATE_SECTION_NUM; k++) { arg = (struct rtw_sar_arg){ .sar_band = idx, .path = j, From b38d7939eae14de24e11740781d9ba29cf1e25e1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 113/465] wifi: rtw88: Extend TX power stuff for 3-4 spatial streams JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0f98a59596579b34932c06aec7d52c1e835fa1f0 Author: Bitterblue Smith Date: Tue Feb 4 20:41:43 2025 +0200 wifi: rtw88: Extend TX power stuff for 3-4 spatial streams Although the RTL8814AU only has 3 spatial streams, maybe some other chip has 4. Correct the TX power index and TX power limit calculations for 3SS and 4SS HT/VHT rates. With this the RTL8814AU can set the TX power correctly. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d0c0e126-0794-4c4e-9203-ea39a707b673@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 5 + drivers/net/wireless/realtek/rtw88/phy.c | 145 ++++++++++++------ drivers/net/wireless/realtek/rtw88/phy.h | 4 + drivers/net/wireless/realtek/rtw88/rtw8821c.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 2 +- drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 2 +- 7 files changed, 111 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 057141e196e6..24ac749271cc 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -166,6 +166,11 @@ enum rtw_rate_section { RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_1S, RTW_RATE_SECTION_VHT_2S, + __RTW_RATE_SECTION_2SS_MAX = RTW_RATE_SECTION_VHT_2S, + RTW_RATE_SECTION_HT_3S, + RTW_RATE_SECTION_HT_4S, + RTW_RATE_SECTION_VHT_3S, + RTW_RATE_SECTION_VHT_4S, /* keep last */ RTW_RATE_SECTION_NUM, diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index e3a5f8e1e30a..b487457d2215 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -88,10 +88,40 @@ const u8 rtw_vht_2s_rates[] = { DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 }; +const u8 rtw_ht_3s_rates[] = { + DESC_RATEMCS16, DESC_RATEMCS17, DESC_RATEMCS18, + DESC_RATEMCS19, DESC_RATEMCS20, DESC_RATEMCS21, + DESC_RATEMCS22, DESC_RATEMCS23 +}; + +const u8 rtw_ht_4s_rates[] = { + DESC_RATEMCS24, DESC_RATEMCS25, DESC_RATEMCS26, + DESC_RATEMCS27, DESC_RATEMCS28, DESC_RATEMCS29, + DESC_RATEMCS30, DESC_RATEMCS31 +}; + +const u8 rtw_vht_3s_rates[] = { + DESC_RATEVHT3SS_MCS0, DESC_RATEVHT3SS_MCS1, + DESC_RATEVHT3SS_MCS2, DESC_RATEVHT3SS_MCS3, + DESC_RATEVHT3SS_MCS4, DESC_RATEVHT3SS_MCS5, + DESC_RATEVHT3SS_MCS6, DESC_RATEVHT3SS_MCS7, + DESC_RATEVHT3SS_MCS8, DESC_RATEVHT3SS_MCS9 +}; + +const u8 rtw_vht_4s_rates[] = { + DESC_RATEVHT4SS_MCS0, DESC_RATEVHT4SS_MCS1, + DESC_RATEVHT4SS_MCS2, DESC_RATEVHT4SS_MCS3, + DESC_RATEVHT4SS_MCS4, DESC_RATEVHT4SS_MCS5, + DESC_RATEVHT4SS_MCS6, DESC_RATEVHT4SS_MCS7, + DESC_RATEVHT4SS_MCS8, DESC_RATEVHT4SS_MCS9 +}; + const u8 * const rtw_rate_section[RTW_RATE_SECTION_NUM] = { rtw_cck_rates, rtw_ofdm_rates, rtw_ht_1s_rates, rtw_ht_2s_rates, - rtw_vht_1s_rates, rtw_vht_2s_rates + rtw_vht_1s_rates, rtw_vht_2s_rates, + rtw_ht_3s_rates, rtw_ht_4s_rates, + rtw_vht_3s_rates, rtw_vht_4s_rates }; EXPORT_SYMBOL(rtw_rate_section); @@ -101,17 +131,14 @@ const u8 rtw_rate_size[RTW_RATE_SECTION_NUM] = { ARRAY_SIZE(rtw_ht_1s_rates), ARRAY_SIZE(rtw_ht_2s_rates), ARRAY_SIZE(rtw_vht_1s_rates), - ARRAY_SIZE(rtw_vht_2s_rates) + ARRAY_SIZE(rtw_vht_2s_rates), + ARRAY_SIZE(rtw_ht_3s_rates), + ARRAY_SIZE(rtw_ht_4s_rates), + ARRAY_SIZE(rtw_vht_3s_rates), + ARRAY_SIZE(rtw_vht_4s_rates) }; EXPORT_SYMBOL(rtw_rate_size); -static const u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates); -static const u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates); -static const u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates); -static const u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates); -static const u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates); -static const u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates); - enum rtw_phy_band_type { PHY_BAND_2G = 0, PHY_BAND_5G = 1, @@ -1640,11 +1667,15 @@ rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd, static void rtw_xref_txpwr_lmt_by_rs(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx) { + static const u8 rs_cmp[4][2] = { + {RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, + {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S}, + {RTW_RATE_SECTION_HT_3S, RTW_RATE_SECTION_VHT_3S}, + {RTW_RATE_SECTION_HT_4S, RTW_RATE_SECTION_VHT_4S} + }; u8 rs_idx, rs_ht, rs_vht; - u8 rs_cmp[2][2] = {{RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, - {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S} }; - for (rs_idx = 0; rs_idx < 2; rs_idx++) { + for (rs_idx = 0; rs_idx < 4; rs_idx++) { rs_ht = rs_cmp[rs_idx][0]; rs_vht = rs_cmp[rs_idx][1]; @@ -1965,10 +1996,10 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, u8 rate, u8 group) { const struct rtw_chip_info *chip = rtwdev->chip; - u8 tx_power; - bool mcs_rate; - bool above_2ss; + bool above_2ss, above_3ss, above_4ss; u8 factor = chip->txgi_factor; + bool mcs_rate; + u8 tx_power; if (rate <= DESC_RATE11M) tx_power = pwr_idx_2g->cck_base[group]; @@ -1978,11 +2009,15 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) tx_power += pwr_idx_2g->ht_1s_diff.ofdm * factor; - mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || + mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT1SS_MCS0 && - rate <= DESC_RATEVHT2SS_MCS9); - above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || + rate <= DESC_RATEVHT4SS_MCS9); + above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT2SS_MCS0); + above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT3SS_MCS0); + above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT4SS_MCS0); if (!mcs_rate) return tx_power; @@ -1995,11 +2030,19 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_2g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw20 * factor; + if (above_3ss) + tx_power += pwr_idx_2g->ht_3s_diff.bw20 * factor; + if (above_4ss) + tx_power += pwr_idx_2g->ht_4s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw40 * factor; + if (above_3ss) + tx_power += pwr_idx_2g->ht_3s_diff.bw40 * factor; + if (above_4ss) + tx_power += pwr_idx_2g->ht_4s_diff.bw40 * factor; break; } @@ -2012,19 +2055,23 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, u8 rate, u8 group) { const struct rtw_chip_info *chip = rtwdev->chip; - u8 tx_power; + bool above_2ss, above_3ss, above_4ss; + u8 factor = chip->txgi_factor; u8 upper, lower; bool mcs_rate; - bool above_2ss; - u8 factor = chip->txgi_factor; + u8 tx_power; tx_power = pwr_idx_5g->bw40_base[group]; - mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || + mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT1SS_MCS0 && - rate <= DESC_RATEVHT2SS_MCS9); - above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || + rate <= DESC_RATEVHT4SS_MCS9); + above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT2SS_MCS0); + above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT3SS_MCS0); + above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT4SS_MCS0); if (!mcs_rate) { tx_power += pwr_idx_5g->ht_1s_diff.ofdm * factor; @@ -2039,11 +2086,19 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_5g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw20 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->ht_3s_diff.bw20 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->ht_4s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw40 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->ht_3s_diff.bw40 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->ht_4s_diff.bw40 * factor; break; case RTW_CHANNEL_WIDTH_80: /* the base idx of bw80 is the average of bw40+/bw40- */ @@ -2054,6 +2109,10 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_5g->vht_1s_diff.bw80 * factor; if (above_2ss) tx_power += pwr_idx_5g->vht_2s_diff.bw80 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->vht_3s_diff.bw80 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->vht_4s_diff.bw80 * factor; break; } @@ -2071,10 +2130,18 @@ static u8 rtw_phy_rate_to_rate_section(u8 rate) return RTW_RATE_SECTION_HT_1S; else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) return RTW_RATE_SECTION_HT_2S; + else if (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS23) + return RTW_RATE_SECTION_HT_3S; + else if (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) + return RTW_RATE_SECTION_HT_4S; else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) return RTW_RATE_SECTION_VHT_1S; else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) return RTW_RATE_SECTION_VHT_2S; + else if (rate >= DESC_RATEVHT3SS_MCS0 && rate <= DESC_RATEVHT3SS_MCS9) + return RTW_RATE_SECTION_VHT_3S; + else if (rate >= DESC_RATEVHT4SS_MCS0 && rate <= DESC_RATEVHT4SS_MCS9) + return RTW_RATE_SECTION_VHT_4S; else return RTW_RATE_SECTION_NUM; } @@ -2102,7 +2169,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, bw = RTW_CHANNEL_WIDTH_20; /* only 20/40M BW with ht */ - if (rs == RTW_RATE_SECTION_HT_1S || rs == RTW_RATE_SECTION_HT_2S) + if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) bw = min_t(u8, bw, RTW_CHANNEL_WIDTH_40); /* select min power limit among [20M BW ~ current BW] */ @@ -2286,7 +2353,7 @@ rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, u8 base_idx, rate_idx; s8 base_2g, base_5g; - if (rs >= RTW_RATE_SECTION_VHT_1S) + if (size == 10) /* VHT rates */ base_idx = rates[size - 3]; else base_idx = rates[size - 1]; @@ -2303,28 +2370,12 @@ rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal) { - u8 path; + u8 path, rs; - for (path = 0; path < RTW_RF_PATH_MAX; path++) { - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_CCK, - rtw_cck_size, rtw_cck_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_OFDM, - rtw_ofdm_size, rtw_ofdm_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_HT_1S, - rtw_ht_1s_size, rtw_ht_1s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_HT_2S, - rtw_ht_2s_size, rtw_ht_2s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_VHT_1S, - rtw_vht_1s_size, rtw_vht_1s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_VHT_2S, - rtw_vht_2s_size, rtw_vht_2s_rates); - } + for (path = 0; path < RTW_RF_PATH_MAX; path++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) + rtw_phy_tx_power_by_rate_config_by_path(hal, path, rs, + rtw_rate_size[rs], rtw_rate_section[rs]); } static void diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h index ce6ee16a77dc..c9e6b869661d 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.h +++ b/drivers/net/wireless/realtek/rtw88/phy.h @@ -13,6 +13,10 @@ extern const u8 rtw_ht_1s_rates[]; extern const u8 rtw_ht_2s_rates[]; extern const u8 rtw_vht_1s_rates[]; extern const u8 rtw_vht_2s_rates[]; +extern const u8 rtw_ht_3s_rates[]; +extern const u8 rtw_ht_4s_rates[]; +extern const u8 rtw_vht_3s_rates[]; +extern const u8 rtw_vht_4s_rates[]; extern const u8 * const rtw_rate_section[]; extern const u8 rtw_rate_size[]; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 6abb21067aed..0ade7f11cbd2 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -709,7 +709,7 @@ static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { if (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S) continue; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 742a2a05632e..b4934da88e33 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -964,7 +964,7 @@ static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs, &phy_pwr_idx); } diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 2314d160292a..5e53e0db177e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2746,7 +2746,7 @@ static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev) s8 diff_idx[4]; rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm); - for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate]; diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c index 109ff42eda82..0fa943271fb6 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c @@ -1637,7 +1637,7 @@ void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { if (hal->rf_path_num == 1 && (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S)) From 9836aa8e12a6ba8db683c403298b448798955821 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 114/465] wifi: rtw88: Fix rtw_update_sta_info() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9f00e2218e15a2ea3c284567424a100c10b6fb85 Author: Bitterblue Smith Date: Tue Feb 4 20:42:08 2025 +0200 wifi: rtw88: Fix rtw_update_sta_info() for RTL8814AU This function tells the firmware what rates it can use. Put the 3SS and 4SS HT rates supported by the other station into the rate mask. Remove the 3SS and 4SS rates from the rate mask if the hardware only has 2 spatial streams. And finally, select the right rate ID (a parameter for the firmware) when the hardware has 3 spatial streams. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/48d1d90f-2aeb-4ec5-9a24-0980e10eae1e@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 8d22df293e78..e4f9b744f24d 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1234,7 +1234,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC) ldpc_en = VHT_LDPC_EN; } else if (sta->deflink.ht_cap.ht_supported) { - ra_mask |= (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) | + ra_mask |= ((u64)sta->deflink.ht_cap.mcs.rx_mask[3] << 36) | + ((u64)sta->deflink.ht_cap.mcs.rx_mask[2] << 28) | + (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) | (sta->deflink.ht_cap.mcs.rx_mask[0] << 12); if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) stbc_en = HT_STBC_EN; @@ -1244,6 +1246,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss) ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; + else if (efuse->hw_cap.nss == 2) + ra_mask &= RA_MASK_VHT_RATES_2SS | RA_MASK_HT_RATES_2SS | + RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; if (hal->current_band_type == RTW_BAND_5G) { ra_mask |= (u64)sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 4; @@ -1302,10 +1307,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, break; } - if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) - tx_num = 2; - else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) - tx_num = 2; + if (sta->deflink.vht_cap.vht_supported || + sta->deflink.ht_cap.ht_supported) + tx_num = efuse->hw_cap.nss; rate_id = get_rate_id(wireless_set, bw_mode, tx_num); From ce2c305f7ea5b0abac9925071fab0b3c51916b71 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 115/465] wifi: rtlwifi: rtl8192de: Fix typos of debug message of phy setting JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6b39cc01af66a32c4d03952e0b3aae19cdf4b66e Author: Andrew Kreimer Date: Thu Feb 6 10:24:12 2025 +0200 wifi: rtlwifi: rtl8192de: Fix typos of debug message of phy setting There are some typos in comments/messages: - althougth -> although - asume -> assume Fix them via codespell. Signed-off-by: Andrew Kreimer Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250206082457.9148-1-algonell@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c index d429560009bb..e07402e73ba3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c @@ -484,7 +484,7 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, * pathA or mac1 has to set phy0&phy1 pathA */ if ((content == radiob_txt) && (rfpath == RF90_PATH_A)) { rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD, - " ===> althougth Path A, we load radiob.txt\n"); + " ===> although Path A, we load radiob.txt\n"); radioa_arraylen = radiob_arraylen; radioa_array_table = radiob_array_table; } @@ -750,7 +750,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) && rtlhal->interfaceindex == 1) { need_pwr_down = rtl92d_phy_enable_anotherphy(hw, false); rtlhal->during_mac1init_radioa = true; - /* asume no this case */ + /* assume no this case */ if (need_pwr_down) rtl92d_phy_enable_rf_env(hw, path, &u4regvalue); @@ -1885,7 +1885,7 @@ static void _rtl92d_phy_reload_lck_setting(struct ieee80211_hw *hw, bneed_powerdown_radio = rtl92d_phy_enable_anotherphy(hw, false); rtlpriv->rtlhal.during_mac1init_radioa = true; - /* asume no this case */ + /* assume no this case */ if (bneed_powerdown_radio) rtl92d_phy_enable_rf_env(hw, erfpath, &u4regvalue); From 311c1eba18a131bbedae1a8d325983168d1a6dd1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 116/465] wifi: rtw89: debugfs depends on CFG80211's one JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a0519433ad8d347d8fb0e7f118f468456dff00fd Author: Ping-Ke Shih Date: Mon Feb 10 20:31:05 2025 +0800 wifi: rtw89: debugfs depends on CFG80211's one The wiphy_locked_debugfs_read() and wiphy_locked_debugfs_write() used by rtw89 are defined if CFG80211_DEBUGFS enabled. Add the dependency accordingly. Fixes: 8fdf78f3cd5f ("wifi: rtw89: debugfs: use wiphy_locked_debugfs_{read,write}() if needed") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202502101810.3CUpUL7p-lkp@intel.com/ Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250210123105.10466-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index b1c86cdd9c0e..205d7ecca7d7 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -123,7 +123,7 @@ config RTW89_DEBUGMSG config RTW89_DEBUGFS bool "Realtek rtw89 debugfs support" - depends on RTW89_CORE + depends on RTW89_CORE && CFG80211_DEBUGFS select RTW89_DEBUG help Enable debugfs support From 4eca80167a63c5401483dc909f4a3a9a5567a6c2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:34 +0200 Subject: [PATCH 117/465] wifi: iwlwifi: dvm: Remove unused iwl_rx_ant_restriction JIRA: https://issues.redhat.com/browse/RHEL-89168 commit fa5b663bbf0bb5ed339761d5b82ca7137a571b25 Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:31:57 2024 +0000 wifi: iwlwifi: dvm: Remove unused iwl_rx_ant_restriction iwl_rx_ant_restriction() was added in 2009 by commit 46f9381aa3fb ("iwlwifi: Thermal Throttling Management - part 2") but never used. Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-2-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/dvm/tt.c | 11 ----------- drivers/net/wireless/intel/iwlwifi/dvm/tt.h | 1 - 2 files changed, 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c index 43e8d04d5a8b..e1d78550e443 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -124,17 +124,6 @@ enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) return restriction->tx_stream; } -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->rx_stream; -} - #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ #define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h index 4af792d41dce..198f934a0d16 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h @@ -100,7 +100,6 @@ u8 iwl_tt_current_power_mode(struct iwl_priv *priv); bool iwl_tt_is_low_power_state(struct iwl_priv *priv); bool iwl_ht_enabled(struct iwl_priv *priv); enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); void iwl_tt_enter_ct_kill(struct iwl_priv *priv); void iwl_tt_exit_ct_kill(struct iwl_priv *priv); void iwl_tt_handler(struct iwl_priv *priv); From 95f857f386a955a89337f26e4ca24f411a56d83b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 118/465] wifi: iwlwifi: mvm: Remove unused iwl_mvm_rx_missed_vap_notif JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 63e616649c901e3a123f08500abbde946ab4ea55 Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:31:58 2024 +0000 wifi: iwlwifi: mvm: Remove unused iwl_mvm_rx_missed_vap_notif iwl_mvm_rx_missed_vap_notif() was added in 2019 by commit 449a29d0fead ("iwlwifi: mvm: add notification for missed VAP") but hasn't been used. Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-3-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 23 ------------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 -- 2 files changed, 25 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 07e61b20eb96..9bc859bc6e4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1960,26 +1960,3 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, ieee80211_channel_switch_disconnect(vif); rcu_read_unlock(); } - -void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_vap_notif *mb = (void *)pkt->data; - struct ieee80211_vif *vif; - u32 id = le32_to_cpu(mb->mac_id); - - IWL_DEBUG_INFO(mvm, - "missed_vap notify mac_id=%u, num_beacon_intervals_elapsed=%u, profile_periodicity=%u\n", - le32_to_cpu(mb->mac_id), - mb->num_beacon_intervals_elapsed, - mb->profile_periodicity); - - rcu_read_lock(); - - vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true); - if (vif) - iwl_mvm_connection_loss(mvm, vif, "missed vap beacon"); - - rcu_read_unlock(); -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ee769da72e68..9f0db96ac320 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2095,8 +2095,6 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, From 4ecaf2267dc6b89a043204536197e4f59d0b55f0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 119/465] wifi: iwlwifi: mvm: Remove unused iwl_mvm_ftm_*_add_pasn_sta functions JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7efd4b61307d99f0fc8c01152af270731c70c2fe Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:31:59 2024 +0000 wifi: iwlwifi: mvm: Remove unused iwl_mvm_ftm_*_add_pasn_sta functions iwl_mvm_ftm_respoder_add_pasn_sta() and iwl_mvm_ftm_resp_remove_pasn_sta() were added in 2020 by commit be82ecd3a5c8 ("iwlwifi: mvm: add an option to add PASN station") but have remained unused. Remove them. After that removal iwl_mvm_add_pasn_sta() is now unused. Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-4-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- .../intel/iwlwifi/mvm/ftm-responder.c | 86 ------------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 -- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 61 ------------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 4 - 4 files changed, 157 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index e6e468e81ab3..83f6e508a094 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -324,92 +324,6 @@ static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm, kfree(sta); } -int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len) -{ - int ret; - struct iwl_mvm_pasn_sta *sta = NULL; - struct iwl_mvm_pasn_hltk_data hltk_data = { - .addr = addr, - .hltk = hltk, - }; - struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL; - - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), - 2); - - lockdep_assert_held(&mvm->mutex); - - if (cmd_ver < 3) { - IWL_ERR(mvm, "Adding PASN station not supported by FW\n"); - return -EOPNOTSUPP; - } - - if ((!hltk || !hltk_len) && (!tk || !tk_len)) { - IWL_ERR(mvm, "TK and HLTK not set\n"); - return -EINVAL; - } - - if (hltk && hltk_len) { - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) { - IWL_ERR(mvm, "No support for secure LTF measurement\n"); - return -EINVAL; - } - - hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); - if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { - IWL_ERR(mvm, "invalid cipher: %u\n", cipher); - return -EINVAL; - } - - hltk_data_ptr = &hltk_data; - } - - if (tk && tk_len) { - sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL); - if (!sta) - return -ENOBUFS; - - ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr, - cipher, tk, tk_len, &sta->keyconf); - if (ret) { - kfree(sta); - return ret; - } - - memcpy(sta->addr, addr, ETH_ALEN); - list_add_tail(&sta->list, &mvm->resp_pasn_list); - } - - ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr); - if (ret && sta) - iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); - - return ret; -} - -int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, u8 *addr) -{ - struct iwl_mvm_pasn_sta *sta, *prev; - - lockdep_assert_held(&mvm->mutex); - - list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) { - if (!memcmp(sta->addr, addr, ETH_ALEN)) { - iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); - return 0; - } - } - - IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr); - return -EINVAL; -} - int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 9f0db96ac320..69ad8d489832 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2518,12 +2518,6 @@ void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm, struct ieee80211_bss_conf *bss_conf); void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, u8 *addr); -int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len); void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 7a4844ec3c10..78fd7faaed97 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -4311,67 +4311,6 @@ u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data) return ieee80211_sn_sub(sn, tid_data->next_reclaimed); } -int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len, - struct ieee80211_key_conf *keyconf) -{ - int ret; - u16 queue; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif); - bool mld = iwl_mvm_has_mld_api(mvm->fw); - u32 type = IWL_STA_LINK; - - if (mld) - type = STATION_TYPE_PEER; - - ret = iwl_mvm_allocate_int_sta(mvm, sta, 0, - NL80211_IFTYPE_UNSPECIFIED, type); - if (ret) - return ret; - - if (mld) - ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, sta, addr, - mvmvif->deflink.fw_link_id, - &queue, - IWL_MAX_TID_COUNT, - &wdg_timeout); - else - ret = iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, - mvmvif->color, addr, sta, - &queue, - IWL_MVM_TX_FIFO_BE); - if (ret) - goto out; - - keyconf->cipher = cipher; - memcpy(keyconf->key, key, key_len); - keyconf->keylen = key_len; - keyconf->flags = IEEE80211_KEY_FLAG_PAIRWISE; - - if (mld) { - /* The MFP flag is set according to the station mfp field. Since - * we don't have a station, set it manually. - */ - u32 key_flags = - iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) | - IWL_SEC_KEY_FLAG_MFP; - u32 sta_mask = BIT(sta->sta_id); - - ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf); - } else { - ret = iwl_mvm_send_sta_key(mvm, sta->sta_id, keyconf, false, - 0, NULL, 0, 0, true); - } - -out: - if (ret) - iwl_mvm_dealloc_int_sta(mvm, sta); - return ret; -} - void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 id) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 6856f7440ef3..19c905b641e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -597,10 +597,6 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_sta_ensure_queue(struct iwl_mvm *mvm, struct ieee80211_txq *txq); void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk); -int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len, - struct ieee80211_key_conf *key_conf_out); void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 id); From 80f93a09c6e39c34022739d45687ae60fa181acb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 120/465] wifi: iwlwifi: mvm: Remove unused iwl_mvm_ftm_add_pasn_sta JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8c7df6490b600c9500a5ad698ecd435c5a482940 Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:32:00 2024 +0000 wifi: iwlwifi: mvm: Remove unused iwl_mvm_ftm_add_pasn_sta iwl_mvm_ftm_add_pasn_sta() was added in 2020 by commit 0739a7d70e00 ("iwlwifi: mvm: initiator: add option for adding a PASN responder") but hasn't been used. Remove it. That was the only caller of iwl_mvm_ftm_remove_pasn_sta(). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-5-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- .../intel/iwlwifi/mvm/ftm-initiator.c | 101 ------------------ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 - 2 files changed, 105 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index b26141c30c61..f7034fa40c26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -46,107 +46,6 @@ struct iwl_mvm_ftm_iter_data { u8 *tk; }; -int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len) -{ - struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn), - GFP_KERNEL); - u32 expected_tk_len; - - lockdep_assert_held(&mvm->mutex); - - if (!pasn) - return -ENOBUFS; - - iwl_mvm_ftm_remove_pasn_sta(mvm, addr); - - pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher); - - switch (pasn->cipher) { - case IWL_LOCATION_CIPHER_CCMP_128: - case IWL_LOCATION_CIPHER_GCMP_128: - expected_tk_len = WLAN_KEY_LEN_CCMP; - break; - case IWL_LOCATION_CIPHER_GCMP_256: - expected_tk_len = WLAN_KEY_LEN_GCMP_256; - break; - default: - goto out; - } - - /* - * If associated to this AP and already have security context, - * the TK is already configured for this station, so it - * shouldn't be set again here. - */ - if (vif->cfg.assoc) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - unsigned int link_id; - struct ieee80211_sta *sta; - u8 sta_id; - - rcu_read_lock(); - for_each_vif_active_link(vif, link_conf, link_id) { - if (memcmp(addr, link_conf->bssid, ETH_ALEN)) - continue; - - sta_id = mvmvif->link[link_id]->ap_sta_id; - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (!IS_ERR_OR_NULL(sta) && sta->mfp) - expected_tk_len = 0; - break; - } - rcu_read_unlock(); - } - - if (tk_len != expected_tk_len || - (hltk_len && hltk_len != sizeof(pasn->hltk))) { - IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n", - tk_len, hltk_len); - goto out; - } - - if (!expected_tk_len && !hltk_len) { - IWL_ERR(mvm, "TK and HLTK not set\n"); - goto out; - } - - memcpy(pasn->addr, addr, sizeof(pasn->addr)); - - if (hltk_len) { - memcpy(pasn->hltk, hltk, sizeof(pasn->hltk)); - pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK; - } - - if (tk && tk_len) - memcpy(pasn->tk, tk, sizeof(pasn->tk)); - - list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list); - return 0; -out: - kfree(pasn); - return -EINVAL; -} - -void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr) -{ - struct iwl_mvm_ftm_pasn_entry *entry, *prev; - - lockdep_assert_held(&mvm->mutex); - - list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list, - list) { - if (memcmp(entry->addr, addr, sizeof(entry->addr))) - continue; - - list_del(&entry->list); - kfree(entry); - return; - } -} - static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm) { struct iwl_mvm_loc_entry *e, *t; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 69ad8d489832..d17592d3f6c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2532,10 +2532,6 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req); void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm); void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm); -int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len); -void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr); /* TDLS */ From 6788dd6dfd17dc68f3bafe3cbeb56198c869c5f0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 121/465] wifi: iwlwifi: Remove unused iwl_bz_name JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 619bd63a9428bcd6f34da12c0015f8949c1f6274 Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:32:01 2024 +0000 wifi: iwlwifi: Remove unused iwl_bz_name iwl_bz_name[] has been unused since the resent commit 6795a37161fb ("wifi: iwlwifi: Print a specific device name.") Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-6-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 1 - drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index efa3e0e35f79..074b2c9ea003 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -145,7 +145,6 @@ const struct iwl_cfg_trans_params iwl_gl_trans_cfg = { .low_latency_xtal = true, }; -const char iwl_bz_name[] = "Intel(R) TBD Bz device"; const char iwl_fm_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; const char iwl_wh_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz"; const char iwl_gl_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 2b6a80142aba..b51e3b5d902b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -553,7 +553,6 @@ extern const char iwl_ax211_name[]; extern const char iwl_ax221_name[]; extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; -extern const char iwl_bz_name[]; extern const char iwl_fm_name[]; extern const char iwl_wh_name[]; extern const char iwl_gl_name[]; From fb177dae0d37bd525d245c9d3809459b1fe2b8c5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 122/465] wifi: iwlwifi: Remove old device data JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5f60a40ee524422bebc233281a6d7fa34a986a77 Author: Dr. David Alan Gilbert Date: Mon Dec 23 01:32:02 2024 +0000 wifi: iwlwifi: Remove old device data The last use of iwl_ax204_name[], iwl_ax221_name[] and iwl_cfg_so_a0_ms_a0 was removed by commit f473a7fd6d60 ("wifi: iwlwifi: remove devices that never came out") Remove them. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20241223013202.340180-7-linux@treblig.org Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 1 - drivers/net/wireless/intel/iwlwifi/cfg/ax210.c | 1 - drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 -- 3 files changed, 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 2e2fcb3807ef..130b9a8aa7eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -205,7 +205,6 @@ const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101"; const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz"; const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz"; const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203"; -const char iwl_ax204_name[] = "Intel(R) Wi-Fi 6 AX204 160MHz"; const char iwl_ax200_killer_1650w_name[] = "Killer(R) Wi-Fi 6 AX1650w 160MHz Wireless Network Adapter (200D2W)"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index dcba1a5d793b..e87b57b9e2c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -180,7 +180,6 @@ const struct iwl_cfg_trans_params iwl_ma_trans_cfg = { }; const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; -const char iwl_ax221_name[] = "Intel(R) Wi-Fi 6E AX221 160MHz"; const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6E AX231 160MHz"; const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index b51e3b5d902b..91275680dd2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -534,7 +534,6 @@ extern const char iwl9560_killer_1550i_name[]; extern const char iwl9560_killer_1550s_name[]; extern const char iwl_ax200_name[]; extern const char iwl_ax203_name[]; -extern const char iwl_ax204_name[]; extern const char iwl_ax201_name[]; extern const char iwl_ax101_name[]; extern const char iwl_ax200_killer_1650w_name[]; @@ -550,7 +549,6 @@ extern const char iwl_ax211_killer_1675i_name[]; extern const char iwl_ax411_killer_1690s_name[]; extern const char iwl_ax411_killer_1690i_name[]; extern const char iwl_ax211_name[]; -extern const char iwl_ax221_name[]; extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_fm_name[]; From 1ab56fdcf36f295757038c59c2a0c755e9a08388 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 123/465] net: rfkill: gpio: allow booting in blocked state JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2882bf7dd4f5dc732aa98d206026582cc136cb4d Author: Catalin Popescu Date: Thu Jan 16 09:47:02 2025 +0100 net: rfkill: gpio: allow booting in blocked state By default, rfkill state is unblocked and this behavior is not configurable. Add support for booting in blocked state based on the presence of a devicetree property. Signed-off-by: Catalin Popescu Link: https://patch.msgid.link/20250116084702.3473176-2-catalin.popescu@leica-geosystems.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/rfkill/rfkill-gpio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index a8e21060112f..17de1ffbf636 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -162,6 +162,9 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (!rfkill->rfkill_dev) return -ENOMEM; + if (device_property_present(&pdev->dev, "default-blocked")) + rfkill_init_sw_state(rfkill->rfkill_dev, true); + ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) goto err_destroy; From e4f62747170beb9bdd90453699fc5dfc1aa58c7a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 124/465] dt-bindings: net: Add rfkill-gpio binding JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 50071fdf0882a0449328c967f282ad7444d194bb Author: Philipp Zabel Date: Mon Jan 2 18:29:33 2023 +0100 dt-bindings: net: Add rfkill-gpio binding Add a device tree binding document for GPIO controlled rfkill switches. The label and radio-type properties correspond to the name and type properties used for ACPI, respectively. The shutdown-gpios property is the same as defined for ACPI. Signed-off-by: Philipp Zabel Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230102-rfkill-gpio-dt-v2-1-d1b83758c16d@pengutronix.de Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../devicetree/bindings/net/rfkill-gpio.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/rfkill-gpio.yaml diff --git a/Documentation/devicetree/bindings/net/rfkill-gpio.yaml b/Documentation/devicetree/bindings/net/rfkill-gpio.yaml new file mode 100644 index 000000000000..9630c8466fac --- /dev/null +++ b/Documentation/devicetree/bindings/net/rfkill-gpio.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/rfkill-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO controlled rfkill switch + +maintainers: + - Johannes Berg + - Philipp Zabel + +properties: + compatible: + const: rfkill-gpio + + label: + description: rfkill switch name, defaults to node name + + radio-type: + description: rfkill radio type + enum: + - bluetooth + - fm + - gps + - nfc + - ultrawideband + - wimax + - wlan + - wwan + + shutdown-gpios: + maxItems: 1 + +required: + - compatible + - radio-type + - shutdown-gpios + +additionalProperties: false + +examples: + - | + #include + + rfkill { + compatible = "rfkill-gpio"; + label = "rfkill-pcie-wlan"; + radio-type = "wlan"; + shutdown-gpios = <&gpio2 25 GPIO_ACTIVE_HIGH>; + }; From 3dfe229cb990087c973ee7b9f7d7875c81961c4a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 125/465] dt-bindings: net: rfkill-gpio: enable booting in blocked state JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7951e8099c2f12c1b98ce964ee05fd6bb7f2e7e7 Author: Catalin Popescu Date: Thu Jan 16 09:47:01 2025 +0100 dt-bindings: net: rfkill-gpio: enable booting in blocked state By default, rfkill state is set to unblocked. Sometimes, we want to boot in blocked state and let the application unblock the rfkill. Signed-off-by: Catalin Popescu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250116084702.3473176-1-catalin.popescu@leica-geosystems.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- Documentation/devicetree/bindings/net/rfkill-gpio.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/net/rfkill-gpio.yaml b/Documentation/devicetree/bindings/net/rfkill-gpio.yaml index 9630c8466fac..4a706a41ab38 100644 --- a/Documentation/devicetree/bindings/net/rfkill-gpio.yaml +++ b/Documentation/devicetree/bindings/net/rfkill-gpio.yaml @@ -32,6 +32,10 @@ properties: shutdown-gpios: maxItems: 1 + default-blocked: + $ref: /schemas/types.yaml#/definitions/flag + description: configure rfkill state as blocked at boot + required: - compatible - radio-type @@ -48,4 +52,5 @@ examples: label = "rfkill-pcie-wlan"; radio-type = "wlan"; shutdown-gpios = <&gpio2 25 GPIO_ACTIVE_HIGH>; + default-blocked; }; From b82e890317500c42d1d2ab65fdcb42d2a2125f33 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:35 +0200 Subject: [PATCH 126/465] wifi: iwlwifi: remove the mvm prefix from iwl_mvm_ctdp_cmd JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8c2ffc65563fff5d596c4bef60445d443991ae26 Author: Emmanuel Grumbach Date: Sun Jan 19 21:03:13 2025 +0200 wifi: iwlwifi: remove the mvm prefix from iwl_mvm_ctdp_cmd This command is not specific to iwlmvm. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250119210104.04f3afcf9c77.Ic2b6f265d0b4aea25ccc7114d6f48afa621871be@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/phy.h | 12 ++++++------ drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h index c73d4d597857..1dce28f3afa0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -19,7 +19,7 @@ enum iwl_phy_ops_subcmd_ids { CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, /** - * @CTDP_CONFIG_CMD: &struct iwl_mvm_ctdp_cmd + * @CTDP_CONFIG_CMD: &struct iwl_ctdp_cmd */ CTDP_CONFIG_CMD = 0x03, @@ -195,25 +195,25 @@ struct ct_kill_notif { } __packed; /* CT_KILL_NOTIFICATION_API_S_VER_1, CT_KILL_NOTIFICATION_API_S_VER_2 */ /** -* enum iwl_mvm_ctdp_cmd_operation - CTDP command operations +* enum iwl_ctdp_cmd_operation - CTDP command operations * @CTDP_CMD_OPERATION_START: update the current budget * @CTDP_CMD_OPERATION_STOP: stop ctdp * @CTDP_CMD_OPERATION_REPORT: get the average budget */ -enum iwl_mvm_ctdp_cmd_operation { +enum iwl_ctdp_cmd_operation { CTDP_CMD_OPERATION_START = 0x1, CTDP_CMD_OPERATION_STOP = 0x2, CTDP_CMD_OPERATION_REPORT = 0x4, };/* CTDP_CMD_OPERATION_TYPE_E */ /** - * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget + * struct iwl_ctdp_cmd - track and manage the FW power consumption budget * - * @operation: see &enum iwl_mvm_ctdp_cmd_operation + * @operation: see &enum iwl_ctdp_cmd_operation * @budget: the budget in milliwatt * @window_size: defined in API but not used */ -struct iwl_mvm_ctdp_cmd { +struct iwl_ctdp_cmd { __le32 operation; __le32 budget; __le32 window_size; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index d92470960b38..256f8f558b15 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -506,7 +506,7 @@ static const u32 iwl_mvm_cdev_budgets[] = { int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state) { - struct iwl_mvm_ctdp_cmd cmd = { + struct iwl_ctdp_cmd cmd = { .operation = cpu_to_le32(op), .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]), .window_size = 0, From 7fe3a3686646708b3a7e6367232393cbc53a7ca7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 127/465] wifi: iwlwifi: remove the version number from iwl_dts_measurement_notif_v2 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 55e52a3b8f60413e1e935d9de1719ceafeb5ee64 Author: Emmanuel Grumbach Date: Sun Jan 19 21:03:14 2025 +0200 wifi: iwlwifi: remove the version number from iwl_dts_measurement_notif_v2 No need to carry the version number in the structure name if this is the latest version available. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250119210104.9d218a5c4f6b.I9de3e424be48d66994cde3684ce7e9e99456067d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/commands.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/api/phy.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 34a1f97653c0..4b450c722a9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -502,7 +502,7 @@ enum iwl_legacy_cmds { /** * @DTS_MEASUREMENT_NOTIFICATION: * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 + * &struct iwl_dts_measurement_notif */ DTS_MEASUREMENT_NOTIFICATION = 0xdd, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h index 1dce28f3afa0..eb8961b51cb0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -55,7 +55,7 @@ enum iwl_phy_ops_subcmd_ids { /** * @DTS_MEASUREMENT_NOTIF_WIDE: * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 + * &struct iwl_dts_measurement_notif */ DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, }; @@ -152,13 +152,13 @@ struct iwl_dts_measurement_notif_v1 { } __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/ /** - * struct iwl_dts_measurement_notif_v2 - measurements notification + * struct iwl_dts_measurement_notif - measurements notification * * @temp: the measured temperature * @voltage: the measured voltage * @threshold_idx: the trip index that was crossed */ -struct iwl_dts_measurement_notif_v2 { +struct iwl_dts_measurement_notif { __le32 temp; __le32 voltage; __le32 threshold_idx; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 256f8f558b15..c851290e75a2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -105,7 +105,7 @@ static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_dts_measurement_notif_v2 *notif_v2; + struct iwl_dts_measurement_notif *notif_v2; int len = iwl_rx_packet_payload_len(pkt); int temp; u32 ths_crossed; From 7e5178ffaf3932fe0485eccf30878502fc9106a4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 128/465] wifi: iwlwifi: remove the mvm prefix from iwl_mvm_aux_sta_cmd JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3d4b0f0c5cda8f071641a151a2eb494099ed7337 Author: Emmanuel Grumbach Date: Sun Jan 19 21:03:15 2025 +0200 wifi: iwlwifi: remove the mvm prefix from iwl_mvm_aux_sta_cmd This is a firmware command and is not specific to the iwlmvm op_mode Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250119210104.276658439163.I70641851f9e5210ec3a7033db38a45d24814083b@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 37bb7002c1c9..232e20482e2d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -46,7 +46,7 @@ enum iwl_mac_conf_subcmd_ids { */ STA_CONFIG_CMD = 0xA, /** - * @AUX_STA_CMD: &struct iwl_mvm_aux_sta_cmd + * @AUX_STA_CMD: &struct iwl_aux_sta_cmd */ AUX_STA_CMD = 0xB, /** @@ -641,7 +641,7 @@ struct iwl_sta_cfg_cmd { } __packed; /* STA_CMD_API_S_VER_1 */ /** - * struct iwl_mvm_aux_sta_cmd - command for AUX STA configuration + * struct iwl_aux_sta_cmd - command for AUX STA configuration * ( AUX_STA_CMD = 0xB ) * * @sta_id: index of aux sta to configure @@ -649,7 +649,7 @@ struct iwl_sta_cfg_cmd { * @mac_addr: mac addr of the auxilary sta * @reserved_for_mac_addr: reserved */ -struct iwl_mvm_aux_sta_cmd { +struct iwl_aux_sta_cmd { __le32 sta_id; __le32 lmac_id; u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index 2f159024eeb8..9dd670041137 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -121,7 +121,7 @@ static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm, { int ret; - struct iwl_mvm_aux_sta_cmd cmd = { + struct iwl_aux_sta_cmd cmd = { .sta_id = cpu_to_le32(sta->sta_id), .lmac_id = cpu_to_le32(lmac_id), }; From d409585f1db83b731417fd023e583f158bde5ddf Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 129/465] wifi: mwifiex: Remove unused mwifiex_uap_del_sta_data JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f5903ca220360d2e5539473aacd845909a2d5274 Author: Dr. David Alan Gilbert Date: Tue Feb 4 01:25:12 2025 +0000 wifi: mwifiex: Remove unused mwifiex_uap_del_sta_data The last use of mwifiex_uap_del_sta_data() was removed in 2014 by commit dda9ddeb2638 ("mwifiex: do not delete station entries in del_sta handler") Remove it. Signed-off-by: Dr. David Alan Gilbert Link: https://patch.msgid.link/20250204012512.390209-1-linux@treblig.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/main.h | 2 -- drivers/net/wireless/marvell/mwifiex/uap_event.c | 16 ---------------- 2 files changed, 18 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index fb15831201f7..63f1c900e096 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1571,8 +1571,6 @@ void mwifiex_uap_set_channel(struct mwifiex_private *priv, struct cfg80211_chan_def chandef); int mwifiex_config_start_uap(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg); -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node); void mwifiex_config_uap_11d(struct mwifiex_private *priv, struct cfg80211_beacon_data *beacon_data); diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 58ef5020a46a..245cb99a3daa 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -325,19 +325,3 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) return 0; } - -/* This function deletes station entry from associated station list. - * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream - * tables created for this station are deleted. - */ -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - if (priv->ap_11n_enabled && node->is_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); - } - mwifiex_del_sta_entry(priv, node->mac_addr); - - return; -} From bad6eccc5018e8458d618a85c75785ad354f300b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 130/465] wifi: nl80211/cfg80211: Stop supporting cooked monitor JIRA: https://issues.redhat.com/browse/RHEL-89168 commit be22179cfb2fb1164004b70b33a4bdf67e6dd349 Author: Alexander Wetzel Date: Tue Feb 4 12:13:51 2025 +0100 wifi: nl80211/cfg80211: Stop supporting cooked monitor Unconditionally start to refuse creating cooked monitor interfaces to phase them out. There is no feature flag for drivers to opt-in for cooked monitor and all known users are using/preferring the modern API since the hostapd release 1.0 in May 2012. Signed-off-by: Alexander Wetzel Link: https://patch.msgid.link/20250204111352.7004-1-Alexander@wetzel-home.de Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 2 +- include/uapi/linux/nl80211.h | 4 ++-- net/wireless/nl80211.c | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 308f7f8e8311..c433b6ebe1fa 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2265,7 +2265,7 @@ static inline int cfg80211_get_station(struct net_device *dev, * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP * @MONITOR_FLAG_CONTROL: pass control frames * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering - * @MONITOR_FLAG_COOK_FRAMES: report frames after processing + * @MONITOR_FLAG_COOK_FRAMES: deprecated, will unconditionally be refused * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address * @MONITOR_FLAG_SKIP_TX: do not pass locally transmitted frames */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f6c1b181c886..9d8ecf20ef0d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4727,8 +4727,8 @@ enum nl80211_survey_info { * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP * @NL80211_MNTR_FLAG_CONTROL: pass control frames * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering - * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. - * overrides all other flags. + * @NL80211_MNTR_FLAG_COOK_FRAMES: deprecated + * will unconditionally be refused * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address * and ACK incoming unicast packets. * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0c2d028c31a4..e66c545be6d5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4255,6 +4255,10 @@ static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev, change = true; } + /* MONITOR_FLAG_COOK_FRAMES is deprecated, refuse cooperation */ + if (params->flags & MONITOR_FLAG_COOK_FRAMES) + return -EOPNOTSUPP; + if (params->flags & MONITOR_FLAG_ACTIVE && !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) return -EOPNOTSUPP; From 3ec2ce2411bcd54c1dda05aee4f9b90c0249b9f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 131/465] wifi: mac80211: Drop cooked monitor support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 286e696770654d79b34bd15953e7101a1c4784c7 Author: Alexander Wetzel Date: Tue Feb 4 12:13:52 2025 +0100 wifi: mac80211: Drop cooked monitor support Hostapd switched from cooked monitor interfaces to nl80211 Dec 2011. Drop support for the outdated cooked monitor interfaces and fix creating the virtual monitor interfaces in the following cases: 1) We have one non-monitor and one monitor interface with %MONITOR_FLAG_ACTIVE enabled and then delete the non-monitor interface. 2) We only have monitor interfaces enabled on resume while at least one has %MONITOR_FLAG_ACTIVE set. Signed-off-by: Alexander Wetzel Link: https://patch.msgid.link/20250204111352.7004-2-Alexander@wetzel-home.de Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/dropreason.h | 6 -- net/mac80211/cfg.c | 9 +- net/mac80211/drop.h | 21 ++-- net/mac80211/ieee80211_i.h | 11 +-- net/mac80211/iface.c | 50 ++++------ net/mac80211/main.c | 16 +-- net/mac80211/rx.c | 194 ++++++++++--------------------------- net/mac80211/status.c | 34 +------ net/mac80211/tx.c | 2 +- 9 files changed, 94 insertions(+), 249 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 56cb7be92244..7d3b1a2a6fec 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -17,12 +17,6 @@ enum skb_drop_reason_subsys { */ SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE, - /** - * @SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR: mac80211 drop reasons - * for frames still going to monitor, see net/mac80211/drop.h - */ - SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR, - /** * @SKB_DROP_REASON_SUBSYS_OPENVSWITCH: openvswitch drop reasons, * see net/openvswitch/drop.h diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a6906f08df17..9d08e5255dd8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -89,15 +89,14 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata, /* check flags first */ if (params->flags && ieee80211_sdata_running(sdata)) { - u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE; + u32 mask = MONITOR_FLAG_ACTIVE; /* - * Prohibit MONITOR_FLAG_COOK_FRAMES and - * MONITOR_FLAG_ACTIVE to be changed while the - * interface is up. + * Prohibit MONITOR_FLAG_ACTIVE to be changed + * while the interface is up. * Else we would need to add a lot of cruft * to update everything: - * cooked_mntrs, monitor and all fif_* counters + * monitor and all fif_* counters * reconfigure hardware */ if ((params->flags & mask) != (sdata->u.mntr.flags & mask)) diff --git a/net/mac80211/drop.h b/net/mac80211/drop.h index 59e3ec4dc960..eb9ab310f91c 100644 --- a/net/mac80211/drop.h +++ b/net/mac80211/drop.h @@ -11,12 +11,6 @@ typedef unsigned int __bitwise ieee80211_rx_result; -#define MAC80211_DROP_REASONS_MONITOR(R) \ - R(RX_DROP_M_UNEXPECTED_4ADDR_FRAME) \ - R(RX_DROP_M_BAD_BCN_KEYIDX) \ - R(RX_DROP_M_BAD_MGMT_KEYIDX) \ -/* this line for the trailing \ - add before this */ - #define MAC80211_DROP_REASONS_UNUSABLE(R) \ /* 0x00 == ___RX_DROP_UNUSABLE */ \ R(RX_DROP_U_MIC_FAIL) \ @@ -66,6 +60,10 @@ typedef unsigned int __bitwise ieee80211_rx_result; R(RX_DROP_U_UNEXPECTED_STA_4ADDR) \ R(RX_DROP_U_UNEXPECTED_VLAN_MCAST) \ R(RX_DROP_U_NOT_PORT_CONTROL) \ + R(RX_DROP_U_UNEXPECTED_4ADDR_FRAME) \ + R(RX_DROP_U_BAD_BCN_KEYIDX) \ + /* 0x30 */ \ + R(RX_DROP_U_BAD_MGMT_KEYIDX) \ R(RX_DROP_U_UNKNOWN_ACTION_REJECTED) \ /* this line for the trailing \ - add before this */ @@ -78,10 +76,6 @@ enum ___mac80211_drop_reason { ___RX_QUEUED = SKB_NOT_DROPPED_YET, #define ENUM(x) ___ ## x, - ___RX_DROP_MONITOR = SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR << - SKB_DROP_REASON_SUBSYS_SHIFT, - MAC80211_DROP_REASONS_MONITOR(ENUM) - ___RX_DROP_UNUSABLE = SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE << SKB_DROP_REASON_SUBSYS_SHIFT, MAC80211_DROP_REASONS_UNUSABLE(ENUM) @@ -89,11 +83,10 @@ enum ___mac80211_drop_reason { }; enum mac80211_drop_reason { - RX_CONTINUE = (__force ieee80211_rx_result)___RX_CONTINUE, - RX_QUEUED = (__force ieee80211_rx_result)___RX_QUEUED, - RX_DROP_MONITOR = (__force ieee80211_rx_result)___RX_DROP_MONITOR, + RX_CONTINUE = (__force ieee80211_rx_result)___RX_CONTINUE, + RX_QUEUED = (__force ieee80211_rx_result)___RX_QUEUED, + RX_DROP = (__force ieee80211_rx_result)___RX_DROP_UNUSABLE, #define DEF(x) x = (__force ieee80211_rx_result)___ ## x, - MAC80211_DROP_REASONS_MONITOR(DEF) MAC80211_DROP_REASONS_UNUSABLE(DEF) #undef DEF }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e7dc3f0cfc9a..a90a44aa5758 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -200,7 +200,6 @@ enum ieee80211_packet_rx_flags { /** * enum ieee80211_rx_flags - RX data flags * - * @IEEE80211_RX_CMNTR: received on cooked monitor already * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported * to cfg80211_report_obss_beacon(). * @@ -208,8 +207,7 @@ enum ieee80211_packet_rx_flags { * for a single frame. */ enum ieee80211_rx_flags { - IEEE80211_RX_CMNTR = BIT(0), - IEEE80211_RX_BEACON_REPORTED = BIT(1), + IEEE80211_RX_BEACON_REPORTED = BIT(0), }; struct ieee80211_rx_data { @@ -1380,7 +1378,7 @@ struct ieee80211_local { spinlock_t queue_stop_reason_lock; int open_count; - int monitors, cooked_mntrs, tx_mntrs; + int monitors, tx_mntrs; /* number of interfaces with corresponding FIF_ flags */ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll, fif_probe_req; @@ -1492,7 +1490,7 @@ struct ieee80211_local { /* see iface.c */ struct list_head interfaces; - struct list_head mon_list; /* only that are IFF_UP && !cooked */ + struct list_head mon_list; /* only that are IFF_UP */ struct mutex iflist_mtx; /* Scanning and BSS list */ @@ -2090,8 +2088,7 @@ struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags); void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, - int retry_count, bool send_to_cooked, - struct ieee80211_tx_status *status); + int retry_count, struct ieee80211_tx_status *status); void ieee80211_check_fast_xmit(struct sta_info *sta); void ieee80211_check_fast_xmit_all(struct ieee80211_local *local); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 738de269e13f..e627e2df3038 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -483,8 +483,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do ieee80211_ibss_stop(sdata); break; case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) - break; list_del_rcu(&sdata->u.mntr.list); break; default: @@ -584,18 +582,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) { - local->cooked_mntrs--; - break; - } + if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) && + !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) { - local->monitors--; - if (local->monitors == 0) { - local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; - hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; - } + local->monitors--; + if (local->monitors == 0) { + local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; + hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; + } - ieee80211_adjust_monitor_flags(sdata, -1); + ieee80211_adjust_monitor_flags(sdata, -1); + } break; case NL80211_IFTYPE_NAN: /* clean all the functions */ @@ -1327,27 +1324,24 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) } break; case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) { - local->cooked_mntrs++; - break; - } - if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) || ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) { res = drv_add_interface(local, sdata); if (res) goto err_stop; - } else if (local->monitors == 0 && local->open_count == 0) { - res = ieee80211_add_virtual_monitor(local); - if (res) - goto err_stop; - } + } else { + if (local->monitors == 0 && local->open_count == 0) { + res = ieee80211_add_virtual_monitor(local); + if (res) + goto err_stop; + } + local->monitors++; - /* must be before the call to ieee80211_configure_filter */ - local->monitors++; - if (local->monitors == 1) { - local->hw.conf.flags |= IEEE80211_CONF_MONITOR; - hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; + /* must be before the call to ieee80211_configure_filter */ + if (local->monitors == 1) { + local->hw.conf.flags |= IEEE80211_CONF_MONITOR; + hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; + } } ieee80211_adjust_monitor_flags(sdata, 1); @@ -1424,8 +1418,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) rcu_assign_pointer(local->p2p_sdata, sdata); break; case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) - break; list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); break; default: diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0e88abe0d293..2130c638bc1f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1744,18 +1744,7 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) wiphy_free(local->hw.wiphy); } EXPORT_SYMBOL(ieee80211_free_hw); - -static const char * const drop_reasons_monitor[] = { -#define V(x) #x, - [0] = "RX_DROP_MONITOR", - MAC80211_DROP_REASONS_MONITOR(V) -}; - -static struct drop_reason_list drop_reason_list_monitor = { - .reasons = drop_reasons_monitor, - .n_reasons = ARRAY_SIZE(drop_reasons_monitor), -}; - +#define V(x) #x, static const char * const drop_reasons_unusable[] = { [0] = "RX_DROP_UNUSABLE", MAC80211_DROP_REASONS_UNUSABLE(V) @@ -1784,8 +1773,6 @@ static int __init ieee80211_init(void) if (ret) goto err_netdev; - drop_reasons_register_subsys(SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR, - &drop_reason_list_monitor); drop_reasons_register_subsys(SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE, &drop_reason_list_unusable); @@ -1804,7 +1791,6 @@ static void __exit ieee80211_exit(void) ieee80211_iface_exit(); - drop_reasons_unregister_subsys(SKB_DROP_REASON_SUBSYS_MAC80211_MONITOR); drop_reasons_unregister_subsys(SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE); rcu_barrier(); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e4913d79cf7d..9a22d7090aa4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1045,14 +1045,14 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(hdr->addr1)) { if (ieee80211_has_tods(hdr->frame_control) || !ieee80211_has_fromds(hdr->frame_control)) - return RX_DROP_MONITOR; + return RX_DROP; if (ether_addr_equal(hdr->addr3, dev_addr)) - return RX_DROP_MONITOR; + return RX_DROP; } else { if (!ieee80211_has_a4(hdr->frame_control)) - return RX_DROP_MONITOR; + return RX_DROP; if (ether_addr_equal(hdr->addr4, dev_addr)) - return RX_DROP_MONITOR; + return RX_DROP; } } @@ -1064,20 +1064,20 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) struct ieee80211_mgmt *mgmt; if (!ieee80211_is_mgmt(hdr->frame_control)) - return RX_DROP_MONITOR; + return RX_DROP; if (ieee80211_is_action(hdr->frame_control)) { u8 category; /* make sure category field is present */ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) - return RX_DROP_MONITOR; + return RX_DROP; mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && category != WLAN_CATEGORY_SELF_PROTECTED) - return RX_DROP_MONITOR; + return RX_DROP; return RX_CONTINUE; } @@ -1087,7 +1087,7 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ieee80211_is_auth(hdr->frame_control)) return RX_CONTINUE; - return RX_DROP_MONITOR; + return RX_DROP; } return RX_CONTINUE; @@ -1513,7 +1513,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(hdr->frame_control); if (rx->skb->len < hdrlen + 8) - return RX_DROP_MONITOR; + return RX_DROP; skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); if (ethertype == rx->sdata->control_port_protocol) @@ -1526,7 +1526,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) GFP_ATOMIC)) return RX_DROP_U_SPURIOUS; - return RX_DROP_MONITOR; + return RX_DROP; } return RX_CONTINUE; @@ -1862,7 +1862,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) cfg80211_rx_unexpected_4addr_frame( rx->sdata->dev, sta->sta.addr, GFP_ATOMIC); - return RX_DROP_M_UNEXPECTED_4ADDR_FRAME; + return RX_DROP_U_UNEXPECTED_4ADDR_FRAME; } /* * Update counter and free packet here to avoid @@ -1997,7 +1997,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, skb->data, skb->len); - return RX_DROP_M_BAD_BCN_KEYIDX; + return RX_DROP_U_BAD_BCN_KEYIDX; } rx->key = ieee80211_rx_get_bigtk(rx, mmie_keyidx); @@ -2011,11 +2011,11 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (mmie_keyidx < NUM_DEFAULT_KEYS || mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) - return RX_DROP_M_BAD_MGMT_KEYIDX; /* unexpected BIP keyidx */ + return RX_DROP_U_BAD_MGMT_KEYIDX; /* unexpected BIP keyidx */ if (rx->link_sta) { if (ieee80211_is_group_privacy_action(skb) && test_sta_flag(rx->sta, WLAN_STA_MFP)) - return RX_DROP_MONITOR; + return RX_DROP; rx->key = rcu_dereference(rx->link_sta->gtk[mmie_keyidx]); } @@ -2100,11 +2100,11 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (rx->key) { if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) - return RX_DROP_MONITOR; + return RX_DROP; /* TODO: add threshold stuff again */ } else { - return RX_DROP_MONITOR; + return RX_DROP; } switch (rx->key->conf.cipher) { @@ -2278,7 +2278,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) goto out; if (is_multicast_ether_addr(hdr->addr1)) - return RX_DROP_MONITOR; + return RX_DROP; I802_DEBUG_INC(rx->local->rx_handlers_fragments); @@ -2333,7 +2333,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) rx->seqno_idx, hdr); if (!entry) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); - return RX_DROP_MONITOR; + return RX_DROP; } /* "The receiver shall discard MSDUs and MMPDUs whose constituent @@ -2855,25 +2855,25 @@ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta return RX_CONTINUE; if (!pskb_may_pull(skb, sizeof(*eth) + 6)) - return RX_DROP_MONITOR; + return RX_DROP; mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(*eth)); mesh_hdrlen = ieee80211_get_mesh_hdrlen(mesh_hdr); if (!pskb_may_pull(skb, sizeof(*eth) + mesh_hdrlen)) - return RX_DROP_MONITOR; + return RX_DROP; eth = (struct ethhdr *)skb->data; multicast = is_multicast_ether_addr(eth->h_dest); mesh_hdr = (struct ieee80211s_hdr *)(eth + 1); if (!mesh_hdr->ttl) - return RX_DROP_MONITOR; + return RX_DROP; /* frame is in RMC, don't forward */ if (is_multicast_ether_addr(eth->h_dest) && mesh_rmc_check(sdata, eth->h_source, mesh_hdr)) - return RX_DROP_MONITOR; + return RX_DROP; /* forward packet */ if (sdata->crypto_tx_tailroom_needed_cnt) @@ -2890,7 +2890,7 @@ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta /* has_a4 already checked in ieee80211_rx_mesh_check */ proxied_addr = mesh_hdr->eaddr2; else - return RX_DROP_MONITOR; + return RX_DROP; rcu_read_lock(); mppath = mpp_path_lookup(sdata, proxied_addr); @@ -2922,14 +2922,14 @@ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta goto rx_accept; IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); - return RX_DROP_MONITOR; + return RX_DROP; } if (!ifmsh->mshcfg.dot11MeshForwarding) { if (is_multicast_ether_addr(eth->h_dest)) goto rx_accept; - return RX_DROP_MONITOR; + return RX_DROP; } skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]); @@ -3122,7 +3122,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (unlikely(!ieee80211_is_data_present(fc))) - return RX_DROP_MONITOR; + return RX_DROP; if (unlikely(ieee80211_has_a4(hdr->frame_control))) { switch (rx->sdata->vif.type) { @@ -3179,19 +3179,16 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) - return RX_DROP_MONITOR; + return RX_DROP; - /* - * Send unexpected-4addr-frame event to hostapd. For older versions, - * also drop the frame to cooked monitor interfaces. - */ + /* Send unexpected-4addr-frame event to hostapd */ if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP) { if (rx->sta && !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) cfg80211_rx_unexpected_4addr_frame( rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); - return RX_DROP_MONITOR; + return RX_DROP; } res = __ieee80211_data_to_8023(rx, &port_control); @@ -3203,7 +3200,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) return res; if (!ieee80211_frame_allowed(rx, fc)) - return RX_DROP_MONITOR; + return RX_DROP; /* directly handle TDLS channel switch requests/responses */ if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto == @@ -3268,11 +3265,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) }; if (!rx->sta) - return RX_DROP_MONITOR; + return RX_DROP; if (skb_copy_bits(skb, offsetof(struct ieee80211_bar, control), &bar_data, sizeof(bar_data))) - return RX_DROP_MONITOR; + return RX_DROP; tid = le16_to_cpu(bar_data.control) >> 12; @@ -3284,7 +3281,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) - return RX_DROP_MONITOR; + return RX_DROP; start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4; event.u.ba.tid = tid; @@ -3308,12 +3305,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) return RX_QUEUED; } - /* - * After this point, we only want management frames, - * so we can drop all remaining control frames to - * cooked monitor interfaces. - */ - return RX_DROP_MONITOR; + return RX_DROP; } static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, @@ -3422,10 +3414,10 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) * and unknown (reserved) frames are useless. */ if (rx->skb->len < 24) - return RX_DROP_MONITOR; + return RX_DROP; if (!ieee80211_is_mgmt(mgmt->frame_control)) - return RX_DROP_MONITOR; + return RX_DROP; /* drop too small action frames */ if (ieee80211_is_action(mgmt->frame_control) && @@ -3951,17 +3943,16 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx) * ones. For all other modes we will return them to the sender, * setting the 0x80 bit in the action category, as required by * 802.11-2012 9.24.4. - * Newer versions of hostapd shall also use the management frame - * registration mechanisms, but older ones still use cooked - * monitor interfaces so push all frames there. + * Newer versions of hostapd use the management frame registration + * mechanisms and old cooked monitor interface is no longer supported. */ if (!(status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) && (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) - return RX_DROP_MONITOR; + return RX_DROP; if (is_multicast_ether_addr(mgmt->da)) - return RX_DROP_MONITOR; + return RX_DROP; /* do not return rejected action frames */ if (mgmt->u.action.category & 0x80) @@ -4006,7 +3997,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + return RX_DROP; /* for now only beacons are ext, so queue them */ ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb); @@ -4027,7 +4018,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) sdata->vif.type != NL80211_IFTYPE_ADHOC && sdata->vif.type != NL80211_IFTYPE_OCB && sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + return RX_DROP; switch (stype) { case cpu_to_le16(IEEE80211_STYPE_AUTH): @@ -4038,32 +4029,32 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) case cpu_to_le16(IEEE80211_STYPE_DEAUTH): if (is_multicast_ether_addr(mgmt->da) && !is_broadcast_ether_addr(mgmt->da)) - return RX_DROP_MONITOR; + return RX_DROP; /* process only for station/IBSS */ if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_ADHOC) - return RX_DROP_MONITOR; + return RX_DROP; break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_DISASSOC): if (is_multicast_ether_addr(mgmt->da) && !is_broadcast_ether_addr(mgmt->da)) - return RX_DROP_MONITOR; + return RX_DROP; /* process only for station */ if (sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + return RX_DROP; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): /* process only for ibss and mesh */ if (sdata->vif.type != NL80211_IFTYPE_ADHOC && sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return RX_DROP_MONITOR; + return RX_DROP; break; default: - return RX_DROP_MONITOR; + return RX_DROP; } ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb); @@ -4071,82 +4062,9 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_QUEUED; } -static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, - struct ieee80211_rate *rate, - ieee80211_rx_result reason) -{ - struct ieee80211_sub_if_data *sdata; - struct ieee80211_local *local = rx->local; - struct sk_buff *skb = rx->skb, *skb2; - struct net_device *prev_dev = NULL; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - int needed_headroom; - - /* - * If cooked monitor has been processed already, then - * don't do it again. If not, set the flag. - */ - if (rx->flags & IEEE80211_RX_CMNTR) - goto out_free_skb; - rx->flags |= IEEE80211_RX_CMNTR; - - /* If there are no cooked monitor interfaces, just free the SKB */ - if (!local->cooked_mntrs) - goto out_free_skb; - - /* room for the radiotap header based on driver features */ - needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb); - - if (skb_headroom(skb) < needed_headroom && - pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) - goto out_free_skb; - - /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, - false); - - skb_reset_mac_header(skb); - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_802_2); - - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (!ieee80211_sdata_running(sdata)) - continue; - - if (sdata->vif.type != NL80211_IFTYPE_MONITOR || - !(sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)) - continue; - - if (prev_dev) { - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2) { - skb2->dev = prev_dev; - netif_receive_skb(skb2); - } - } - - prev_dev = sdata->dev; - dev_sw_netstats_rx_add(sdata->dev, skb->len); - } - - if (prev_dev) { - skb->dev = prev_dev; - netif_receive_skb(skb); - return; - } - - out_free_skb: - kfree_skb_reason(skb, (__force u32)reason); -} - static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, ieee80211_rx_result res) { - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - struct ieee80211_supported_band *sband; - struct ieee80211_rate *rate = NULL; - if (res == RX_QUEUED) { I802_DEBUG_INC(rx->sdata->local->rx_handlers_queued); return; @@ -4158,23 +4076,13 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, rx->link_sta->rx_stats.dropped++; } - if (u32_get_bits((__force u32)res, SKB_DROP_REASON_SUBSYS_MASK) == - SKB_DROP_REASON_SUBSYS_MAC80211_UNUSABLE) { - kfree_skb_reason(rx->skb, (__force u32)res); - return; - } - - sband = rx->local->hw.wiphy->bands[status->band]; - if (status->encoding == RX_ENC_LEGACY) - rate = &sband->bitrates[status->rate_idx]; - - ieee80211_rx_cooked_monitor(rx, rate, res); + kfree_skb_reason(rx->skb, (__force u32)res); } static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) { - ieee80211_rx_result res = RX_DROP_MONITOR; + ieee80211_rx_result res = RX_DROP; struct sk_buff *skb; #define CALL_RXH(rxh) \ @@ -4238,7 +4146,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) { struct sk_buff_head reorder_release; - ieee80211_rx_result res = RX_DROP_MONITOR; + ieee80211_rx_result res = RX_DROP; __skb_queue_head_init(&reorder_release); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index eb889cda8bf9..41ba1d979613 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -895,8 +895,7 @@ static int ieee80211_tx_get_rates(struct ieee80211_hw *hw, } void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, - int retry_count, bool send_to_cooked, - struct ieee80211_tx_status *status) + int retry_count, struct ieee80211_tx_status *status) { struct sk_buff *skb2; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -930,10 +929,6 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, if (sdata->u.mntr.flags & MONITOR_FLAG_SKIP_TX) continue; - if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) && - !send_to_cooked) - continue; - if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { @@ -964,7 +959,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = status->info; struct sta_info *sta; __le16 fc; - bool send_to_cooked; bool acked; bool noack_success; struct ieee80211_bar *bar; @@ -1091,28 +1085,10 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp); - /* this was a transmitted frame, but now we want to reuse it */ - skb_orphan(skb); - - /* Need to make a copy before skb->cb gets cleared */ - send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || - !(ieee80211_is_data(fc)); - - /* - * This is a bit racy but we can avoid a lot of work - * with this test... - */ - if (!local->tx_mntrs && (!send_to_cooked || !local->cooked_mntrs)) { - if (status->free_list) - list_add_tail(&skb->list, status->free_list); - else - dev_kfree_skb(skb); - return; - } - - /* send to monitor interfaces */ - ieee80211_tx_monitor(local, skb, retry_count, - send_to_cooked, status); + if (status->free_list) + list_add_tail(&skb->list, status->free_list); + else + dev_kfree_skb(skb); } void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2d33eec0774c..433c5531afd1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5617,7 +5617,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!copy) return bcn; - ieee80211_tx_monitor(hw_to_local(hw), copy, 1, false, NULL); + ieee80211_tx_monitor(hw_to_local(hw), copy, 1, NULL); return bcn; } From 726a7ab24c883dbc359f8794be8350887f5d3cc1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 132/465] wifi: cfg80211: Fix trace print for removed links JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 31320ccb09a06df35d1cd1fe2531c55fe983ca19 Author: Ilan Peer Date: Wed Feb 5 11:39:11 2025 +0200 wifi: cfg80211: Fix trace print for removed links Print the mask of removed links in hexadecimal. Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.1dd2831cab5f.Ib9f5e82286f0352cd057b4bf76737223e9de8274@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 6342e30dfcd5..e1affe560a8e 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -4118,7 +4118,7 @@ TRACE_EVENT(cfg80211_links_removed, NETDEV_ASSIGN; __entry->link_mask = link_mask; ), - TP_printk(NETDEV_PR_FMT ", link_mask:%u", NETDEV_PR_ARG, + TP_printk(NETDEV_PR_FMT ", link_mask:0x%x", NETDEV_PR_ARG, __entry->link_mask) ); From cdedab0d67c28881ec6f8429915912ebb30978ca Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 133/465] wifi: mac80211: Refactor ieee80211_sta_wmm_params() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9696b80b87a0db5779f37c7818650966dd32d4ee Author: Ilan Peer Date: Wed Feb 5 11:39:12 2025 +0200 wifi: mac80211: Refactor ieee80211_sta_wmm_params() The function first updates the link configuration and then calls the driver to set the link parameters. Since the call to the driver might sleep, split the function such that the link configuration could be done without calling the driver. This would be useful in cases that WMM parameters need to be configured, but the current locking doesn't allow to call the driver. Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.b1cedcf93763.I65783c102d44127035838f97fab64ec4df5c40f3@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3d365aee8add..b2f6ebcc4569 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3375,10 +3375,10 @@ void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link) /* MLME */ static bool -ieee80211_sta_wmm_params(struct ieee80211_local *local, - struct ieee80211_link_data *link, - const u8 *wmm_param, size_t wmm_param_len, - const struct ieee80211_mu_edca_param_set *mu_edca) +_ieee80211_sta_wmm_params(struct ieee80211_local *local, + struct ieee80211_link_data *link, + const u8 *wmm_param, size_t wmm_param_len, + const struct ieee80211_mu_edca_param_set *mu_edca) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; @@ -3507,6 +3507,19 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local, for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) link->tx_conf[ac] = params[ac]; + return true; +} + +static bool +ieee80211_sta_wmm_params(struct ieee80211_local *local, + struct ieee80211_link_data *link, + const u8 *wmm_param, size_t wmm_param_len, + const struct ieee80211_mu_edca_param_set *mu_edca) +{ + if (!_ieee80211_sta_wmm_params(local, link, wmm_param, wmm_param_len, + mu_edca)) + return false; + ieee80211_mgd_set_link_qos_params(link); /* enable WMM or activate new settings */ From 16c4259218333d1b80c8487ad4a4e51274bbcbdc Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 134/465] wifi: mac80211: Add support for EPCS configuration JIRA: https://issues.redhat.com/browse/RHEL-89168 commit de86c5f60839dc0d771711a848b4f55ad3f90844 Author: Ilan Peer Date: Wed Feb 5 11:39:13 2025 +0200 wifi: mac80211: Add support for EPCS configuration Add support for configuring EPCS state: - When EPCS is enabled, send an EPCS enable request action frame to the AP. When the AP replies with EPCS enable response, enable EPCS by applying the QoS parameters provided by the AP. Do so for all the valid MLD links. Once EPCS is enabled, support processing of unsolicited EPCS enable response frames. - When EPCS is disabled, send an EPCS teardown request to the AP and apply the QoS parameters as obtained from the last received beacons. Do so for all the valid links. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.7a90afd7e140.I3f602d65f5c1fd849d6c70b12307dda33aa91ccb@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/linux/ieee80211.h | 7 + include/net/mac80211.h | 3 +- net/mac80211/cfg.c | 9 ++ net/mac80211/ieee80211_i.h | 11 ++ net/mac80211/iface.c | 8 + net/mac80211/mlme.c | 291 ++++++++++++++++++++++++++++++++++++- net/mac80211/rx.c | 17 +++ 7 files changed, 344 insertions(+), 2 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index c68c6003ed53..a10c1201b934 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1543,6 +1543,10 @@ struct ieee80211_mgmt { u8 count; u8 variable[]; } __packed ml_reconf_resp; + struct { + u8 action_code; + u8 variable[]; + } __packed epcs; } u; } __packed action; DECLARE_FLEX_ARRAY(u8, body); /* Generic frame body */ @@ -5570,6 +5574,9 @@ static inline bool ieee80211_mle_reconf_sta_prof_size_ok(const u8 *data, fixed + prof->sta_info_len - 1 <= len; } +#define IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID 0x000f +#define IEEE80211_EPCS_ENA_RESP_BODY_LEN 3 + static inline bool ieee80211_tid_to_link_map_size_ok(const u8 *data, size_t len) { const struct ieee80211_ttlm_elem *t2l = (const void *)data; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 40bb787d73d9..00aea0ce5d2f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -702,6 +702,7 @@ struct ieee80211_parsed_tpe { * @tpe: transmit power envelope information * @pwr_reduction: power constraint of BSS. * @eht_support: does this BSS support EHT + * @epcs_support: does this BSS support EPCS * @csa_active: marks whether a channel switch is going on. * @mu_mimo_owner: indicates interface owns MU-MIMO capability * @chanctx_conf: The channel context this interface is assigned to, or %NULL @@ -823,7 +824,7 @@ struct ieee80211_bss_conf { u8 pwr_reduction; bool eht_support; - + bool epcs_support; bool csa_active; bool mu_mimo_owner; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9d08e5255dd8..86fab28f503a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5196,6 +5196,14 @@ ieee80211_assoc_ml_reconf(struct wiphy *wiphy, struct net_device *dev, return ieee80211_mgd_assoc_ml_reconf(sdata, add_links, rem_links); } +static int +ieee80211_set_epcs(struct wiphy *wiphy, struct net_device *dev, bool enable) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + return ieee80211_mgd_set_epcs(sdata, enable); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -5311,4 +5319,5 @@ const struct cfg80211_ops mac80211_config_ops = { .set_ttlm = ieee80211_set_ttlm, .get_radio_mask = ieee80211_get_radio_mask, .assoc_ml_reconf = ieee80211_assoc_ml_reconf, + .set_epcs = ieee80211_set_epcs, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a90a44aa5758..b6c769fc9abf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -613,6 +613,12 @@ struct ieee80211_if_managed { u16 added_links; u8 dialog_token; } reconf; + + /* Support for epcs */ + struct { + bool enabled; + u8 dialog_token; + } epcs; }; struct ieee80211_if_ibss { @@ -2775,6 +2781,11 @@ int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata, void ieee80211_check_wbrf_support(struct ieee80211_local *local); void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); +int ieee80211_mgd_set_epcs(struct ieee80211_sub_if_data *sdata, bool enable); +void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len); +void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len); int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_link *add_links, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e627e2df3038..741495e95134 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1557,6 +1557,14 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, ieee80211_process_ml_reconf_resp(sdata, mgmt, skb->len); break; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_RESP: + ieee80211_process_epcs_ena_resp(sdata, mgmt, + skb->len); + break; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN: + ieee80211_process_epcs_teardown(sdata, mgmt, + skb->len); + break; default: break; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b2f6ebcc4569..39ffc8fc1e10 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3794,6 +3794,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, /* other links will be destroyed */ sdata->deflink.conf->bss = NULL; + sdata->deflink.conf->epcs_support = false; sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; netif_carrier_off(sdata->dev); @@ -3980,6 +3981,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, * when the flow started. */ ieee80211_ml_reconf_reset(sdata); + + ifmgd->epcs.enabled = false; + ifmgd->epcs.dialog_token = 0; } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) @@ -4848,6 +4852,82 @@ static bool ieee80211_twt_bcast_support(struct ieee80211_sub_if_data *sdata, IEEE80211_HE_MAC_CAP2_BCAST_TWT); } +static void ieee80211_epcs_changed(struct ieee80211_sub_if_data *sdata, + bool enabled) +{ + /* in any case this is called, dialog token should be reset */ + sdata->u.mgd.epcs.dialog_token = 0; + + if (sdata->u.mgd.epcs.enabled == enabled) + return; + + sdata->u.mgd.epcs.enabled = enabled; + cfg80211_epcs_changed(sdata->dev, enabled); +} + +static void ieee80211_epcs_teardown(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u8 link_id; + + if (!sdata->u.mgd.epcs.enabled) + return; + + lockdep_assert_wiphy(local->hw.wiphy); + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + struct ieee802_11_elems *elems; + struct ieee80211_link_data *link; + const struct cfg80211_bss_ies *ies; + bool ret; + + rcu_read_lock(); + + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link || !link->conf || !link->conf->bss) { + rcu_read_unlock(); + continue; + } + + if (link->u.mgd.disable_wmm_tracking) { + rcu_read_unlock(); + ieee80211_set_wmm_default(link, false, false); + continue; + } + + ies = rcu_dereference(link->conf->bss->beacon_ies); + if (!ies) { + rcu_read_unlock(); + ieee80211_set_wmm_default(link, false, false); + continue; + } + + elems = ieee802_11_parse_elems(ies->data, ies->len, false, + NULL); + if (!elems) { + rcu_read_unlock(); + ieee80211_set_wmm_default(link, false, false); + continue; + } + + ret = _ieee80211_sta_wmm_params(local, link, + elems->wmm_param, + elems->wmm_param_len, + elems->mu_edca_param_set); + + kfree(elems); + rcu_read_unlock(); + + if (!ret) { + ieee80211_set_wmm_default(link, false, false); + continue; + } + + ieee80211_mgd_set_link_qos_params(link); + ieee80211_link_info_change_notify(sdata, link, BSS_CHANGED_QOS); + } +} + static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, struct link_sta_info *link_sta, struct cfg80211_bss *cbss, @@ -5122,14 +5202,27 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, link_sta); bss_conf->eht_support = link_sta->pub->eht_cap.has_eht; + bss_conf->epcs_support = bss_conf->eht_support && + !!(elems->eht_cap->fixed.mac_cap_info[0] & + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS); + + /* EPCS might be already enabled but a new added link + * does not support EPCS. This should not really happen + * in practice. + */ + if (sdata->u.mgd.epcs.enabled && + !bss_conf->epcs_support) + ieee80211_epcs_teardown(sdata); } else { bss_conf->eht_support = false; + bss_conf->epcs_support = false; } } else { bss_conf->he_support = false; bss_conf->twt_requester = false; bss_conf->twt_protected = false; bss_conf->eht_support = false; + bss_conf->epcs_support = false; } bss_conf->twt_broadcast = @@ -7160,7 +7253,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_mgd_update_bss_param_ch_cnt(sdata, bss_conf, elems); - if (!link->u.mgd.disable_wmm_tracking && + if (!sdata->u.mgd.epcs.enabled && + !link->u.mgd.disable_wmm_tracking && ieee80211_sta_wmm_params(local, link, elems->wmm_param, elems->wmm_param_len, elems->mu_edca_param_set)) @@ -10335,3 +10429,198 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, kfree(data); return err; } + +static bool ieee80211_mgd_epcs_supp(struct ieee80211_sub_if_data *sdata) +{ + unsigned long valid_links = sdata->vif.valid_links; + u8 link_id; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (!ieee80211_vif_is_mld(&sdata->vif)) + return false; + + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *bss_conf = + sdata_dereference(sdata->vif.link_conf[link_id], sdata); + + if (WARN_ON(!bss_conf) || !bss_conf->epcs_support) + return false; + } + + return true; +} + +int ieee80211_mgd_set_epcs(struct ieee80211_sub_if_data *sdata, bool enable) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + int frame_len = offsetofend(struct ieee80211_mgmt, + u.action.u.epcs) + (enable ? 1 : 0); + + if (!ieee80211_mgd_epcs_supp(sdata)) + return -EINVAL; + + if (sdata->u.mgd.epcs.enabled == enable && + !sdata->u.mgd.epcs.dialog_token) + return 0; + + /* Do not allow enabling EPCS if the AP didn't respond yet. + * However, allow disabling EPCS in such a case. + */ + if (sdata->u.mgd.epcs.dialog_token && enable) + return -EALREADY; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + frame_len); + if (!skb) + return -ENOBUFS; + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = skb_put_zero(skb, frame_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT; + if (enable) { + u8 *pos = mgmt->u.action.u.epcs.variable; + + mgmt->u.action.u.epcs.action_code = + WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_REQ; + + *pos = ++sdata->u.mgd.dialog_token_alloc; + sdata->u.mgd.epcs.dialog_token = *pos; + } else { + mgmt->u.action.u.epcs.action_code = + WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN; + + ieee80211_epcs_teardown(sdata); + ieee80211_epcs_changed(sdata, false); + } + + ieee80211_tx_skb(sdata, skb); + return 0; +} + +static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems) +{ + const struct element *sub; + size_t scratch_len = elems->ml_epcs_len; + u8 *scratch __free(kfree) = kzalloc(scratch_len, GFP_KERNEL); + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (!ieee80211_vif_is_mld(&sdata->vif) || !elems->ml_epcs) + return; + + if (WARN_ON(!scratch)) + return; + + /* Directly parse the sub elements as the common information doesn't + * hold any useful information. + */ + for_each_mle_subelement(sub, (const u8 *)elems->ml_epcs, + elems->ml_epcs_len) { + struct ieee80211_link_data *link; + struct ieee802_11_elems *link_elems __free(kfree); + u8 *pos = (void *)sub->data; + u16 control; + ssize_t len; + u8 link_id; + + if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) + continue; + + if (sub->datalen < sizeof(control)) + break; + + control = get_unaligned_le16(pos); + link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID; + + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; + + len = cfg80211_defragment_element(sub, (u8 *)elems->ml_epcs, + elems->ml_epcs_len, + scratch, scratch_len, + IEEE80211_MLE_SUBELEM_FRAGMENT); + if (len < sizeof(control)) + continue; + + pos = scratch + sizeof(control); + len -= sizeof(control); + + link_elems = ieee802_11_parse_elems(pos, len, false, NULL); + if (!link_elems) + continue; + + if (ieee80211_sta_wmm_params(sdata->local, link, + link_elems->wmm_param, + link_elems->wmm_param_len, + link_elems->mu_edca_param_set)) + ieee80211_link_info_change_notify(sdata, link, + BSS_CHANGED_QOS); + } +} + +void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee802_11_elems *elems __free(kfree) = NULL; + size_t ies_len; + u16 status_code; + u8 *pos, dialog_token; + + if (!ieee80211_mgd_epcs_supp(sdata)) + return; + + /* Handle dialog token and status code */ + pos = mgmt->u.action.u.epcs.variable; + dialog_token = *pos; + status_code = get_unaligned_le16(pos + 1); + + /* An EPCS enable response with dialog token == 0 is an unsolicited + * notification from the AP MLD. In such a case, EPCS should already be + * enabled and status must be success + */ + if (!dialog_token && + (!sdata->u.mgd.epcs.enabled || + status_code != WLAN_STATUS_SUCCESS)) + return; + + if (sdata->u.mgd.epcs.dialog_token != dialog_token) + return; + + sdata->u.mgd.epcs.dialog_token = 0; + + if (status_code != WLAN_STATUS_SUCCESS) + return; + + pos += IEEE80211_EPCS_ENA_RESP_BODY_LEN; + ies_len = len - offsetof(struct ieee80211_mgmt, + u.action.u.epcs.variable) - + IEEE80211_EPCS_ENA_RESP_BODY_LEN; + + elems = ieee802_11_parse_elems(pos, ies_len, true, NULL); + if (!elems) + return; + + ieee80211_ml_epcs(sdata, elems); + ieee80211_epcs_changed(sdata, true); +} + +void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + if (!ieee80211_vif_is_mld(&sdata->vif) || + !sdata->u.mgd.epcs.enabled) + return; + + ieee80211_epcs_teardown(sdata); + ieee80211_epcs_changed(sdata, false); +} diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9a22d7090aa4..97a7465c2787 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3823,6 +3823,23 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) u.action.u.ml_reconf_resp) + 3) goto invalid; goto queue; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_RESP: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < offsetofend(typeof(*mgmt), + u.action.u.epcs) + + IEEE80211_EPCS_ENA_RESP_BODY_LEN) + goto invalid; + goto queue; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < offsetofend(typeof(*mgmt), + u.action.u.epcs)) + goto invalid; + goto queue; default: break; } From 13255a709d407703aa10e0a581401385814344cf Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:36 +0200 Subject: [PATCH 135/465] wifi: ieee80211: Add missing EHT MAC capabilities JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 282eeec9196fc6593540c7bf7479305a8384de32 Author: Ilan Peer Date: Wed Feb 5 11:39:14 2025 +0200 wifi: ieee80211: Add missing EHT MAC capabilities Add missing EHT MAC capabilities definitions. Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.6c1643c345a1.I7405b9c35cb39ae97a52c3fbcc36b0bd81e495dc@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/linux/ieee80211.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a10c1201b934..ac5bd6674958 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -3113,6 +3113,11 @@ ieee80211_he_spr_size(const u8 *he_spr_ie) #define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454 2 #define IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK 0x01 +#define IEEE80211_EHT_MAC_CAP1_EHT_TRS 0x02 +#define IEEE80211_EHT_MAC_CAP1_TXOP_RET 0x04 +#define IEEE80211_EHT_MAC_CAP1_TWO_BQRS 0x08 +#define IEEE80211_EHT_MAC_CAP1_EHT_LINK_ADAPT_MASK 0x30 +#define IEEE80211_EHT_MAC_CAP1_UNSOL_EPCS_PRIO_ACCESS 0x40 /* EHT PHY capabilities as defined in P802.11be_D2.0 section 9.4.2.313.3 */ #define IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ 0x02 From a4466bf5038baabd8b2756eb7325ee9900c72295 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 136/465] wifi: mac80211: Add processing of TTLM teardown frame JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8b8a673155edb97a0938fb7900dc83a7d80ca0ff Author: Ilan Peer Date: Wed Feb 5 11:39:15 2025 +0200 wifi: mac80211: Add processing of TTLM teardown frame Add processing of negotiated TTLM tear down frame. Handle this frame similar to the way a locally initiated tear down is handled. Signed-off-by: Ilan Peer Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.860691076786.I32df71182c25c5f84e4534f40efe1316926b8249@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 3 +++ net/mac80211/mlme.c | 16 +++++++++++----- net/mac80211/rx.c | 8 ++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b6c769fc9abf..df7c803134fd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2777,6 +2777,7 @@ void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata, struct cfg80211_ttlm_params *params); +void ieee80211_process_ttlm_teardown(struct ieee80211_sub_if_data *sdata); void ieee80211_check_wbrf_support(struct ieee80211_local *local); void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 741495e95134..3c175083d3fb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1553,6 +1553,9 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, ieee80211_process_neg_ttlm_res(sdata, mgmt, skb->len); break; + case WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN: + ieee80211_process_ttlm_teardown(sdata); + break; case WLAN_PROTECTED_EHT_ACTION_LINK_RECONFIG_RESP: ieee80211_process_ml_reconf_resp(sdata, mgmt, skb->len); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 39ffc8fc1e10..9b77fc2603c0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7706,13 +7706,9 @@ void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, __ieee80211_disconnect(sdata); } -static void ieee80211_teardown_ttlm_work(struct wiphy *wiphy, - struct wiphy_work *work) +void ieee80211_process_ttlm_teardown(struct ieee80211_sub_if_data *sdata) { u16 new_dormant_links; - struct ieee80211_sub_if_data *sdata = - container_of(work, struct ieee80211_sub_if_data, - u.mgd.teardown_ttlm_work); if (!sdata->vif.neg_ttlm.valid) return; @@ -7727,6 +7723,16 @@ static void ieee80211_teardown_ttlm_work(struct wiphy *wiphy, BSS_CHANGED_MLD_VALID_LINKS); } +static void ieee80211_teardown_ttlm_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.teardown_ttlm_work); + + ieee80211_process_ttlm_teardown(sdata); +} + void ieee80211_send_teardown_neg_ttlm(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 97a7465c2787..a13f6a96f67f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3811,6 +3811,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) u.action.u.ttlm_res)) goto invalid; goto queue; + case WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < offsetofend(typeof(*mgmt), + u.action.u.ttlm_tear_down)) + goto invalid; + goto queue; case WLAN_PROTECTED_EHT_ACTION_LINK_RECONFIG_RESP: if (sdata->vif.type != NL80211_IFTYPE_STATION) break; From 7573e03cf27062886f5b8200c4e303eceb75970b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 137/465] wifi: mac80211: add strict mode disabling workarounds JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3ad4fce66e4f9d82abfc366707757e29cc14a9d2 Author: Johannes Berg Date: Wed Feb 5 11:39:16 2025 +0200 wifi: mac80211: add strict mode disabling workarounds Add a strict mode where we disable certain workarounds and have additional checks such as, for now, that VHT capabilities from association response match those from beacon/probe response. We can extend the checks in the future. Make it an opt-in setting by the driver so it can be set there in some driver-specific way, for example. Also allow setting this one hw flag through the hwflags debugfs, by writing a new strict=0 or strict=1 value. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.5cecb0469479.I4a69617dc60ba0d6308416ffbc3102cfd08ba068@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/mac80211.h | 6 ++++++ net/mac80211/debugfs.c | 44 +++++++++++++++++++++++++++++++++++++++-- net/mac80211/mlme.c | 45 +++++++++++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 00aea0ce5d2f..5bde4094efd3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2852,6 +2852,11 @@ struct ieee80211_txq { * implements MLO, so operation can continue on other links when one * link is switching. * + * @IEEE80211_HW_STRICT: strictly enforce certain things mandated by the spec + * but otherwise ignored/worked around for interoperability. This is a + * HW flag so drivers can opt in according to their own control, e.g. in + * testing. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { @@ -2912,6 +2917,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_DISALLOW_PUNCTURING, IEEE80211_HW_DISALLOW_PUNCTURING_5GHZ, IEEE80211_HW_HANDLES_QUIET_CSA, + IEEE80211_HW_STRICT, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index bf0a2902d93c..69e03630f64c 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -492,6 +492,7 @@ static const char *hw_flag_names[] = { FLAG(DISALLOW_PUNCTURING), FLAG(DISALLOW_PUNCTURING_5GHZ), FLAG(HANDLES_QUIET_CSA), + FLAG(STRICT), #undef FLAG }; @@ -524,6 +525,46 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, return rv; } +static ssize_t hwflags_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + char buf[100]; + int val; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (sscanf(buf, "strict=%d", &val) == 1) { + switch (val) { + case 0: + ieee80211_hw_set(&local->hw, STRICT); + return count; + case 1: + __clear_bit(IEEE80211_HW_STRICT, local->hw.flags); + return count; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static const struct file_operations hwflags_ops = { + .open = simple_open, + .read = hwflags_read, + .write = hwflags_write, +}; + static ssize_t misc_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -574,7 +615,6 @@ static ssize_t queues_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, res); } -DEBUGFS_READONLY_FILE_OPS(hwflags); DEBUGFS_READONLY_FILE_OPS(queues); DEBUGFS_READONLY_FILE_OPS(misc); @@ -651,7 +691,7 @@ void debugfs_hw_add(struct ieee80211_local *local) #ifdef CONFIG_PM DEBUGFS_ADD_MODE(reset, 0200); #endif - DEBUGFS_ADD(hwflags); + DEBUGFS_ADD_MODE(hwflags, 0600); DEBUGFS_ADD(user_power); DEBUGFS_ADD(power); DEBUGFS_ADD(hw_conf); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9b77fc2603c0..74dff5dd614a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -168,6 +168,9 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, bool no_vht = false; u32 ht_cfreq; + if (ieee80211_hw_check(&sdata->local->hw, STRICT)) + ignore_ht_channel_mismatch = false; + *chandef = (struct cfg80211_chan_def) { .chan = channel, .width = NL80211_CHAN_WIDTH_20_NOHT, @@ -388,7 +391,7 @@ ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, * zeroes, which is nonsense, and completely inconsistent with itself * (it doesn't have 8 streams). Accept the settings in this case anyway. */ - if (!ap_min_req_set) + if (!ieee80211_hw_check(&sdata->local->hw, STRICT) && !ap_min_req_set) return true; /* make sure the AP is consistent with itself @@ -448,7 +451,7 @@ ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, * zeroes, which is nonsense, and completely inconsistent with itself * (it doesn't have 8 streams). Accept the settings in this case anyway. */ - if (!ap_min_req_set) + if (!ieee80211_hw_check(&sdata->local->hw, STRICT) && !ap_min_req_set) return true; /* Need to go over for 80MHz, 160MHz and for 80+80 */ @@ -1313,13 +1316,15 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, * Some APs apparently get confused if our capabilities are better * than theirs, so restrict what we advertise in the assoc request. */ - if (!(ap_vht_cap->vht_cap_info & - cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) - cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | - IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); - else if (!(ap_vht_cap->vht_cap_info & - cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))) - cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (!ieee80211_hw_check(&local->hw, STRICT)) { + if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) + cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + else if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))) + cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + } /* * If some other vif is using the MU-MIMO capability we cannot associate @@ -1361,14 +1366,16 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, return mu_mimo_owner; } -static void ieee80211_assoc_add_rates(struct sk_buff *skb, +static void ieee80211_assoc_add_rates(struct ieee80211_local *local, + struct sk_buff *skb, enum nl80211_chan_width width, struct ieee80211_supported_band *sband, struct ieee80211_mgd_assoc_data *assoc_data) { u32 rates; - if (assoc_data->supp_rates_len) { + if (assoc_data->supp_rates_len && + !ieee80211_hw_check(&local->hw, STRICT)) { /* * Get all rates supported by the device and the AP as * some APs don't like getting a superset of their rates @@ -1584,7 +1591,7 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata, *capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; if (sband->band != NL80211_BAND_S1GHZ) - ieee80211_assoc_add_rates(skb, width, sband, assoc_data); + ieee80211_assoc_add_rates(local, skb, width, sband, assoc_data); if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT || *capab & WLAN_CAPABILITY_RADIO_MEASURE) { @@ -2051,7 +2058,8 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) * for some reason check it and want it to be set, set the bit for all * pre-EHT connections as we used to do. */ - if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_EHT) + if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_EHT && + !ieee80211_hw_check(&local->hw, STRICT)) capab |= WLAN_CAPABILITY_ESS; /* add the elements for the assoc (main) link */ @@ -5029,7 +5037,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, * 2G/3G/4G wifi routers, reported models include the "Onda PN51T", * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device. */ - if (!is_6ghz && + if (!ieee80211_hw_check(&local->hw, STRICT) && !is_6ghz && ((assoc_data->wmm && !elems->wmm_param) || (link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT && (!elems->ht_cap_elem || !elems->ht_operation)) || @@ -5165,6 +5173,15 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, bss_vht_cap = (const void *)elem->data; } + if (ieee80211_hw_check(&local->hw, STRICT) && + (!bss_vht_cap || memcmp(bss_vht_cap, elems->vht_cap_elem, + sizeof(*bss_vht_cap)))) { + rcu_read_unlock(); + ret = false; + link_info(link, "VHT capabilities mismatch\n"); + goto out; + } + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, elems->vht_cap_elem, bss_vht_cap, link_sta); From ffab11497de1402fdd02e7243f54f67787fd7e55 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 138/465] wifi: mac80211_hwsim: enable strict mode JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7364a4688ba4ab4f90d9da0ebbb4e4250bae0a27 Author: Johannes Berg Date: Wed Feb 5 11:39:17 2025 +0200 wifi: mac80211_hwsim: enable strict mode Since we use hwsim for testing e.g. the hostapd implementation, enable strict mode to catch errors that would otherwise not be caught. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.85bee694f09c.I61ec37d20fe97699d47fce252dc4ae2e4475fc51@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/virtual/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index fe0e9b10e82e..7a45f539fd49 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -5345,6 +5345,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); ieee80211_hw_set(hw, TDLS_WIDER_BW); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, STRICT); if (param->mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; From 015267a554ebfb9127e3d2fb7a14010533c25856 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 139/465] wifi: mac80211: add HT and VHT basic set verification JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 574faa0e936d12718e2cadad11ce1e184d9e5a32 Author: Benjamin Berg Date: Wed Feb 5 11:39:18 2025 +0200 wifi: mac80211: add HT and VHT basic set verification So far we did not verify the HT and VHT basic MCS set. However, in P802.11REVme/D7.0 (6.5.4.2.4) says that the MLME-JOIN.request shall return an error if the VHT and HT basic set requirements are not met. Given broken APs, apply VHT basic MCS/NSS set checks only in strict mode. Signed-off-by: Benjamin Berg Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.e2d8d4095f6b.I66bcf6c2de3b9d3325e4ffd9f573f4cd26ce5685@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 74dff5dd614a..bbc50e4fdd43 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -345,6 +345,115 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, return IEEE80211_CONN_MODE_EHT; } +static bool +ieee80211_verify_sta_ht_mcs_support(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct ieee80211_ht_operation *ht_op) +{ + struct ieee80211_sta_ht_cap sta_ht_cap; + int i; + + if (sband->band == NL80211_BAND_6GHZ) + return true; + + if (!ht_op) + return false; + + memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); + ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); + + /* + * P802.11REVme/D7.0 - 6.5.4.2.4 + * ... + * If the MLME of an HT STA receives an MLME-JOIN.request primitive + * with the SelectedBSS parameter containing a Basic HT-MCS Set field + * in the HT Operation parameter that contains any unsupported MCSs, + * the MLME response in the resulting MLME-JOIN.confirm primitive shall + * contain a ResultCode parameter that is not set to the value SUCCESS. + * ... + */ + + /* Simply check that all basic rates are in the STA RX mask */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + if ((ht_op->basic_set[i] & sta_ht_cap.mcs.rx_mask[i]) != + ht_op->basic_set[i]) + return false; + } + + return true; +} + +static bool +ieee80211_verify_sta_vht_mcs_support(struct ieee80211_sub_if_data *sdata, + int link_id, + struct ieee80211_supported_band *sband, + const struct ieee80211_vht_operation *vht_op) +{ + struct ieee80211_sta_vht_cap sta_vht_cap; + u16 ap_min_req_set, sta_rx_mcs_map, sta_tx_mcs_map; + int nss; + + if (sband->band != NL80211_BAND_5GHZ) + return true; + + if (!vht_op) + return false; + + memcpy(&sta_vht_cap, &sband->vht_cap, sizeof(sta_vht_cap)); + ieee80211_apply_vhtcap_overrides(sdata, &sta_vht_cap); + + ap_min_req_set = le16_to_cpu(vht_op->basic_mcs_set); + sta_rx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.rx_mcs_map); + sta_tx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.tx_mcs_map); + + /* + * Many APs are incorrectly advertising an all-zero value here, + * which really means MCS 0-7 are required for 1-8 streams, but + * they don't really mean it that way. + * Some other APs are incorrectly advertising 3 spatial streams + * with MCS 0-7 are required, but don't really mean it that way + * and we'll connect only with HT, rather than even HE. + * As a result, unfortunately the VHT basic MCS/NSS set cannot + * be used at all, so check it only in strict mode. + */ + if (!ieee80211_hw_check(&sdata->local->hw, STRICT)) + return true; + + /* + * P802.11REVme/D7.0 - 6.5.4.2.4 + * ... + * If the MLME of a VHT STA receives an MLME-JOIN.request primitive + * with a SelectedBSS parameter containing a Basic VHT-MCS And NSS Set + * field in the VHT Operation parameter that contains any unsupported + * tuple, the MLME response in the resulting + * MLME-JOIN.confirm primitive shall contain a ResultCode parameter + * that is not set to the value SUCCESS. + * ... + */ + for (nss = 8; nss > 0; nss--) { + u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; + u8 sta_rx_val; + u8 sta_tx_val; + + if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) + continue; + + sta_rx_val = (sta_rx_mcs_map >> (2 * (nss - 1))) & 3; + sta_tx_val = (sta_tx_mcs_map >> (2 * (nss - 1))) & 3; + + if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || + sta_rx_val < ap_op_val || sta_tx_val < ap_op_val) { + link_id_info(sdata, link_id, + "Missing mandatory rates for %d Nss, rx %d, tx %d oper %d, disable VHT\n", + nss, sta_rx_val, sta_tx_val, ap_op_val); + return false; + } + } + + return true; +} + static bool ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, int link_id, @@ -1042,6 +1151,26 @@ again: link_id_info(sdata, link_id, "regulatory prevented using AP config, downgraded\n"); + if (conn->mode >= IEEE80211_CONN_MODE_HT && + !ieee80211_verify_sta_ht_mcs_support(sdata, sband, + elems->ht_operation)) { + conn->mode = IEEE80211_CONN_MODE_LEGACY; + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; + link_id_info(sdata, link_id, + "required MCSes not supported, disabling HT\n"); + } + + if (conn->mode >= IEEE80211_CONN_MODE_VHT && + !ieee80211_verify_sta_vht_mcs_support(sdata, link_id, sband, + elems->vht_operation)) { + conn->mode = IEEE80211_CONN_MODE_HT; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_40); + link_id_info(sdata, link_id, + "required MCSes not supported, disabling VHT\n"); + } + if (conn->mode >= IEEE80211_CONN_MODE_HE && (!ieee80211_verify_peer_he_mcs_support(sdata, link_id, (void *)elems->he_cap, From 69e1f2c521961901279ad1cc8057337b785365bb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 140/465] wifi: mac80211: tests: add tests for ieee80211_determine_chan_mode JIRA: https://issues.redhat.com/browse/RHEL-89168 Conflicts: - net/mac80211/tests/chan-mode.c Keep the behavior of MODULE_IMPORT_NS macro because cdd30ebb1b9f ("module: Convert symbol namespace to string literal") and others were not applied. commit b46524b57afdde8ed9ee9567e78d1293ad0e6ea7 Author: Benjamin Berg Date: Wed Feb 5 11:39:19 2025 +0200 wifi: mac80211: tests: add tests for ieee80211_determine_chan_mode Add a few tests for ieee80211_determine_chan_mode that check that mac80211 will not try to connect to an AP if an advertised basic rate is not supported. Signed-off-by: Benjamin Berg Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.530c81eb7fdc.Ia77f5efdf9efb70d2766a3d6bf425553bcb308e8@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/ieee80211_i.h | 7 + net/mac80211/mlme.c | 3 +- net/mac80211/tests/Makefile | 2 +- net/mac80211/tests/chan-mode.c | 254 +++++++++++++++++++++++++++++++++ net/mac80211/tests/util.c | 6 +- 5 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 net/mac80211/tests/chan-mode.c diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index df7c803134fd..f23be8b5d0d8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2804,6 +2804,13 @@ int ieee80211_calc_chandef_subchan_offset(const struct cfg80211_chan_def *ap, void ieee80211_rearrange_tpe_psd(struct ieee80211_parsed_tpe_psd *psd, const struct cfg80211_chan_def *ap, const struct cfg80211_chan_def *used); +struct ieee802_11_elems * +ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, + struct ieee80211_conn_settings *conn, + struct cfg80211_bss *cbss, int link_id, + struct ieee80211_chan_req *chanreq, + struct cfg80211_chan_def *ap_chandef, + unsigned long *userspace_selectors); #else #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) #define VISIBLE_IF_MAC80211_KUNIT static diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bbc50e4fdd43..837a18a67600 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -989,7 +989,7 @@ static void ieee80211_set_chanreq_ap(struct ieee80211_sub_if_data *sdata, chanreq->ap = *ap_chandef; } -static struct ieee802_11_elems * +VISIBLE_IF_MAC80211_KUNIT struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, struct cfg80211_bss *cbss, int link_id, @@ -1214,6 +1214,7 @@ free: kfree(elems); return ERR_PTR(ret); } +EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_determine_chan_mode); static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee802_11_elems *elems, diff --git a/net/mac80211/tests/Makefile b/net/mac80211/tests/Makefile index 0f5336bc7314..3b0c08356fc5 100644 --- a/net/mac80211/tests/Makefile +++ b/net/mac80211/tests/Makefile @@ -1,3 +1,3 @@ -mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o +mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c new file mode 100644 index 000000000000..daff469beb83 --- /dev/null +++ b/net/mac80211/tests/chan-mode.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for channel mode functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include +#include + +#include "util.h" + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static const struct determine_chan_mode_case { + const char *desc; + u8 extra_supp_rate; + enum ieee80211_conn_mode conn_mode; + enum ieee80211_conn_mode expected_mode; + bool strict; + u8 userspace_selector; + struct ieee80211_ht_cap ht_capa_mask; + struct ieee80211_vht_cap vht_capa; + struct ieee80211_vht_cap vht_capa_mask; + u8 vht_basic_mcs_1_4_set:1, + vht_basic_mcs_5_8_set:1, + he_basic_mcs_1_4_set:1, + he_basic_mcs_5_8_set:1; + u8 vht_basic_mcs_1_4, vht_basic_mcs_5_8; + u8 he_basic_mcs_1_4, he_basic_mcs_5_8; + u8 eht_mcs7_min_nss; + int error; +} determine_chan_mode_cases[] = { + { + .desc = "Normal case, EHT is working", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_EHT, + }, { + .desc = "Requiring EHT support is fine", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_EHT, + .extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY, + }, { + .desc = "Lowering the mode limits us", + .conn_mode = IEEE80211_CONN_MODE_VHT, + .expected_mode = IEEE80211_CONN_MODE_VHT, + }, { + .desc = "Requesting a basic rate/selector that we do not support", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1), + .error = EINVAL, + }, { + .desc = "As before, but userspace says it is taking care of it", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .userspace_selector = BSS_MEMBERSHIP_SELECTOR_MIN - 1, + .extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1), + .expected_mode = IEEE80211_CONN_MODE_EHT, + }, { + .desc = "Masking out a supported rate in HT capabilities", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_LEGACY, + .ht_capa_mask = { + .mcs.rx_mask[0] = 0xf7, + }, + }, { + .desc = "Masking out a RX rate in VHT capabilities", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_HT, + /* Only one RX stream at MCS 0-7 */ + .vht_capa = { + .supp_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7), + }, + .vht_capa_mask = { + .supp_mcs.rx_mcs_map = cpu_to_le16(0xffff), + }, + .strict = true, + }, { + .desc = "Masking out a TX rate in VHT capabilities", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_HT, + /* Only one TX stream at MCS 0-7 */ + .vht_capa = { + .supp_mcs.tx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7), + }, + .vht_capa_mask = { + .supp_mcs.tx_mcs_map = cpu_to_le16(0xffff), + }, + .strict = true, + }, { + .desc = "AP has higher VHT requirement than client", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_HT, + .vht_basic_mcs_5_8_set = 1, + .vht_basic_mcs_5_8 = 0xFE, /* require 5th stream */ + .strict = true, + }, { + .desc = "all zero VHT basic rates are ignored (many APs broken)", + .conn_mode = IEEE80211_CONN_MODE_VHT, + .expected_mode = IEEE80211_CONN_MODE_VHT, + .vht_basic_mcs_1_4_set = 1, + .vht_basic_mcs_5_8_set = 1, + }, { + .desc = "AP requires 3 HE streams but client only has two", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_VHT, + .he_basic_mcs_1_4 = 0b11001010, + .he_basic_mcs_1_4_set = 1, + }, { + .desc = "all zero HE basic rates are ignored (iPhone workaround)", + .conn_mode = IEEE80211_CONN_MODE_HE, + .expected_mode = IEEE80211_CONN_MODE_HE, + .he_basic_mcs_1_4_set = 1, + .he_basic_mcs_5_8_set = 1, + }, { + .desc = "AP requires too many RX streams with EHT MCS 7", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_HE, + .eht_mcs7_min_nss = 0x15, + }, { + .desc = "AP requires too many TX streams with EHT MCS 7", + .conn_mode = IEEE80211_CONN_MODE_EHT, + .expected_mode = IEEE80211_CONN_MODE_HE, + .eht_mcs7_min_nss = 0x51, + }, { + .desc = "AP requires too many RX streams with EHT MCS 7 and EHT is required", + .extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY, + .conn_mode = IEEE80211_CONN_MODE_EHT, + .eht_mcs7_min_nss = 0x15, + .error = EINVAL, + } +}; +KUNIT_ARRAY_PARAM_DESC(determine_chan_mode, determine_chan_mode_cases, desc) + +static void test_determine_chan_mode(struct kunit *test) +{ + const struct determine_chan_mode_case *params = test->param_value; + struct t_sdata *t_sdata = T_SDATA(test); + struct ieee80211_conn_settings conn = { + .mode = params->conn_mode, + .bw_limit = IEEE80211_CONN_BW_LIMIT_20, + }; + struct cfg80211_bss cbss = { + .channel = &t_sdata->band_5ghz.channels[0], + }; + unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; + u8 bss_ies[] = { + /* Supported Rates */ + WLAN_EID_SUPP_RATES, 0x08, + 0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24, + /* Extended Supported Rates */ + WLAN_EID_EXT_SUPP_RATES, 0x05, + 0x30, 0x48, 0x60, 0x6c, params->extra_supp_rate, + /* HT Capabilities */ + WLAN_EID_HT_CAPABILITY, 0x1a, + 0x0c, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + /* HT Information (0xff for 1 stream) */ + WLAN_EID_HT_OPERATION, 0x16, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* VHT Capabilities */ + WLAN_EID_VHT_CAPABILITY, 0xc, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, + /* VHT Operation */ + WLAN_EID_VHT_OPERATION, 0x05, + 0x00, 0x00, 0x00, + params->vht_basic_mcs_1_4_set ? + params->vht_basic_mcs_1_4 : + le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff), + params->vht_basic_mcs_5_8_set ? + params->vht_basic_mcs_5_8 : + le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff00), + /* HE Capabilities */ + WLAN_EID_EXTENSION, 0x16, WLAN_EID_EXT_HE_CAPABILITY, + 0x01, 0x78, 0xc8, 0x1a, 0x40, 0x00, 0x00, 0xbf, + 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfa, 0xff, 0xfa, 0xff, + /* HE Operation (permit overriding values) */ + WLAN_EID_EXTENSION, 0x07, WLAN_EID_EXT_HE_OPERATION, + 0xf0, 0x3f, 0x00, 0xb0, + params->he_basic_mcs_1_4_set ? params->he_basic_mcs_1_4 : 0xfc, + params->he_basic_mcs_5_8_set ? params->he_basic_mcs_5_8 : 0xff, + /* EHT Capabilities */ + WLAN_EID_EXTENSION, 0x12, WLAN_EID_EXT_EHT_CAPABILITY, + 0x07, 0x00, 0x1c, 0x00, 0x00, 0xfe, 0xff, 0xff, + 0x7f, 0x01, 0x00, 0x88, 0x88, 0x88, 0x00, 0x00, + 0x00, + /* EHT Operation */ + WLAN_EID_EXTENSION, 0x09, WLAN_EID_EXT_EHT_OPERATION, + 0x01, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, + }; + struct ieee80211_chan_req chanreq = {}; + struct cfg80211_chan_def ap_chandef = {}; + struct ieee802_11_elems *elems; + + if (params->strict) + set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); + else + clear_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); + + t_sdata->sdata->u.mgd.ht_capa_mask = params->ht_capa_mask; + t_sdata->sdata->u.mgd.vht_capa = params->vht_capa; + t_sdata->sdata->u.mgd.vht_capa_mask = params->vht_capa_mask; + + if (params->userspace_selector) + set_bit(params->userspace_selector, userspace_selectors); + + rcu_assign_pointer(cbss.ies, + kunit_kzalloc(test, + sizeof(cbss) + sizeof(bss_ies), + GFP_KERNEL)); + KUNIT_ASSERT_NOT_NULL(test, rcu_access_pointer(cbss.ies)); + ((struct cfg80211_bss_ies *)rcu_access_pointer(cbss.ies))->len = sizeof(bss_ies); + + memcpy((void *)rcu_access_pointer(cbss.ies)->data, bss_ies, + sizeof(bss_ies)); + + rcu_read_lock(); + elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss, + 0, &chanreq, &ap_chandef, + userspace_selectors); + rcu_read_unlock(); + + /* We do not need elems, free them if they are valid. */ + if (!IS_ERR_OR_NULL(elems)) + kfree(elems); + + if (params->error) { + KUNIT_ASSERT_TRUE(test, IS_ERR(elems)); + KUNIT_ASSERT_EQ(test, PTR_ERR(elems), -params->error); + } else { + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elems); + KUNIT_ASSERT_EQ(test, conn.mode, params->expected_mode); + } +} + +static struct kunit_case chan_mode_cases[] = { + KUNIT_CASE_PARAM(test_determine_chan_mode, + determine_chan_mode_gen_params), + {} +}; + +static struct kunit_suite chan_mode = { + .name = "mac80211-mlme-chan-mode", + .test_cases = chan_mode_cases, +}; + +kunit_test_suite(chan_mode); diff --git a/net/mac80211/tests/util.c b/net/mac80211/tests/util.c index 0936a73e3617..9c2d63a5cd2b 100644 --- a/net/mac80211/tests/util.c +++ b/net/mac80211/tests/util.c @@ -266,11 +266,7 @@ int t_sdata_init(struct kunit_resource *resource, void *ctx) cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14); + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6); sband->vht_cap.vht_mcs.tx_mcs_map = sband->vht_cap.vht_mcs.rx_mcs_map; break; From 1698bc980e23b6ea61c4e6e8f89f813637800ea2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 141/465] wifi: mac80211: set ieee80211_prep_tx_info::link_id upon Auth Rx JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8c60179b64434894eac1ffab7396bac131bc8b6e Author: Emmanuel Grumbach Date: Wed Feb 5 11:39:20 2025 +0200 wifi: mac80211: set ieee80211_prep_tx_info::link_id upon Auth Rx This will be used by the low level driver. Note that link_id will be 0 in case of a non-MLO authentication. Also fix a call-site of mgd_prepare_tx() where the link_id was not populated. Update the documentation to reflect the current state ieee80211_prep_tx_info::link_id is also available in mgd_complete_tx(). Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.6a590f189ce5.I1fc5c0da26b143f5b07191eb592f01f7083d55ae@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/mac80211.h | 4 ++-- net/mac80211/driver-ops.h | 3 ++- net/mac80211/mlme.c | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5bde4094efd3..e1131bcdac86 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7,7 +7,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2024 Intel Corporation + * Copyright (C) 2018 - 2025 Intel Corporation */ #ifndef MAC80211_H @@ -3830,7 +3830,7 @@ enum ieee80211_reconfig_type { * @was_assoc: set if this call is due to deauth/disassoc * while just having been associated * @link_id: the link id on which the frame will be TX'ed. - * Only used with the mgd_prepare_tx() method. + * 0 for a non-MLO connection. */ struct ieee80211_prep_tx_info { u16 duration; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5acecc7bd4a9..307587c8a003 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016 Intel Deutschland GmbH -* Copyright (C) 2018-2019, 2021-2024 Intel Corporation +* Copyright (C) 2018-2019, 2021-2025 Intel Corporation */ #ifndef __MAC80211_DRIVER_OPS @@ -955,6 +955,7 @@ static inline void drv_mgd_complete_tx(struct ieee80211_local *local, return; WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); + info->link_id = info->link_id < 0 ? 0 : info->link_id; trace_drv_mgd_complete_tx(local, sdata, info->duration, info->subtype, info->success); if (local->ops->mgd_complete_tx) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 837a18a67600..8caeecc411ce 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8,7 +8,7 @@ * Copyright 2007, Michael Wu * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2024 Intel Corporation + * Copyright (C) 2018 - 2025 Intel Corporation */ #include @@ -4725,6 +4725,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); status_code = le16_to_cpu(mgmt->u.auth.status_code); + info.link_id = ifmgd->auth_data->link_id; + if (auth_alg != ifmgd->auth_data->algorithm || (auth_alg != WLAN_AUTH_SAE && auth_transaction != ifmgd->auth_data->expected_transaction) || From 86a96bb2227e41d11f388e90b270d78c0bee6eac Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 142/465] wifi: mac80211: remove misplaced drv_mgd_complete_tx() call JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f4995cdc4d02d0abc8e9fcccad5c71ce676c1e3f Author: Johannes Berg Date: Wed Feb 5 11:39:21 2025 +0200 wifi: mac80211: remove misplaced drv_mgd_complete_tx() call In the original commit 15fae3410f1d ("mac80211: notify driver on mgd TX completion") I evidently made a mistake and placed the call in the "associated" if, rather than the "assoc_data". Later I noticed the missing call and placed it in commit c042600c17d8 ("wifi: mac80211: adding missing drv_mgd_complete_tx() call"), but didn't remove the wrong one. Remove it now. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.6ed954179bbf.Id8ef8835b7e6da3bf913c76f77d201017dc8a3c9@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8caeecc411ce..0c10718c3202 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9752,7 +9752,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, req->reason_code, false); - drv_mgd_complete_tx(sdata->local, sdata, &info); return 0; } From 4e8a18cf57e6b01e97651b75db69090005f35ffa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 143/465] wifi: mac80211: don't unconditionally call drv_mgd_complete_tx() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1798271b3604b902d45033ec569f2bf77e94ecc2 Author: Johannes Berg Date: Wed Feb 5 11:39:22 2025 +0200 wifi: mac80211: don't unconditionally call drv_mgd_complete_tx() We might not have called drv_mgd_prepare_tx(), so only call drv_mgd_complete_tx() under the same conditions. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.e091fc39a351.Ie6a3cdca070612a0aa4b3c6914ab9ed602d1f456@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0c10718c3202..a7a23d7ed390 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3976,7 +3976,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, if (tx) ieee80211_flush_queues(local, sdata, false); - drv_mgd_complete_tx(sdata->local, sdata, &info); + if (tx || frame_buf) + drv_mgd_complete_tx(sdata->local, sdata, &info); /* clear AP addr only after building the needed mgmt frames */ eth_zero_addr(sdata->deflink.u.mgd.bssid); From 144b7c7e91edac2021fd953b8db657c17f1569d7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:37 +0200 Subject: [PATCH 144/465] wifi: mac80211: always send max agg subframe num in strict mode JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3fca951123b68df8d1b47bbf90409f8a3671362b Author: Johannes Berg Date: Wed Feb 5 11:39:23 2025 +0200 wifi: mac80211: always send max agg subframe num in strict mode Instead of only sending the correct number for EHT and up, always send the correct number as it should be in strict mode. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.5910263db6da.Icd1f93fabc9705e4e760d834095c29b60b934d9e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/agg-tx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 61f2cac37728..92120f905149 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2023 Intel Corporation + * Copyright (C) 2018 - 2024 Intel Corporation */ #include @@ -464,7 +464,8 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta, sta->ampdu_mlme.addba_req_num[tid]++; spin_unlock_bh(&sta->lock); - if (sta->sta.deflink.eht_cap.has_eht) { + if (sta->sta.deflink.eht_cap.has_eht || + ieee80211_hw_check(&local->hw, STRICT)) { buf_size = local->hw.max_tx_aggregation_subframes; } else if (sta->sta.deflink.he_cap.has_he) { buf_size = min_t(u16, local->hw.max_tx_aggregation_subframes, From dc2cbeea370ad94d5dbbb22be60f0c31917ab1ac Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 145/465] wifi: mac80211: aggregation: remove deflink accesses for MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3979c8e6205b268ef99d34d09447981eafcd1ed9 Author: Johannes Berg Date: Wed Feb 5 11:39:24 2025 +0200 wifi: mac80211: aggregation: remove deflink accesses for MLO If a station has connected with MLO (as indicated by valid_links being non-zero, even if that may have just a single bit set), it necessarily supports EHT/aggregation, so we don't need to check the deflink for those cases. Add conditions so we can support removing the link it/we used to associate on. Note that we still use the statistics in the deflink, but that's a whole different story we will need to address separately. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.daf2a9e367f2.Id2c2dfbbe7451cc900ed88c5a81b33c55b4ab1cf@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/agg-rx.c | 22 +++++++++++++--------- net/mac80211/agg-tx.c | 6 ++++-- net/mac80211/tx.c | 3 ++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f3fbe5a4395e..aeb99d102c6e 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ /** @@ -206,17 +206,19 @@ u8 ieee80211_retrieve_addba_ext_data(struct sta_info *sta, elems = ieee802_11_parse_elems(elem_data, elem_len, true, NULL); - if (elems && !elems->parse_error && elems->addba_ext_ie) { - data = elems->addba_ext_ie->data; + if (!elems || elems->parse_error || !elems->addba_ext_ie) + goto free; - if (!sta->sta.deflink.eht_cap.has_eht || !buf_size) - goto free; + data = elems->addba_ext_ie->data; + if (buf_size && + (sta->sta.valid_links || sta->sta.deflink.eht_cap.has_eht)) { buf_size_1k = u8_get_bits(elems->addba_ext_ie->data, IEEE80211_ADDBA_EXT_BUF_SIZE_MASK); *buf_size |= (u16)buf_size_1k << IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT; } + free: kfree(elems); @@ -258,7 +260,7 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout); mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); - if (sta->sta.deflink.he_cap.has_he) + if (sta->sta.valid_links || sta->sta.deflink.he_cap.has_he) ieee80211_add_addbaext(skb, req_addba_ext_data, buf_size); ieee80211_tx_skb(sdata, skb); @@ -293,7 +295,8 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, goto end; } - if (!sta->sta.deflink.ht_cap.ht_supported && + if (!sta->sta.valid_links && + !sta->sta.deflink.ht_cap.ht_supported && !sta->sta.deflink.he_cap.has_he) { ht_dbg(sta->sdata, "STA %pM erroneously requests BA session on tid %d w/o HT\n", @@ -309,7 +312,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, goto end; } - if (sta->sta.deflink.eht_cap.has_eht) + if (sta->sta.valid_links || sta->sta.deflink.eht_cap.has_eht) max_buf_size = IEEE80211_MAX_AMPDU_BUF_EHT; else if (sta->sta.deflink.he_cap.has_he) max_buf_size = IEEE80211_MAX_AMPDU_BUF_HE; @@ -321,7 +324,8 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, * and if buffer size does not exceeds max value */ /* XXX: check own ht delayed BA capability?? */ if (((ba_policy != 1) && - (!(sta->sta.deflink.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || + (sta->sta.valid_links || + !(sta->sta.deflink.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || (buf_size > max_buf_size)) { status = WLAN_STATUS_INVALID_QOS_PARAM; ht_dbg_ratelimited(sta->sdata, diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 92120f905149..63a5e48291ac 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -464,7 +464,8 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta, sta->ampdu_mlme.addba_req_num[tid]++; spin_unlock_bh(&sta->lock); - if (sta->sta.deflink.eht_cap.has_eht || + if (sta->sta.valid_links || + sta->sta.deflink.eht_cap.has_eht || ieee80211_hw_check(&local->hw, STRICT)) { buf_size = local->hw.max_tx_aggregation_subframes; } else if (sta->sta.deflink.he_cap.has_he) { @@ -609,7 +610,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, "Requested to start BA session on reserved tid=%d", tid)) return -EINVAL; - if (!pubsta->deflink.ht_cap.ht_supported && + if (!pubsta->valid_links && + !pubsta->deflink.ht_cap.ht_supported && !pubsta->deflink.vht_cap.vht_supported && !pubsta->deflink.he_cap.has_he && !pubsta->deflink.eht_cap.has_eht) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 433c5531afd1..0eff35562a31 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1182,7 +1182,8 @@ void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata, if (!ref || !(ref->ops->capa & RATE_CTRL_CAPA_AMPDU_TRIGGER)) return; - if (!sta || !sta->sta.deflink.ht_cap.ht_supported || + if (!sta || + (!sta->sta.valid_links && !sta->sta.deflink.ht_cap.ht_supported) || !sta->sta.wme || skb_get_queue_mapping(skb) == IEEE80211_AC_VO || skb->protocol == sdata->control_port_protocol) return; From df10898012e7f4b5370c68402a46b33aa264a0ab Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 146/465] wifi: mac80211: enable removing assoc link JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a883ad479dbbbab80543678389582fa90d2b9339 Author: Johannes Berg Date: Wed Feb 5 11:39:25 2025 +0200 wifi: mac80211: enable removing assoc link With the previous patch to no longer access deflink for aggregation it seems we no longer access the deflink for MLO stations (MLDs) so we can allow removing the assoc link. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.05bc2175cea2.I8f62609a682fdf3f703872d0fce63ab6a4780a7e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a7a23d7ed390..801794b85e7f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10392,9 +10392,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, if (WARN_ON(!sta)) return -ENOLINK; - if (rem_links & BIT(sta->sta.deflink.link_id)) - return -EINVAL; - /* Adding links to the set of valid link is done only after a successful * ML reconfiguration frame exchange. Here prepare the data for the ML * reconfiguration frame construction and allocate the required From 4cdc4c54618f61754a5a868b1de2225587d7af55 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 147/465] wifi: mac80211: ensure sdata->work is canceled before initialized. JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6c93fd502023dd919b5987ccbe990735410edd49 Author: Miri Korenblit Date: Wed Feb 5 11:39:26 2025 +0200 wifi: mac80211: ensure sdata->work is canceled before initialized. This wiphy work is canceled when the iface is stopped, and shouldn't be queued for a non-running iface. If it happens to be queued for a non-running iface (due to a bug) it can cause a corruption of wiphy_work_list when ieee80211_setup_sdata is called. Make sure to cancel it in this case and warn on. Signed-off-by: Miri Korenblit Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20250205110958.99204c767c10.I84ce27a239059f6009cee197b252549a11426046@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/iface.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 3c175083d3fb..77d0078616fb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -8,7 +8,7 @@ * Copyright 2008, Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include @@ -804,6 +804,9 @@ static void ieee80211_set_multicast_list(struct net_device *dev) */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { + if (WARN_ON(!list_empty(&sdata->work.entry))) + wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->work); + /* free extra data */ ieee80211_free_keys(sdata, false); From b2c8a79612859585b268e6e7fb7b9c285e806958 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 148/465] wifi: mac80211: rework the Tx of the deauth in ieee80211_set_disassoc() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ccbaf782390deb584d64dd2cf6aba983737bc48a Author: Emmanuel Grumbach Date: Wed Feb 5 11:39:27 2025 +0200 wifi: mac80211: rework the Tx of the deauth in ieee80211_set_disassoc() When we disassociate we may need to send a deauth frame. Regardless of this decision, we need to flush the queues to drop all the packets on the Tx queues. The flow looks like this: 1) Flush packets waiting on the queues (drop=true) 2) Prepare Tx to send the deauth 3) Build the deauth header 4) send the deauth 5) Flush the deauth packet (drop=false) 6) Complete_tx Step 3 and 4 are done in ieee80211_send_deauth_disassoc() and that function must be called even if we decide not to send the deauth frame because we need step 3 for cfg80211. This means that if we want to send the deauth frame, we need all the steps, but if we don't want to send the deauth frame we still want step 1 and 3. Change the code to do that. Also, prevent sending the deauth frame if we are in the middle of a CSA with mode=1 in which case we won't be able to send the frame anyway. This caused issues in iwlwifi at step 5 since the firmware wouldn't send the frame and we'd be stuck flushing with drop=false. Implement this in ieee80211_set_disassoc() which has many callers. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205110958.480bfea605e0.I91131eed942e49b9885d73f4180a3c9c26691c62@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 68 ++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 801794b85e7f..856014afdde0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3930,6 +3930,31 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->associated = false; + if (tx) { + bool tx_link_found = false; + + for (link_id = 0; + link_id < ARRAY_SIZE(sdata->link); + link_id++) { + struct ieee80211_link_data *link; + + if (!ieee80211_vif_link_active(&sdata->vif, link_id)) + continue; + + link = sdata_dereference(sdata->link[link_id], sdata); + if (WARN_ON_ONCE(!link)) + continue; + + if (link->u.mgd.csa.blocked_tx) + continue; + + tx_link_found = true; + break; + } + + tx = tx_link_found; + } + /* other links will be destroyed */ sdata->deflink.conf->bss = NULL; sdata->deflink.conf->epcs_support = false; @@ -3960,24 +3985,24 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, * insist sending these frames which can take time and delay * the disconnection and possible the roaming. */ - if (tx) - ieee80211_flush_queues(local, sdata, true); + ieee80211_flush_queues(local, sdata, true); - /* deauthenticate/disassociate now */ - if (tx || frame_buf) { + if (tx) { drv_mgd_prepare_tx(sdata->local, sdata, &info); ieee80211_send_deauth_disassoc(sdata, sdata->vif.cfg.ap_addr, sdata->vif.cfg.ap_addr, stype, - reason, tx, frame_buf); - } + reason, true, frame_buf); - /* flush out frame - make sure the deauth was actually sent */ - if (tx) + /* flush out frame - make sure the deauth was actually sent */ ieee80211_flush_queues(local, sdata, false); - if (tx || frame_buf) drv_mgd_complete_tx(sdata->local, sdata, &info); + } else if (frame_buf) { + ieee80211_send_deauth_disassoc(sdata, sdata->vif.cfg.ap_addr, + sdata->vif.cfg.ap_addr, stype, + reason, false, frame_buf); + } /* clear AP addr only after building the needed mgmt frames */ eth_zero_addr(sdata->deflink.u.mgd.bssid); @@ -4403,33 +4428,12 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; - bool tx = false; lockdep_assert_wiphy(local->hw.wiphy); if (!ifmgd->associated) return; - /* only transmit if we have a link that makes that worthwhile */ - for (unsigned int link_id = 0; - link_id < ARRAY_SIZE(sdata->link); - link_id++) { - struct ieee80211_link_data *link; - - if (!ieee80211_vif_link_active(&sdata->vif, link_id)) - continue; - - link = sdata_dereference(sdata->link[link_id], sdata); - if (WARN_ON_ONCE(!link)) - continue; - - if (link->u.mgd.csa.blocked_tx) - continue; - - tx = true; - break; - } - if (!ifmgd->driver_disconnect) { unsigned int link_id; @@ -4457,14 +4461,14 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ifmgd->driver_disconnect ? WLAN_REASON_DEAUTH_LEAVING : WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - tx, frame_buf); + true, frame_buf); /* the other links will be destroyed */ sdata->vif.bss_conf.csa_active = false; sdata->deflink.u.mgd.csa.waiting_bcn = false; sdata->deflink.u.mgd.csa.blocked_tx = false; ieee80211_vif_unblock_queues_csa(sdata); - ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx, + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, ifmgd->reconnect); ifmgd->reconnect = false; From 9b2efd7db8ecc86d1b83b2b204eec5f98c423e8d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 149/465] wifi: iwlwifi: mvm: rename and move iwl_mvm_eval_dsm_rfi() to iwl_rfi_is_enabled_in_bios() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ca47dcc0d0ffd177cbe7cf172c32bef4b0ae3f3a Author: Anjaneyulu Date: Wed Feb 5 14:55:33 2025 +0200 wifi: iwlwifi: mvm: rename and move iwl_mvm_eval_dsm_rfi() to iwl_rfi_is_enabled_in_bios() Renamed iwl_mvm_eval_dsm_rfi() to iwl_rfi_is_enabled_in_bios() to better reflect the function's operation. Additionally, moved the function to the regulatory.c file for better organization. optimize local variable usage in it. Signed-off-by: Anjaneyulu Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.b7ac9d05234e.Ieb623d7e8dca6bb6a5733682b31e4ff1e39373f0@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/regulatory.c | 31 +++++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 34 +------------------ 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index ea435ee94312..63320689e05d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -674,3 +674,34 @@ bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc) } } IWL_EXPORT_SYMBOL(iwl_puncturing_is_allowed_in_bios); + +bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt) +{ + /* default behaviour is disabled */ + u32 value = 0; + int ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_RFI_CONFIG, &value); + + if (ret < 0) { + IWL_DEBUG_RADIO(fwrt, "Failed to get DSM RFI, ret=%d\n", ret); + return false; + } + + value &= DSM_VALUE_RFI_DISABLE; + /* RFI BIOS CONFIG value can be 0 or 3 only. + * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled. + * 1 and 2 are invalid BIOS configurations, So, it's not possible to + * disable ddr/dlvr separately. + */ + if (!value) { + IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to enable\n"); + return true; + } else if (value == DSM_VALUE_RFI_DISABLE) { + IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to disable\n"); + } else { + IWL_DEBUG_RADIO(fwrt, + "DSM RFI got invalid value, value=%d\n", value); + } + + return false; +} +IWL_EXPORT_SYMBOL(iwl_rfi_is_enabled_in_bios); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index b355d7bef14c..ed61bc35ef5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -167,6 +167,8 @@ enum iwl_dsm_values_rfi { #define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ DSM_VALUE_RFI_DDR_DISABLE) +bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt); + enum iwl_dsm_masks_reg { DSM_MASK_CHINA_22_REG = BIT(2) }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d10877856049..82bde419a2f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1213,38 +1213,6 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } -static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) -{ - u32 value = 0; - /* default behaviour is disabled */ - bool bios_enable_rfi = false; - int ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value); - - - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret); - return bios_enable_rfi; - } - - value &= DSM_VALUE_RFI_DISABLE; - /* RFI BIOS CONFIG value can be 0 or 3 only. - * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled. - * 1 and 2 are invalid BIOS configurations, So, it's not possible to - * disable ddr/dlvr separately. - */ - if (!value) { - IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n"); - bios_enable_rfi = true; - } else if (value == DSM_VALUE_RFI_DISABLE) { - IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to disable\n"); - } else { - IWL_DEBUG_RADIO(mvm, - "DSM RFI got invalid value, value=%d\n", value); - } - - return bios_enable_rfi; -} - static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { struct iwl_lari_config_change_cmd cmd; @@ -1631,7 +1599,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_mvm_uats_init(mvm); if (iwl_rfi_supported(mvm)) { - if (iwl_mvm_eval_dsm_rfi(mvm)) + if (iwl_rfi_is_enabled_in_bios(&mvm->fwrt)) iwl_rfi_send_config_cmd(mvm, NULL); } From 243a269758c5e4ce608947760ee55fbd374934f6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 150/465] wifi: iwlwifi: Unify TAS block list handling in regulatory.c JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3b67a2c5aa0fea981569426000b0adbe6271703e Author: Anjaneyulu Date: Wed Feb 5 14:55:34 2025 +0200 wifi: iwlwifi: Unify TAS block list handling in regulatory.c Created a common function iwl_add_mcc_to_tas_block_list() to handle the operations previously performed by iwl_mld_add_to_tas_block_list() and iwl_mvm_add_to_tas_block_list(). moved this new function to regulatory.c to better reflect its purpose and improve code organization. Signed-off-by: Anjaneyulu Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.157d26fb7f02.I87e20e967835bc895be390daf1c6637e20b52aae@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/regulatory.c | 18 +++++++++++++++++ .../wireless/intel/iwlwifi/fw/regulatory.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 20 ++----------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 63320689e05d..11f54339acc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -456,6 +456,24 @@ iwl_parse_tas_selection(const u32 tas_selection_in, const u8 tbl_rev) } IWL_EXPORT_SYMBOL(iwl_parse_tas_selection); +bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) +{ + for (int i = 0; i < *size; i++) { + if (list[i] == mcc) + return true; + } + + /* Verify that there is room for another country + * If *size == IWL_WTAS_BLACK_LIST_MAX, then the table is full. + */ + if (*size >= IWL_WTAS_BLACK_LIST_MAX) + return false; + + list[*size++] = mcc; + return true; +} +IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); + static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index ed61bc35ef5b..d978a4fadfae 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -192,6 +192,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); bool iwl_is_tas_approved(void); +bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc); struct iwl_tas_selection_data iwl_parse_tas_selection(const u32 tas_selection, const u8 tbl_rev); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 82bde419a2f5..2b5a62604fc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1094,22 +1094,6 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) return iwl_mvm_ppag_send_cmd(mvm); } -static bool -iwl_mvm_add_to_tas_block_list(u16 *list, u8 *size, u16 mcc) -{ - /* Verify that there is room for another country */ - if (*size >= IWL_WTAS_BLACK_LIST_MAX) - return false; - - for (u8 i = 0; i < *size; i++) { - if (list[i] == mcc) - return true; - } - - list[*size++] = mcc; - return true; -} - static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); @@ -1150,10 +1134,10 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); - if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array, + if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array, &data.block_list_size, IWL_MCC_US)) || - (!iwl_mvm_add_to_tas_block_list(data.block_list_array, + (!iwl_add_mcc_to_tas_block_list(data.block_list_array, &data.block_list_size, IWL_MCC_CANADA))) { IWL_DEBUG_RADIO(mvm, From 0ac12c0d4ee30daca2f109fc18119f22eeb36ce1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 151/465] wifi: iwlwifi: don't warn during reprobe JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 696cca64308dc641d0bbe4aa2c09dd9752aa288d Author: Miri Korenblit Date: Wed Feb 5 14:55:35 2025 +0200 wifi: iwlwifi: don't warn during reprobe During reprobe, the sw state is being destroyd, and so is the connection. When the peer STA is being removed, the opmode sends a command to flush the TXQs of the STA and uses iwl_trans_wait_txq_empty. This one warns if the FW is not alive, but it really shouldn't if there is a FW error - and return silently instead, just like we do when sending a hcmd. Signed-off-by: Miri Korenblit Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20250205145347.76425b10e5a0.I3bf0de2eb090a8b94c4e36d93dd91df61fadb808@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 47854a36413e..f700353274b5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -673,6 +673,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_txq_enable_cfg); int iwl_trans_wait_txq_empty(struct iwl_trans *trans, int queue) { + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "bad state = %d\n", trans->state)) return -EIO; From 793391b7de3d615c6741c885c018acb9e364637b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 152/465] wifi: iwlwifi: enable 320 MHz on slow PCIe links JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8a065234e877f9ffa51112539aff7bdf179a40c4 Author: Johannes Berg Date: Wed Feb 5 14:55:36 2025 +0200 wifi: iwlwifi: enable 320 MHz on slow PCIe links Despite not being able to sustain the full 320 MHz throughput even at MCS 9, enable 320 MHz on slow PCIe links. This may in some cases result in frames being dropped (on the air) by the firmware if they cannot be delivered to the host, but it can still be better to use 320 MHz. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.1e5356a3b124.I1224023721aaeff8ebcaa47dff88613c7fd0533a@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 9f7e013252fe..76259ac261cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -913,11 +913,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, { bool is_ap = iftype_data->types_mask & (BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO)); - bool no_320; - - no_320 = (!trans->trans_cfg->integrated && - trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB) || - trans->reduced_cap_sku; + bool slow_pcie = (!trans->trans_cfg->integrated && + trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB); if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) iftype_data->eht_cap.has_eht = false; @@ -944,7 +941,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: - if (!no_320) { + if (!trans->reduced_cap_sku) { iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |= @@ -986,6 +983,14 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; } } + + if (slow_pcie) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss = + &iftype_data->eht_cap.eht_mcs_nss_supp; + + mcs_nss->bw._320.rx_tx_mcs11_max_nss = 0; + mcs_nss->bw._320.rx_tx_mcs13_max_nss = 0; + } } else { struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp = &iftype_data->he_cap.he_mcs_nss_supp; From bbe0ab2a8064a3bd316c9a7b126e68e480fbc9aa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 153/465] wifi: iwlwifi: cfg: separate 22000/BZ family HT params JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4cb46c1c732dd40220aa7c938c9647a6041e0a29 Author: Johannes Berg Date: Wed Feb 5 14:55:37 2025 +0200 wifi: iwlwifi: cfg: separate 22000/BZ family HT params We're adding a new IWLMLD opmode for just BZ and later devices. If that's enabled but IWLMVM isn't, the build fails because 22000 family configs aren't built but BZ and later refer to it. Rather than trying to make some new file to build it in all cases, just copy the small struct. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.1d6186c23bee.I3c61a6c9e0db3ba6eea4dac63e1547945ad01703@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 9 ++++++++- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 074b2c9ea003..64a5bbb45c83 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -50,6 +50,13 @@ static const struct iwl_base_params iwl_bz_base_params = { .pcie_l1_allowed = true, }; +const struct iwl_ht_params iwl_bz_ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ) | + BIT(NL80211_BAND_6GHZ), +}; + #define IWL_DEVICE_BZ_COMMON \ .ucode_api_max = IWL_BZ_UCODE_API_MAX, \ .ucode_api_min = IWL_BZ_UCODE_API_MIN, \ @@ -113,7 +120,7 @@ static const struct iwl_base_params iwl_bz_base_params = { #define IWL_DEVICE_BZ \ IWL_DEVICE_BZ_COMMON, \ - .ht_params = &iwl_22000_ht_params + .ht_params = &iwl_bz_ht_params /* * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index ab7c0f8d54f4..69176b59b4de 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -114,7 +114,7 @@ static const struct iwl_base_params iwl_dr_base_params = { .uhb_supported = true, \ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ .num_rbds = IWL_NUM_RBDS_DR_EHT, \ - .ht_params = &iwl_22000_ht_params + .ht_params = &iwl_bz_ht_params /* * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index c9eeb3f6704e..eee47af728d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -121,7 +121,7 @@ static const struct iwl_base_params iwl_sc_base_params = { .uhb_supported = true, \ .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ .num_rbds = IWL_NUM_RBDS_SC_EHT, \ - .ht_params = &iwl_22000_ht_params + .ht_params = &iwl_bz_ht_params /* * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 91275680dd2c..c2caaf875c11 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -660,6 +660,8 @@ extern const struct iwl_cfg iwl_cfg_ma; extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0; extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; +extern const struct iwl_ht_params iwl_bz_ht_params; + extern const struct iwl_cfg iwl_cfg_bz; extern const struct iwl_cfg iwl_cfg_gl; From 520f24405208fd91fe858177b454602e7ffa9a02 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:38 +0200 Subject: [PATCH 154/465] wifi: iwlwifi: Indicate support for EPCS JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bdfc32abd5d640198a00aceb1d497223b85bca7e Author: Ilan Peer Date: Wed Feb 5 14:55:38 2025 +0200 wifi: iwlwifi: Indicate support for EPCS Indicate support for EPCS and unsolicited EPCS in the EHT MAC capabilities. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.6b1c7cc8a958.Idd72ea53f70eb452d43d99e6c45ff21f891100bd@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 76259ac261cb..c381511e9ec6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -684,10 +684,13 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 | IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC, + .mac_cap_info[1] = + IEEE80211_EHT_MAC_CAP1_UNSOL_EPCS_PRIO_ACCESS, .phy_cap_info[0] = IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | From 1c09469b2859d0a08c033d077481a60d5c653e62 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 155/465] wifi: iwlwifi: mvm: Indicate support link reconfiguration JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 926ad5f970d6b6573ba508da69e91cc6dba3611f Author: Ilan Peer Date: Wed Feb 5 14:55:39 2025 +0200 wifi: iwlwifi: mvm: Indicate support link reconfiguration As MLO link configuration is supported by mac80211, indicate support for MLO link reconfiguration in station mode. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit ---------------- depends on "wifi: ieee80211: Add some missing MLO related definitions" Link: https://patch.msgid.link/20250205145347.92d19705d2b9.Id07fa3ebad6bc23ecf6e91868f67150ce70f47b0@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index af6644b7e95f..014777c9cc5d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -272,9 +272,10 @@ static const u8 tm_if_types_ext_capa_sta[] = { __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) -#define IWL_MVM_MLD_CAPA_OPS FIELD_PREP_CONST( \ +#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ - IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ + IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT) static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = { { From fc8f5d896e9372b10a831f10e83c26b323d844d5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 156/465] wifi: iwlwifi: fw: make iwl_send_dbg_dump_complete_cmd() static JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c9afb4cf6d2fe1b34e95b1d1db695d6054a7dc35 Author: Johannes Berg Date: Wed Feb 5 14:55:40 2025 +0200 wifi: iwlwifi: fw: make iwl_send_dbg_dump_complete_cmd() static It's only used in the same file, so can be static. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.319b66c00676.I3c06d6c2ee5850a5a89feff7d770e557fd625a6d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 5 ++--- drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 6594216f873c..bb3114c03eed 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -3072,9 +3072,8 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) } IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); -void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, - u32 timepoint, - u32 timepoint_data) +static void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, + u32 timepoint, u32 timepoint_data) { struct iwl_dbg_dump_complete_cmd hcmd_data; struct iwl_host_cmd hcmd = { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 87998374f459..35e30e5db462 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -324,9 +324,6 @@ static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, } void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt); -void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, - u32 timepoint, - u32 timepoint_data); bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id); void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt); From 62857885b72e9d19e66ecab31e51c2a1a65ba26b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 157/465] wifi: iwlwifi: be less aggressive with re-probe JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1bbc086f49b436ec6773ce48864c80cd682888e8 Author: Emmanuel Grumbach Date: Wed Feb 5 14:55:41 2025 +0200 wifi: iwlwifi: be less aggressive with re-probe Re-probing if we had 2 firmware crash within 3 minutes is really too aggressive. Drastically reduce the threshold to 7 seconds. After 7 seconds, a new firmware crash will be considered "new" and not cause a PCI re-probe. This allows to pass tests that cause a firmware crash every 10 seconds and expect to see no impact on the traffic. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.38f912b047f4.I03f0c10ae9e7ecea639431f3e089b757cc8a4347@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index f700353274b5..2d0e09aa9cbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -97,7 +97,7 @@ static void iwl_trans_reprobe_wk(struct work_struct *wk) module_put(THIS_MODULE); } -#define IWL_TRANS_RESET_OK_TIME 180 /* seconds */ +#define IWL_TRANS_RESET_OK_TIME 7 /* seconds */ static enum iwl_reset_mode iwl_trans_determine_restart_mode(struct iwl_trans *trans) From b7ff6f624db57e84450925dfd9e15458475ffc6e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 158/465] wifi: iwlwifi: make no_160 more generic JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 75a3313f52b7e08e7e73746f69a68c2b7c28bb2b Author: Emmanuel Grumbach Date: Wed Feb 5 14:55:42 2025 +0200 wifi: iwlwifi: make no_160 more generic We'll have devices that are EHT capable but don't support 320 MHz and those devices look like the 320 MHz capable devices, but have distinct subsystem ID. We already had the same type of differentiation for HE devices that support 160 MHz or not. Enhance that mechanism and now the _IWL_DEV_INFO macro gets an indication whether the bandwidth should be limited for that specific device. The subsystem ID gives a binary answer about the bandwidth limitation and iwl_pci_find_dev_info() compares this to the list of _IWL_DEV_INFO entries. Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.1ba406c538a5.I6e24123f60a764aedfeaaac8768c26e136c320cf@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-config.h | 15 +- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 +- .../net/wireless/intel/iwlwifi/iwl-trans.h | 7 +- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 194 +++++++++--------- .../wireless/intel/iwlwifi/tests/devinfo.c | 15 +- 5 files changed, 123 insertions(+), 112 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index c2caaf875c11..c2cdb5026460 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -451,11 +451,8 @@ struct iwl_cfg { #define IWL_CFG_RF_ID_HR 0x7 #define IWL_CFG_RF_ID_HR1 0x4 -#define IWL_CFG_NO_160 0x1 -#define IWL_CFG_160 0x0 - -#define IWL_CFG_NO_320 0x1 -#define IWL_CFG_320 0x0 +#define IWL_CFG_BW_NO_LIM (U16_MAX - 1) +#define IWL_CFG_BW_ANY U16_MAX #define IWL_CFG_CORES_BT 0x0 #define IWL_CFG_CORES_BT_GNSS 0x5 @@ -467,7 +464,7 @@ struct iwl_cfg { #define IWL_CFG_IS_JACKET 0x1 #define IWL_SUBDEVICE_RF_ID(subdevice) ((u16)((subdevice) & 0x00F0) >> 4) -#define IWL_SUBDEVICE_NO_160(subdevice) ((u16)((subdevice) & 0x0200) >> 9) +#define IWL_SUBDEVICE_BW_LIM(subdevice) ((u16)((subdevice) & 0x0200) >> 9) #define IWL_SUBDEVICE_CORES(subdevice) ((u16)((subdevice) & 0x1C00) >> 10) struct iwl_dev_info { @@ -475,10 +472,10 @@ struct iwl_dev_info { u16 subdevice; u16 mac_type; u16 rf_type; + u16 bw_limit; u8 mac_step; u8 rf_step; u8 rf_id; - u8 no_160; u8 cores; u8 cdb; u8 jacket; @@ -492,7 +489,7 @@ extern const unsigned int iwl_dev_info_table_size; const struct iwl_dev_info * iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step); + u8 jacket, u8 rf_id, u8 bw_limit, u8 cores, u8 rf_step); extern const struct pci_device_id iwl_hw_card_ids[]; #endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index c381511e9ec6..08269168b2fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1094,7 +1094,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs13_max_nss = 0; } - if (trans->no_160) + if (trans->bw_limit < 160) iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index f6234065dbdd..3561ff8483f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -876,7 +876,7 @@ struct iwl_txq { * only valid for discrete (not integrated) NICs * @invalid_tx_cmd: invalid TX command buffer * @reduced_cap_sku: reduced capability supported SKU - * @no_160: device not supporting 160 MHz + * @bw_limit: the max bandwidth * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz * @restart: restart worker data * @restart.wk: restart worker @@ -910,7 +910,8 @@ struct iwl_trans { char hw_id_str[52]; u32 sku_id[3]; bool reduced_cap_sku; - u8 no_160:1, step_urm:1; + u16 bw_limit; + bool step_urm; u8 dsbr_urm_fw_dependent:1, dsbr_urm_permanent:1; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index e0b657b2f74b..346a5c178dc2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -551,16 +551,17 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_hw_card_ids); #define _IWL_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \ - _rf_id, _rf_step, _no_160, _cores, _cdb, _cfg, _name) \ + _rf_id, _rf_step, _bw_limit, _cores, _cdb, _cfg, _name) \ { .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg), \ .name = _name, .mac_type = _mac_type, .rf_type = _rf_type, .rf_step = _rf_step, \ - .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id, \ + .bw_limit = _bw_limit, .cores = _cores, .rf_id = _rf_id, \ .mac_step = _mac_step, .cdb = _cdb, .jacket = IWL_CFG_ANY } #define IWL_DEV_INFO(_device, _subdevice, _cfg, _name) \ _IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_ANY, _cfg, _name) + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, \ + _cfg, _name) VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { #if IS_ENABLED(CONFIG_IWLMVM) @@ -723,66 +724,66 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9560_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9270_160_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9270_name), _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9162_160_name), _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9162_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9260_160_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9260_name), /* Qu with Jf */ @@ -790,132 +791,132 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550i_name), /* Qu C step */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550i_name), /* QuZ */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550i_name), /* Qu with Hr */ @@ -923,202 +924,202 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_b0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_b0_hr_b0, iwl_ax203_name), /* Qu C step */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr_b0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr_b0, iwl_ax201_name), /* QuZ */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_quz_a0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_quz_a0_hr_b0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_quz_a0_hr_b0, iwl_ax201_name), /* Ma */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_ma, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_ma, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_ma, iwl_ax231_name), /* So with Hr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax201_name), /* So-F with Hr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax201_name), /* So-F with Gf */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_CDB, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), /* SoF with JF2 */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), /* SoF with JF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), /* So with GF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_CDB, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), /* So with JF2 */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), /* So with JF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), /* Bz */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, @@ -1130,73 +1131,73 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_wh_name), /* Ga (Gl) */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_gl, iwl_gl_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, + 160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_gl, iwl_mtp_name), /* Sc */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_sc_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_sc2_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_sc2f_name), /* Dr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_dr, iwl_dr_name), /* Br */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BR, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_br, iwl_br_name), #endif /* CONFIG_IWLMVM */ }; @@ -1348,7 +1349,7 @@ out: VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info * iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step) + u8 jacket, u8 rf_id, u8 bw_limit, u8 cores, u8 rf_step) { int num_devices = ARRAY_SIZE(iwl_dev_info_table); int i; @@ -1391,8 +1392,15 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device, dev_info->rf_id != rf_id) continue; - if (dev_info->no_160 != (u8)IWL_CFG_ANY && - dev_info->no_160 != no_160) + /* + * Check that bw_limit have the same "boolean" value since + * IWL_SUBDEVICE_BW_LIM can only return a boolean value and + * dev_info->bw_limit encodes a non-boolean value. + * dev_info->bw_limit == IWL_CFG_BW_NO_LIM must be equal to + * !bw_limit to have a match. + */ + if (dev_info->bw_limit != IWL_CFG_BW_ANY && + (dev_info->bw_limit == IWL_CFG_BW_NO_LIM) == !!bw_limit) continue; if (dev_info->cores != (u8)IWL_CFG_ANY && @@ -1530,13 +1538,13 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) CSR_HW_RFID_IS_CDB(iwl_trans->hw_rf_id), CSR_HW_RFID_IS_JACKET(iwl_trans->hw_rf_id), IWL_SUBDEVICE_RF_ID(pdev->subsystem_device), - IWL_SUBDEVICE_NO_160(pdev->subsystem_device), + IWL_SUBDEVICE_BW_LIM(pdev->subsystem_device), IWL_SUBDEVICE_CORES(pdev->subsystem_device), CSR_HW_RFID_STEP(iwl_trans->hw_rf_id)); if (dev_info) { iwl_trans->cfg = dev_info->cfg; iwl_trans->name = dev_info->name; - iwl_trans->no_160 = dev_info->no_160 == IWL_CFG_NO_160; + iwl_trans->bw_limit = dev_info->bw_limit; } #if IS_ENABLED(CONFIG_IWLMVM) diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c index 7361b6d0cdb8..59e33fbd8361 100644 --- a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c +++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c @@ -2,7 +2,7 @@ /* * KUnit tests for the iwlwifi device info table * - * Copyright (C) 2023-2024 Intel Corporation + * Copyright (C) 2023-2025 Intel Corporation */ #include #include @@ -13,9 +13,9 @@ MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di) { - printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,no_160=%d,cores=%.2x\n", + printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,bw_limit=%d,cores=%.2x\n", pfx, di->device, di->subdevice, di->mac_type, di->mac_step, - di->rf_type, di->cdb, di->jacket, di->rf_id, di->no_160, + di->rf_type, di->cdb, di->jacket, di->rf_id, di->bw_limit, di->cores); } @@ -31,8 +31,13 @@ static void devinfo_table_order(struct kunit *test) di->mac_type, di->mac_step, di->rf_type, di->cdb, di->jacket, di->rf_id, - di->no_160, di->cores, di->rf_step); - if (ret != di) { + di->bw_limit != IWL_CFG_BW_NO_LIM, + di->cores, di->rf_step); + if (!ret) { + iwl_pci_print_dev_info("No entry found for: ", di); + KUNIT_FAIL(test, + "No entry found for entry at index %d\n", idx); + } else if (ret != di) { iwl_pci_print_dev_info("searched: ", di); iwl_pci_print_dev_info("found: ", ret); KUNIT_FAIL(test, From 14197f66772c55a435460179ab00342fc0983153 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 159/465] wifi: iwlwifi: properly set the names for SC devices JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 66672fa681b3f21d90300a723a22cc2a0d5d3446 Author: Emmanuel Grumbach Date: Wed Feb 5 14:55:43 2025 +0200 wifi: iwlwifi: properly set the names for SC devices Sc devices can come with several CRFs. Use the CRF to determine the name of the device. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.5bf5d931204e.I5eb435db1b8df46687c43ebae6488c0c4430d530@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 6 --- .../net/wireless/intel/iwlwifi/iwl-config.h | 3 -- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 43 ++++++++++++++++--- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index eee47af728d7..eb56af9a8411 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -142,22 +142,16 @@ const struct iwl_cfg_trans_params iwl_sc_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const char iwl_sc_name[] = "Intel(R) TBD Sc device"; - const struct iwl_cfg iwl_cfg_sc = { .fw_name_mac = "sc", IWL_DEVICE_SC, }; -const char iwl_sc2_name[] = "Intel(R) TBD Sc2 device"; - const struct iwl_cfg iwl_cfg_sc2 = { .fw_name_mac = "sc2", IWL_DEVICE_SC, }; -const char iwl_sc2f_name[] = "Intel(R) TBD Sc2f device"; - const struct iwl_cfg iwl_cfg_sc2f = { .fw_name_mac = "sc2f", IWL_DEVICE_SC, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index c2cdb5026460..5fc9ce2b350e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -552,9 +552,6 @@ extern const char iwl_fm_name[]; extern const char iwl_wh_name[]; extern const char iwl_gl_name[]; extern const char iwl_mtp_name[]; -extern const char iwl_sc_name[]; -extern const char iwl_sc2_name[]; -extern const char iwl_sc2f_name[]; extern const char iwl_dr_name[]; extern const char iwl_br_name[]; #if IS_ENABLED(CONFIG_IWLDVM) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 346a5c178dc2..cd347cd8176a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1173,19 +1173,50 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { /* Sc */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc, iwl_sc_name), + iwl_cfg_sc, iwl_ax211_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc, iwl_fm_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2, iwl_sc2_name), + iwl_cfg_sc2, iwl_ax211_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2, iwl_fm_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2f, iwl_sc2f_name), + iwl_cfg_sc2f, iwl_ax211_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2f, iwl_fm_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2f, iwl_wh_name), + /* Dr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY, From 9251bf6eb0c26fd4e51821bf9c07239c8c208962 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 160/465] wifi: iwlwifi: clarify the meaning of IWL_INIT_PHY JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9e8c760471243a10e78090f38198d846c41140a3 Author: Emmanuel Grumbach Date: Wed Feb 5 14:55:44 2025 +0200 wifi: iwlwifi: clarify the meaning of IWL_INIT_PHY This is a bit that tells the firmware to wait for the PHY_CONFIGURATION_CMD before completing its init sequence. Clarify this in the comment. Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.097510347ae2.Ica00b4b30163a21bf993fa968dd406ee4023fc9e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/alive.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h index ebe85fdf08d3..42360a8f23aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020-2021, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2021, 2024-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -117,7 +117,7 @@ struct iwl_alive_ntf_v6 { * finishing init flow * @IWL_INIT_DEBUG_CFG: driver is going to send debug config command * @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands - * @IWL_INIT_PHY: driver is going to send PHY_DB commands + * @IWL_INIT_PHY: driver is going to send the PHY_CONFIGURATION_CMD */ enum iwl_extended_cfg_flags { IWL_INIT_DEBUG_CFG, From ce04e5c2199f00b74dcd802ec285431cade27472 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 161/465] wifi: iwlwifi: support ROC version 6 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit af3be9088404ef8007e895ecaff5d173a227ea61 Author: Shaul Triebitz Date: Wed Feb 5 14:55:45 2025 +0200 wifi: iwlwifi: support ROC version 6 Version 6 added ROC with multi repetitions. We don't use it, but need to update the command length. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.956c33729d48.I609835c08f0003c084a13a1e1e505cb7bc8ecbc6@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../intel/iwlwifi/fw/api/time-event.h | 42 +++- drivers/net/wireless/intel/iwlwifi/mld/roc.c | 224 ++++++++++++++++++ .../wireless/intel/iwlwifi/mvm/time-event.c | 6 +- 3 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/roc.c diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h index 18d030334a6a..f586379d66dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2020, 2022-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2020, 2022-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -351,7 +351,7 @@ enum iwl_roc_activity { }; /* ROC_ACTIVITY_API_E_VER_1 */ /* - * ROC command + * ROC command v5 * * Command requests the firmware to remain on a channel for a certain duration. * @@ -366,7 +366,7 @@ enum iwl_roc_activity { * @max_delay: max delay the ROC can start in TU * @duration: remain on channel duration in TU */ -struct iwl_roc_req { +struct iwl_roc_req_v5 { __le32 action; __le32 activity; __le32 sta_id; @@ -375,7 +375,41 @@ struct iwl_roc_req { __le16 reserved; __le32 max_delay; __le32 duration; -} __packed; /* ROC_CMD_API_S_VER_3 */ +} __packed; /* ROC_CMD_API_S_VER_5 */ + +/* + * ROC command + * + * Command requests the firmware to remain on a channel for a certain duration. + * + * ( MAC_CONF_GROUP 0x3, ROC_CMD 0xE ) + * + * @action: action to perform, see &enum iwl_ctxt_action + * @activity: type of activity, see &enum iwl_roc_activity + * @sta_id: station id, resumed during "Remain On Channel" activity. + * @channel_info: &struct iwl_fw_channel_info + * @node_addr: node MAC address for Rx filtering + * @reserved1: align to a dword + * @max_delay: max delay the ROC can start in TU + * @duration: remain on channel duration in TU + * @interval: interval between repetitions (when repetitions > 1). + * @repetitions: number of repetitions + * 0xFF: infinite repetitions. 0 or 1: single repetition. + * @reserved2: align to a dword + */ +struct iwl_roc_req { + __le32 action; + __le32 activity; + __le32 sta_id; + struct iwl_fw_channel_info channel_info; + u8 node_addr[ETH_ALEN]; + __le16 reserved1; + __le32 max_delay; + __le32 duration; + __le32 interval; + u8 repetitions; + u8 reserved2[3]; +} __packed; /* ROC_CMD_API_S_VER_6 */ /* * ROC notification diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c new file mode 100644 index 000000000000..b87faca23ceb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 - 2025 Intel Corporation + */ +#include +#include + +#include "mld.h" +#include "roc.h" +#include "hcmd.h" +#include "iface.h" +#include "sta.h" +#include "mlo.h" + +#include "fw/api/context.h" +#include "fw/api/time-event.h" + +#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200) + +static void +iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int *result = data; + int ret; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_ROC, + iwl_mld_get_primary_link(vif)); + if (ret) + *result = ret; +} + +int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *channel, int duration, + enum ieee80211_roc_type type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_int_sta *aux_sta; + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + enum iwl_roc_activity activity; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_roc, + &ret); + if (ret) + return ret; + + /* TODO: task=Hotspot 2.0 */ + if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { + IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", + vif->type); + + return -EOPNOTSUPP; + } + + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + activity = ROC_ACTIVITY_P2P_DISC; + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + activity = ROC_ACTIVITY_P2P_NEG; + break; + default: + WARN_ONCE(1, "Got an invalid P2P ROC type\n"); + return -EINVAL; + } + + if (WARN_ON(mld_vif->roc_activity != ROC_NUM_ACTIVITIES)) + return -EBUSY; + + /* No MLO on P2P device */ + aux_sta = &mld_vif->deflink.aux_sta; + + ret = iwl_mld_add_aux_sta(mld, aux_sta); + if (ret) + return ret; + + cmd.activity = cpu_to_le32(activity); + cmd.sta_id = cpu_to_le32(aux_sta->sta_id); + cmd.channel_info.channel = cpu_to_le32(channel->hw_value); + cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); + cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; + /* TODO: task=Hotspot 2.0, revisit those parameters when we add an ROC + * on the BSS vif + */ + cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); + cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); + + memcpy(cmd.node_addr, vif->addr, ETH_ALEN); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) { + IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); + return ret; + } + mld_vif->roc_activity = activity; + + return 0; +} + +static void +iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC); +} + +static void iwl_mld_destroy_roc(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_vif *mld_vif) +{ + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_unblock_roc, + NULL); + + /* wait until every tx has seen that roc_activity has been reset */ + synchronize_net(); + /* from here, no new tx will be added + * we can flush the Tx on the queues + */ + + iwl_mld_flush_link_sta_txqs(mld, mld_vif->deflink.aux_sta.sta_id); + + iwl_mld_remove_aux_sta(mld, vif, &vif->bss_conf); +} + +int iwl_mld_cancel_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* TODO: task=Hotspot 2.0 */ + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + return -EOPNOTSUPP; + + /* No roc activity running it's probably already done */ + if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) + return 0; + + cmd.activity = cpu_to_le32(mld_vif->roc_activity); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) + IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n"); + + /* We may have raced with the firmware expiring the ROC instance at + * this very moment. In that case, we can have a notification in the + * async processing queue. However, none can arrive _after_ this as + * ROC_CMD was sent synchronously, i.e. we waited for a response and + * the firmware cannot refer to this ROC after the response. Thus, + * if we just cancel the notification (if there's one) we'll be at a + * clean state for any possible next ROC. + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC, + mld_vif->roc_activity); + + iwl_mld_destroy_roc(mld, vif, mld_vif); + + return 0; +} + +void iwl_mld_handle_roc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_roc_notif *notif = (void *)pkt->data; + u32 activity = le32_to_cpu(notif->activity); + /* TODO: task=Hotspot 2.0 - roc can run on BSS */ + struct ieee80211_vif *vif = mld->p2p_device_vif; + struct iwl_mld_vif *mld_vif; + + if (WARN_ON(!vif)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + /* It is possible that the ROC was canceled + * but the notification was already fired. + */ + if (mld_vif->roc_activity != activity) + return; + + if (le32_to_cpu(notif->success) && + le32_to_cpu(notif->started)) { + /* We had a successful start */ + ieee80211_ready_on_channel(mld->hw); + } else { + /* ROC was not successful, tell the firmware to remove it */ + if (le32_to_cpu(notif->started)) + iwl_mld_cancel_roc(mld->hw, vif); + else + iwl_mld_destroy_roc(mld, vif, mld_vif); + /* we need to let know mac80211 about end OR + * an unsuccessful start + */ + ieee80211_remain_on_channel_expired(mld->hw); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index ebfa88b38b71..1a30bb1ff8ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH */ @@ -771,7 +771,7 @@ static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity) { - struct iwl_roc_req roc_cmd = { + struct iwl_roc_req_v5 roc_cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), .activity = cpu_to_le32(activity), }; @@ -1102,7 +1102,7 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, { int res; u32 duration_tu, delay; - struct iwl_roc_req roc_req = { + struct iwl_roc_req_v5 roc_req = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), .activity = cpu_to_le32(activity), .sta_id = cpu_to_le32(mvm->aux_sta.sta_id), From 9b28a78a3911bd56b64f494bec0a77c089969e99 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 162/465] wifi: iwlwifi: use correct IMR dump variable JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 21e4d29ac0def546d57bacebe4a51cbed1209b03 Author: Johannes Berg Date: Wed Feb 5 14:55:46 2025 +0200 wifi: iwlwifi: use correct IMR dump variable We shouldn't dump the reg_data here which dumps the last entry again, it should use the imr_reg_data. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.3313b18667d1.Iaa9ab66b1d397912a573525e060d39ea01b29d19@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index bb3114c03eed..c0b332aa260f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -2691,7 +2691,7 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, } /* collect DRAM_IMR region in the last */ if (imr_reg_data.reg_tlv) - size += iwl_dump_ini_mem(fwrt, list, ®_data, + size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data, &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); if (size) { From eccf4a625e296070ce9b9b1ced5d4c403f939fe1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:39 +0200 Subject: [PATCH 163/465] wifi: iwlwifi: add twt operation cmd JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 11ccf9a76fa7764f33f884b746d17589e78a2a8e Author: Shaul Triebitz Date: Wed Feb 5 14:55:47 2025 +0200 wifi: iwlwifi: add twt operation cmd Add the firmware API. Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.50a9f7bebe4c.I15ac1361fdab547dbf680a6fa6e88fdc5b177082@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) 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 232e20482e2d..511e86ea11d4 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-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -61,6 +61,10 @@ enum iwl_mac_conf_subcmd_ids { * @ROC_CMD: &struct iwl_roc_req */ ROC_CMD = 0xE, + /** + * @TWT_OPERATION_CMD: &struct iwl_twt_operation_cmd + */ + TWT_OPERATION_CMD = 0x10, /** * @MISSED_BEACONS_NOTIF: &struct iwl_missed_beacons_notif */ @@ -748,4 +752,83 @@ struct iwl_esr_trans_fail_notif { __le32 err_code; } __packed; /* ESR_TRANSITION_FAILED_NTFY_API_S_VER_1 */ +/* + * enum iwl_twt_operation_type: TWT operation in a TWT action frame + * + * @TWT_OPERATION_REQUEST: TWT Request + * @TWT_OPERATION_SUGGEST: TWT Suggest + * @TWT_OPERATION_DEMAND: TWT Demand + * @TWT_OPERATION_GROUPING: TWT Grouping + * @TWT_OPERATION_ACCEPT: TWT Accept + * @TWT_OPERATION_ALTERNATE: TWT Alternate + * @TWT_OPERATION_DICTATE: TWT Dictate + * @TWT_OPERATION_REJECT: TWT Reject + * @TWT_OPERATION_TEARDOWN: TWT Teardown + * @TWT_OPERATION_UNAVAILABILITY: TWT Unavailability + */ +enum iwl_twt_operation_type { + TWT_OPERATION_REQUEST, + TWT_OPERATION_SUGGEST, + TWT_OPERATION_DEMAND, + TWT_OPERATION_GROUPING, + TWT_OPERATION_ACCEPT, + TWT_OPERATION_ALTERNATE, + TWT_OPERATION_DICTATE, + TWT_OPERATION_REJECT, + TWT_OPERATION_TEARDOWN, + TWT_OPERATION_UNAVAILABILITY, + TWT_OPERATION_MAX, +}; /* TWT_OPERATION_TYPE_E_VER_1 */ + +/** + * struct iwl_twt_operation_cmd - initiate a TWT session from driver + * + * @link_id: FW link id to initiate the TWT + * @twt_operation: &enum iwl_twt_operation_type + * @target_wake_time: TSF time to start the TWT + * @interval_exponent: the exponent for the interval + * @interval_mantissa: the mantissa for the interval + * @minimum_wake_duration: the minimum duration for the wake period + * @trigger: is the TWT triggered or not + * @flow_type: is the TWT announced (0) or not (1) + * @flow_id: the TWT flow identifier 0 - 7 + * @twt_protection: is the TWT protected + * @ndp_paging_indicator: is ndp paging indicator set + * @responder_pm_mode: is responder pm mode set + * @negotiation_type: if the responder wants to doze outside the TWT SP + * @twt_request: 1 for TWT request (STA), 0 for TWT response (AP) + * @implicit: is TWT implicit + * @twt_group_assignment: the TWT group assignment + * @twt_channel: the TWT channel + * @restricted_info_present: is this a restricted TWT + * @dl_bitmap_valid: is DL (download) bitmap valid (restricted TWT) + * @ul_bitmap_valid: is UL (upload) bitmap valid (restricted TWT) + * @dl_tid_bitmap: DL TID bitmap (restricted TWT) + * @ul_tid_bitmap: UL TID bitmap (restricted TWT) + */ +struct iwl_twt_operation_cmd { + __le32 link_id; + __le32 twt_operation; + __le64 target_wake_time; + __le32 interval_exponent; + __le32 interval_mantissa; + __le32 minimum_wake_duration; + u8 trigger; + u8 flow_type; + u8 flow_id; + u8 twt_protection; + u8 ndp_paging_indicator; + u8 responder_pm_mode; + u8 negotiation_type; + u8 twt_request; + u8 implicit; + u8 twt_group_assignment; + u8 twt_channel; + u8 restricted_info_present; + u8 dl_bitmap_valid; + u8 ul_bitmap_valid; + u8 dl_tid_bitmap; + u8 ul_tid_bitmap; +} __packed; /* TWT_OPERATION_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ From dd682b6930a15733c7eb260080e552357d46ef10 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 164/465] wifi: iwlwifi: implement dump region split JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 85ccbdc4d3930f2a6e22d6ba7d42aad241176d09 Author: Johannes Berg Date: Wed Feb 5 14:55:48 2025 +0200 wifi: iwlwifi: implement dump region split Due to hardware design constraints, a reset handshake may be necessary even when the firmware has already crashed, with the dump descriptions indicating which parts should be done before/after the handshake, if needed. Implement that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250205145347.9296e3113d42.Ifb32703fd06a644d08a86b7af1b990738e3c8134@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 9 +- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 99 ++++++++++++++----- .../net/wireless/intel/iwlwifi/iwl-trans.h | 1 + .../wireless/intel/iwlwifi/pcie/trans-gen2.c | 4 +- 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 550de6db1834..4fab6c66994e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -527,6 +527,8 @@ enum iwl_fw_ini_time_point { * @IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA: override trigger data. * Append otherwise * @IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD: send cmd once dump collected + * @IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE: perform reset handshake and + * split dump to before/after with region marking */ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT = BIT(0), @@ -535,6 +537,7 @@ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG = BIT(9), IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA = BIT(10), IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD = BIT(16), + IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE = BIT(17), }; /** @@ -556,12 +559,14 @@ enum iwl_fw_ini_trigger_reset_fw_policy { * @IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT: OS has no limit of dump size * @IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB: mini dump only 600KB region dump * @IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB: mini dump 5MB size dump + * @IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET: dump this region before reset + * handshake (if requested by %IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE) */ enum iwl_fw_ini_dump_policy { IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT = BIT(0), IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB = BIT(1), IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB = BIT(2), - + IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET = BIT(3), }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index c0b332aa260f..03f639fbf9b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2618,29 +2618,28 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { }, }; -static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_dump_data *dump_data, - struct list_head *list) +enum iwl_dump_ini_region_selector { + IWL_INI_DUMP_ALL_REGIONS, + IWL_INI_DUMP_EARLY_REGIONS, + IWL_INI_DUMP_LATE_REGIONS, +}; + +static u32 +iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data, + struct list_head *list, + enum iwl_fw_ini_time_point tp_id, + u64 regions_mask, + struct iwl_dump_ini_region_data *imr_reg_data, + enum iwl_dump_ini_region_selector which) { - struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; - enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point); - struct iwl_dump_ini_region_data reg_data = { - .dump_data = dump_data, - }; - struct iwl_dump_ini_region_data imr_reg_data = { - .dump_data = dump_data, - }; - int i; u32 size = 0; - u64 regions_mask = le64_to_cpu(trigger->regions_mask) & - ~(fwrt->trans->dbg.unsupported_region_msk); - BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask)); - BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < - ARRAY_SIZE(fwrt->trans->dbg.active_regions)); - - for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { - u32 reg_type; + for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { + struct iwl_dump_ini_region_data reg_data = { + .dump_data = dump_data, + }; + u32 reg_type, dp; struct iwl_fw_ini_region_tlv *reg; if (!(BIT_ULL(i) & regions_mask)) @@ -2658,6 +2657,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops)) continue; + dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK); + if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY || reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE || reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) && @@ -2667,6 +2668,20 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, tp_id); continue; } + + switch (which) { + case IWL_INI_DUMP_ALL_REGIONS: + break; + case IWL_INI_DUMP_EARLY_REGIONS: + if (!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET)) + continue; + break; + case IWL_INI_DUMP_LATE_REGIONS: + if (dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET) + continue; + break; + } + /* * DRAM_IMR can be collected only for FW/HW error timepoint * when fw is not alive. In addition, it must be collected @@ -2676,7 +2691,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) { if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT || tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR) - imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i]; + imr_reg_data->reg_tlv = + fwrt->trans->dbg.active_regions[i]; else IWL_INFO(fwrt, "WRT: trying to collect DRAM_IMR at time point: %d, skipping\n", @@ -2689,6 +2705,41 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, size += iwl_dump_ini_mem(fwrt, list, ®_data, &iwl_dump_ini_region_ops[reg_type]); } + + return size; +} + +static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data, + struct list_head *list) +{ + struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; + enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point); + struct iwl_dump_ini_region_data imr_reg_data = { + .dump_data = dump_data, + }; + u32 size = 0; + u64 regions_mask = le64_to_cpu(trigger->regions_mask) & + ~(fwrt->trans->dbg.unsupported_region_msk); + + BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask)); + BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < + ARRAY_SIZE(fwrt->trans->dbg.active_regions)); + + if (trigger->time_point & + cpu_to_le32(IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE)) { + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_EARLY_REGIONS); + iwl_trans_pcie_fw_reset_handshake(fwrt->trans); + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_LATE_REGIONS); + } else { + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_ALL_REGIONS); + } /* collect DRAM_IMR region in the last */ if (imr_reg_data.reg_tlv) size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data, @@ -3101,6 +3152,7 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) struct iwl_fw_dbg_params params = {0}; struct iwl_fwrt_dump_data *dump_data = &fwrt->dump.wks[wk_idx].dump_data; + if (!test_bit(wk_idx, &fwrt->dump.active_wks)) return; @@ -3125,9 +3177,9 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n"); if (iwl_trans_dbg_ini_valid(fwrt->trans)) - iwl_fw_error_ini_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); + iwl_fw_error_ini_dump(fwrt, dump_data); else - iwl_fw_error_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); + iwl_fw_error_dump(fwrt, dump_data); IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n"); iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); @@ -3144,7 +3196,6 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) iwl_force_nmi(fwrt->trans); - out: if (iwl_trans_dbg_ini_valid(fwrt->trans)) { iwl_fw_error_dump_data_free(dump_data); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3561ff8483f2..021691513a57 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1262,6 +1262,7 @@ enum iwl_reset_mode { }; void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode); +void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 793514a1852a..3ece34e30d58 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include "iwl-trans.h" #include "iwl-prph.h" @@ -95,7 +95,7 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } -static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) +void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; From 5c3d319ce7e1ad1d92049c6a1dbbbbaf866e2ab9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 165/465] wifi: ath12k: add support of station average signal strength JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cb53a6785ea5b0a14e7e5af4322bee3df266e949 Author: Nicolas Escande Date: Sat Feb 1 22:13:01 2025 +0100 wifi: ath12k: add support of station average signal strength This adds support for reporting to the kernel the average rssi. This is done the same way as it was done in ath11k. A simple ewma (with the same parameters) is updated with each rssi update. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Nicolas Escande Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250201211301.357985-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 4 ++++ drivers/net/wireless/ath/ath12k/dp_mon.c | 2 ++ drivers/net/wireless/ath/ath12k/mac.c | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 57e71ccbbb67..3dcdedf9e9de 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "qmi.h" #include "htc.h" #include "wmi.h" @@ -483,6 +484,8 @@ struct ath12k_wbm_tx_stats { u64 wbm_tx_comp_stats[HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX]; }; +DECLARE_EWMA(avg_rssi, 10, 8) + struct ath12k_link_sta { struct ath12k_link_vif *arvif; struct ath12k_sta *ahsta; @@ -502,6 +505,7 @@ struct ath12k_link_sta { u64 rx_duration; u64 tx_duration; u8 rssi_comb; + struct ewma_avg_rssi avg_rssi; u8 link_id; struct ath12k_rx_peer_stats *rx_stats; struct ath12k_wbm_tx_stats *wbm_tx_stats; diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 4e9a60181c06..d529229f2d0f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2002,6 +2002,7 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, return; arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; @@ -2174,6 +2175,7 @@ ath12k_dp_mon_rx_update_user_stats(struct ath12k *ar, return; arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); num_msdu = user_stats->tcp_msdu_count + user_stats->tcp_ack_msdu_count + user_stats->udp_msdu_count + user_stats->other_msdu_count; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 16e6f2fae943..fc5cfd860d4d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5519,6 +5519,7 @@ static int ath12k_mac_station_add(struct ath12k *ar, } } + ewma_avg_rssi_init(&arsta->avg_rssi); return 0; free_peer: @@ -10197,6 +10198,13 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); } + + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi); + + if (!db2dbm) + sinfo->signal_avg += ATH12K_DEFAULT_NOISE_FLOOR; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, From 785fbe2eb80fa43e6f8d445a048efcf0cd3dec78 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 166/465] wifi: ath12k: Add HAL_PHYRX_GENERIC_U_SIG TLV parsing support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a5f3c2b89136f13f18ea64fc1144691ca771b98b Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:46 2025 +0530 wifi: ath12k: Add HAL_PHYRX_GENERIC_U_SIG TLV parsing support Currently, monitor is not enabled. However, in the future, the monitor will be enabled. Therefore, add the necessary HAL_PHYRX_GENERIC_U_SIG TLV parsing support in monitor Rx path, which help to populate the EHT radiotap data. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-2-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 205 ++++++++++++++++++++++- drivers/net/wireless/ath/ath12k/hal_rx.h | 60 +++++++ 2 files changed, 264 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index d529229f2d0f..3b0b75958589 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -10,6 +10,9 @@ #include "dp_tx.h" #include "peer.h" +#define ATH12K_LE32_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + static void ath12k_dp_mon_rx_handle_ofdma_info(const struct hal_rx_ppdu_end_user_stats *ppdu_end_user, struct hal_rx_user_status *rx_user_status) @@ -562,6 +565,187 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info * ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } +static void +ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + u32 common; + u8 bw; + + bw = le32_get_bits(cmn->info0, HAL_RX_USIG_CMN_INFO0_BW); + + common = __le32_to_cpu(ppdu_info->usig.common); + common |= IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_PHY_VERSION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) | + u32_encode_bits(bw, IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_UL_DL, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_TXOP, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + ppdu_info->usig.common = cpu_to_le32(common); + + switch (bw) { + default: + fallthrough; + case HAL_EHT_BW_20: + ppdu_info->bw = HAL_RX_BW_20MHZ; + break; + case HAL_EHT_BW_40: + ppdu_info->bw = HAL_RX_BW_40MHZ; + break; + case HAL_EHT_BW_80: + ppdu_info->bw = HAL_RX_BW_80MHZ; + break; + case HAL_EHT_BW_160: + ppdu_info->bw = HAL_RX_BW_160MHZ; + break; + case HAL_EHT_BW_320_1: + case HAL_EHT_BW_320_2: + ppdu_info->bw = HAL_RX_BW_320MHZ; + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + enum ieee80211_radiotap_eht_usig_tb spatial_reuse1, spatial_reuse2; + u32 common, value, mask; + + spatial_reuse1 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1; + spatial_reuse2 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2; + + common = __le32_to_cpu(ppdu_info->usig.common); + value = __le32_to_cpu(ppdu_info->usig.value); + mask = __le32_to_cpu(ppdu_info->usig.mask); + + common |= ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1, + spatial_reuse1) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2, + spatial_reuse2) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + spatial_reuse1 | spatial_reuse2 | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL; + + ppdu_info->usig.common = cpu_to_le32(common); + ppdu_info->usig.value = cpu_to_le32(value); + ppdu_info->usig.mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + enum ieee80211_radiotap_eht_usig_mu sig_symb, punc; + u32 common, value, mask; + + sig_symb = IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS; + punc = IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO; + + common = __le32_to_cpu(ppdu_info->usig.common); + value = __le32_to_cpu(ppdu_info->usig.value); + mask = __le32_to_cpu(ppdu_info->usig.mask); + + common |= ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO, + punc) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM, + sig_symb) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + punc | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS | + sig_symb | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL; + + ppdu_info->usig.common = cpu_to_le32(common); + ppdu_info->usig.value = cpu_to_le32(value); + ppdu_info->usig.mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_hdr(const struct hal_mon_usig_hdr *usig, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_mon_usig_cmn *usig_cmn = &usig->cmn; + u8 comp_mode; + bool ap_ppdu; + + ppdu_info->eht_usig = true; + + ath12k_dp_mon_hal_rx_parse_u_sig_cmn(&usig->cmn, ppdu_info); + + ap_ppdu = le32_get_bits(usig_cmn->info0, HAL_RX_USIG_CMN_INFO0_UL_DL); + comp_mode = le32_get_bits(usig->non_cmn.mu.info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + + if (comp_mode == 0 && ap_ppdu) + ath12k_dp_mon_hal_rx_parse_u_sig_tb(&usig->non_cmn.tb, ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_u_sig_mu(&usig->non_cmn.mu, ppdu_info); +} + static enum hal_rx_mon_status ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, @@ -774,6 +958,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, return HAL_RX_MON_STATUS_MSDU_END; case HAL_RX_MPDU_END: return HAL_RX_MON_STATUS_MPDU_END; + case HAL_PHYRX_GENERIC_U_SIG: + ath12k_dp_mon_hal_rx_parse_u_sig_hdr(tlv_data, ppdu_info); + break; case HAL_DUMMY: return HAL_RX_MON_STATUS_BUF_DONE; case HAL_RX_PPDU_END_STATUS_DONE: @@ -971,7 +1158,23 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, rxs->ampdu_reference = ampdu_id; } - if (ppduinfo->he_mu_flags) { + if (ppduinfo->eht_usig) { + struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_radiotap_eht_usig *usig; + u16 len = sizeof(*usig); + + rxs->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + rxs->encoding = RX_ENC_EHT; + + skb_reset_mac_header(mon_skb); + + tlv = skb_push(mon_skb, sizeof(*tlv) + len); + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG); + tlv->len = cpu_to_le16(len); + + usig = (struct ieee80211_radiotap_eht_usig *)tlv->data; + *usig = ppduinfo->usig; + } else if (ppduinfo->he_mu_flags) { rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 96954b2f7ca6..2da16f27e76c 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -235,6 +235,8 @@ struct hal_rx_mon_ppdu_info { bool is_ampdu; u8 medium_prot_type; bool ppdu_continuation; + bool eht_usig; + struct ieee80211_radiotap_eht_usig usig; }; #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) @@ -678,6 +680,64 @@ enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) return ret; } +enum hal_eht_bw { + HAL_EHT_BW_20, + HAL_EHT_BW_40, + HAL_EHT_BW_80, + HAL_EHT_BW_160, + HAL_EHT_BW_320_1, + HAL_EHT_BW_320_2, +}; + +#define HAL_RX_USIG_CMN_INFO0_PHY_VERSION GENMASK(2, 0) +#define HAL_RX_USIG_CMN_INFO0_BW GENMASK(5, 3) +#define HAL_RX_USIG_CMN_INFO0_UL_DL BIT(6) +#define HAL_RX_USIG_CMN_INFO0_BSS_COLOR GENMASK(12, 7) +#define HAL_RX_USIG_CMN_INFO0_TXOP GENMASK(19, 13) +#define HAL_RX_USIG_CMN_INFO0_DISREGARD GENMASK(25, 20) +#define HAL_RX_USIG_CMN_INFO0_VALIDATE BIT(26) + +struct hal_mon_usig_cmn { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_TB_INFO0_VALIDATE BIT(2) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1 GENMASK(6, 3) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2 GENMASK(10, 7) +#define HAL_RX_USIG_TB_INFO0_DISREGARD_1 GENMASK(15, 11) +#define HAL_RX_USIG_TB_INFO0_CRC GENMASK(19, 16) +#define HAL_RX_USIG_TB_INFO0_TAIL GENMASK(25, 20) +#define HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_tb { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_1 BIT(2) +#define HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO GENMASK(7, 3) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_2 BIT(8) +#define HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS GENMASK(10, 9) +#define HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM GENMASK(15, 11) +#define HAL_RX_USIG_MU_INFO0_CRC GENMASK(20, 16) +#define HAL_RX_USIG_MU_INFO0_TAIL GENMASK(26, 21) +#define HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_mu { + __le32 info0; +} __packed; + +union hal_mon_usig_non_cmn { + struct hal_mon_usig_tb tb; + struct hal_mon_usig_mu mu; +}; + +struct hal_mon_usig_hdr { + struct hal_mon_usig_cmn cmn; + union hal_mon_usig_non_cmn non_cmn; +} __packed; + void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv, struct hal_reo_status *status); From 94abac7d8ccc295b313532d0fc60cb8cfc4cbd2d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 167/465] wifi: ath12k: Add HAL_PHYRX_GENERIC_EHT_SIG TLV parsing support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f756f878692f688640f6994a4dacd98665a2bc2f Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:47 2025 +0530 wifi: ath12k: Add HAL_PHYRX_GENERIC_EHT_SIG TLV parsing support Currently, monitor is not enabled. However, in the future, the monitor will be enabled. Therefore, add the necessary HAL_PHYRX_GENERIC_EHT_SIG TLV parsing support in monitor Rx path, which help to populate the EHT radiotap data. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-3-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 548 +++++++++++++++++++++-- drivers/net/wireless/ath/ath12k/hal_rx.h | 136 +++++- 2 files changed, 638 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 3b0b75958589..0b12701beaef 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -13,6 +13,9 @@ #define ATH12K_LE32_DEC_ENC(value, dec_bits, enc_bits) \ u32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) +#define ATH12K_LE64_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le64_get_bits(value, dec_bits), enc_bits) + static void ath12k_dp_mon_rx_handle_ofdma_info(const struct hal_rx_ppdu_end_user_stats *ppdu_end_user, struct hal_rx_user_status *rx_user_status) @@ -570,11 +573,13 @@ ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn, struct hal_rx_mon_ppdu_info *ppdu_info) { u32 common; - u8 bw; - bw = le32_get_bits(cmn->info0, HAL_RX_USIG_CMN_INFO0_BW); + ppdu_info->u_sig_info.bw = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BW); + ppdu_info->u_sig_info.ul_dl = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_UL_DL); - common = __le32_to_cpu(ppdu_info->usig.common); + common = __le32_to_cpu(ppdu_info->u_sig_info.usig.common); common |= IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN | IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN | IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | @@ -583,19 +588,19 @@ ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn, ATH12K_LE32_DEC_ENC(cmn->info0, HAL_RX_USIG_CMN_INFO0_PHY_VERSION, IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) | - u32_encode_bits(bw, IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | - ATH12K_LE32_DEC_ENC(cmn->info0, - HAL_RX_USIG_CMN_INFO0_UL_DL, - IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) | + u32_encode_bits(ppdu_info->u_sig_info.bw, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | + u32_encode_bits(ppdu_info->u_sig_info.ul_dl, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) | ATH12K_LE32_DEC_ENC(cmn->info0, HAL_RX_USIG_CMN_INFO0_BSS_COLOR, IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR) | ATH12K_LE32_DEC_ENC(cmn->info0, HAL_RX_USIG_CMN_INFO0_TXOP, IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); - ppdu_info->usig.common = cpu_to_le32(common); + ppdu_info->u_sig_info.usig.common = cpu_to_le32(common); - switch (bw) { + switch (ppdu_info->u_sig_info.bw) { default: fallthrough; case HAL_EHT_BW_20: @@ -621,24 +626,28 @@ static void ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb, struct hal_rx_mon_ppdu_info *ppdu_info) { + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; enum ieee80211_radiotap_eht_usig_tb spatial_reuse1, spatial_reuse2; u32 common, value, mask; spatial_reuse1 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1; spatial_reuse2 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2; - common = __le32_to_cpu(ppdu_info->usig.common); - value = __le32_to_cpu(ppdu_info->usig.value); - mask = __le32_to_cpu(ppdu_info->usig.mask); + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE); common |= ATH12K_LE32_DEC_ENC(usig_tb->info0, HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS, IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); value |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | - ATH12K_LE32_DEC_ENC(usig_tb->info0, - HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE, - IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) | IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | ATH12K_LE32_DEC_ENC(usig_tb->info0, HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1, @@ -662,24 +671,35 @@ ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb, IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC | IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL; - ppdu_info->usig.common = cpu_to_le32(common); - ppdu_info->usig.value = cpu_to_le32(value); - ppdu_info->usig.mask = cpu_to_le32(mask); + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); } static void ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, struct hal_rx_mon_ppdu_info *ppdu_info) { + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; enum ieee80211_radiotap_eht_usig_mu sig_symb, punc; u32 common, value, mask; sig_symb = IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS; punc = IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO; - common = __le32_to_cpu(ppdu_info->usig.common); - value = __le32_to_cpu(ppdu_info->usig.value); - mask = __le32_to_cpu(ppdu_info->usig.mask); + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + ppdu_info->u_sig_info.eht_sig_mcs = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS); + ppdu_info->u_sig_info.num_eht_sig_sym = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM); common |= ATH12K_LE32_DEC_ENC(usig_mu->info0, HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS, @@ -687,20 +707,17 @@ ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, value |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | - ATH12K_LE32_DEC_ENC(usig_mu->info0, - HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE, - IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) | IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | ATH12K_LE32_DEC_ENC(usig_mu->info0, HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO, punc) | IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | - ATH12K_LE32_DEC_ENC(usig_mu->info0, - HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS, - IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) | - ATH12K_LE32_DEC_ENC(usig_mu->info0, - HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM, - sig_symb) | + u32_encode_bits(ppdu_info->u_sig_info.eht_sig_mcs, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) | + u32_encode_bits(ppdu_info->u_sig_info.num_eht_sig_sym, + sig_symb) | ATH12K_LE32_DEC_ENC(usig_mu->info0, HAL_RX_USIG_MU_INFO0_CRC, IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC) | @@ -719,33 +736,411 @@ ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC | IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL; - ppdu_info->usig.common = cpu_to_le32(common); - ppdu_info->usig.value = cpu_to_le32(value); - ppdu_info->usig.mask = cpu_to_le32(mask); + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); } static void ath12k_dp_mon_hal_rx_parse_u_sig_hdr(const struct hal_mon_usig_hdr *usig, struct hal_rx_mon_ppdu_info *ppdu_info) { - const struct hal_mon_usig_cmn *usig_cmn = &usig->cmn; u8 comp_mode; - bool ap_ppdu; ppdu_info->eht_usig = true; ath12k_dp_mon_hal_rx_parse_u_sig_cmn(&usig->cmn, ppdu_info); - ap_ppdu = le32_get_bits(usig_cmn->info0, HAL_RX_USIG_CMN_INFO0_UL_DL); comp_mode = le32_get_bits(usig->non_cmn.mu.info0, HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); - if (comp_mode == 0 && ap_ppdu) + if (comp_mode == 0 && ppdu_info->u_sig_info.ul_dl) ath12k_dp_mon_hal_rx_parse_u_sig_tb(&usig->non_cmn.tb, ppdu_info); else ath12k_dp_mon_hal_rx_parse_u_sig_mu(&usig->non_cmn.mu, ppdu_info); } +static void +ath12k_dp_mon_hal_aggr_tlv(struct hal_rx_mon_ppdu_info *ppdu_info, + u16 tlv_len, const void *tlv_data) +{ + if (tlv_len <= HAL_RX_MON_MAX_AGGR_SIZE - ppdu_info->tlv_aggr.cur_len) { + memcpy(ppdu_info->tlv_aggr.buf + ppdu_info->tlv_aggr.cur_len, + tlv_data, tlv_len); + ppdu_info->tlv_aggr.cur_len += tlv_len; + } +} + +static inline bool +ath12k_dp_mon_hal_rx_is_frame_type_ndp(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 1 && + usig_info->eht_sig_mcs == 0 && + usig_info->num_eht_sig_sym == 0) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_non_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + u32 ppdu_type_comp_mode = usig_info->ppdu_type_comp_mode; + u32 ul_dl = usig_info->ul_dl; + + if ((ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_OFDMA && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 1)) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 0 && usig_info->ul_dl == 0) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(const struct hal_eht_sig_ndp_cmn_eb *eht_sig_ndp, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_S | + IEEE80211_RADIOTAP_EHT_KNOWN_CRC1 | + IEEE80211_RADIOTAP_EHT_KNOWN_TAIL1; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_DATA0_CRC1_O); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_S); + eht->data[0] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_usig_overflow(const struct hal_eht_sig_usig_overflow *ovflow, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_O; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_O); + eht->data[0] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_non_ofdma_users(const struct hal_eht_sig_non_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eb->info0, + HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(const struct hal_eht_sig_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_KNOWN_M | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_M); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(const struct hal_eht_sig_non_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS); + + ppdu_info->nss = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS) + 1; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_mu_mimo_user(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_SU && + usig_info->ul_dl == 1) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_non_ofdma_cmn_eb *eb = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_non_ofdma_users(eb, ppdu_info); + + if (ath12k_dp_mon_hal_rx_is_mu_mimo_user(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(&eb->user_field.mu_mimo, + ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&eb->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_hal_rx_parse_ru_allocation(const struct hal_eht_sig_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb1 *ofdma_cmn_eb1 = &eb->eb1; + const struct hal_eht_sig_ofdma_cmn_eb2 *ofdma_cmn_eb2 = &eb->eb2; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ieee80211_radiotap_eht_data ru_123, ru_124, ru_125, ru_126; + enum ieee80211_radiotap_eht_data ru_121, ru_122, ru_112, ru_111; + u32 data; + + ru_123 = IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3; + ru_124 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4; + ru_125 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5; + ru_126 = IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6; + ru_121 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1; + ru_122 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2; + ru_112 = IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2; + ru_111 = IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1; + + switch (ppdu_info->u_sig_info.bw) { + case HAL_EHT_BW_320_2: + case HAL_EHT_BW_320_1: + data = __le32_to_cpu(eht->data[4]); + /* CC1 2::3 */ + data |= IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3, + ru_123); + eht->data[4] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[5]); + /* CC1 2::4 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4, + ru_124); + + /* CC1 2::5 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5, + ru_125); + eht->data[5] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[6]); + /* CC1 2::6 */ + data |= IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6, + ru_126); + eht->data[6] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_160: + data = __le32_to_cpu(eht->data[3]); + /* CC1 2::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1, + ru_121); + /* CC1 2::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2, + ru_122); + eht->data[3] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_80: + data = __le32_to_cpu(eht->data[2]); + /* CC1 1::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2, + ru_112); + eht->data[2] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_40: + fallthrough; + case HAL_EHT_BW_20: + data = __le32_to_cpu(eht->data[1]); + /* CC1 1::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1, + ru_111); + eht->data[1] = cpu_to_le32(data); + break; + default: + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb *ofdma = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_ru_allocation(ofdma, ppdu_info); + + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&ofdma->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_parse_eht_sig_hdr(struct hal_rx_mon_ppdu_info *ppdu_info, + const void *tlv_data) +{ + ppdu_info->is_eht = true; + + if (ath12k_dp_mon_hal_rx_is_frame_type_ndp(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_non_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(tlv_data, ppdu_info); +} + static enum hal_rx_mon_status ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, @@ -754,11 +1149,19 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; const void *tlv_data = tlv->value; u32 info[7], userid; - u16 tlv_tag; + u16 tlv_tag, tlv_len; tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); + if (ppdu_info->tlv_aggr.in_progress && ppdu_info->tlv_aggr.tlv_tag != tlv_tag) { + ath12k_dp_mon_parse_eht_sig_hdr(ppdu_info, ppdu_info->tlv_aggr.buf); + + ppdu_info->tlv_aggr.in_progress = false; + ppdu_info->tlv_aggr.cur_len = 0; + } + switch (tlv_tag) { case HAL_RX_PPDU_START: { const struct hal_rx_ppdu_start *ppdu_start = tlv_data; @@ -836,6 +1239,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, case HAL_RX_PREAMBLE_11AX: ppdu_info->he_flags = 1; break; + case HAL_RX_PREAMBLE_11BE: + ppdu_info->is_eht = true; + break; default: break; } @@ -961,6 +1367,21 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, case HAL_PHYRX_GENERIC_U_SIG: ath12k_dp_mon_hal_rx_parse_u_sig_hdr(tlv_data, ppdu_info); break; + case HAL_PHYRX_GENERIC_EHT_SIG: + /* Handle the case where aggregation is in progress + * or the current TLV is one of the TLVs which should be + * aggregated + */ + if (!ppdu_info->tlv_aggr.in_progress) { + ppdu_info->tlv_aggr.in_progress = true; + ppdu_info->tlv_aggr.tlv_tag = tlv_tag; + ppdu_info->tlv_aggr.cur_len = 0; + } + + ppdu_info->is_eht = true; + + ath12k_dp_mon_hal_aggr_tlv(ppdu_info, tlv_len, tlv_data); + break; case HAL_DUMMY: return HAL_RX_MON_STATUS_BUF_DONE; case HAL_RX_PPDU_END_STATUS_DONE: @@ -1158,22 +1579,59 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, rxs->ampdu_reference = ampdu_id; } - if (ppduinfo->eht_usig) { + if (ppduinfo->is_eht || ppduinfo->eht_usig) { struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_radiotap_eht *eht; struct ieee80211_radiotap_eht_usig *usig; - u16 len = sizeof(*usig); + u16 len = 0, i, eht_len, usig_len; + u8 user; + + if (ppduinfo->is_eht) { + eht_len = struct_size(eht, + user_info, + ppduinfo->eht_info.num_user_info); + len += sizeof(*tlv) + eht_len; + } + + if (ppduinfo->eht_usig) { + usig_len = sizeof(*usig); + len += sizeof(*tlv) + usig_len; + } rxs->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; rxs->encoding = RX_ENC_EHT; skb_reset_mac_header(mon_skb); - tlv = skb_push(mon_skb, sizeof(*tlv) + len); - tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG); - tlv->len = cpu_to_le16(len); + tlv = skb_push(mon_skb, len); - usig = (struct ieee80211_radiotap_eht_usig *)tlv->data; - *usig = ppduinfo->usig; + if (ppduinfo->is_eht) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT); + tlv->len = cpu_to_le16(eht_len); + + eht = (struct ieee80211_radiotap_eht *)tlv->data; + eht->known = ppduinfo->eht_info.eht.known; + + for (i = 0; + i < ARRAY_SIZE(eht->data) && + i < ARRAY_SIZE(ppduinfo->eht_info.eht.data); + i++) + eht->data[i] = ppduinfo->eht_info.eht.data[i]; + + for (user = 0; user < ppduinfo->eht_info.num_user_info; user++) + put_unaligned_le32(ppduinfo->eht_info.user_info[user], + &eht->user_info[user]); + + tlv = (struct ieee80211_radiotap_tlv *)&tlv->data[eht_len]; + } + + if (ppduinfo->eht_usig) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG); + tlv->len = cpu_to_le16(usig_len); + + usig = (struct ieee80211_radiotap_eht_usig *)tlv->data; + *usig = ppduinfo->u_sig_info.usig; + } } else if (ppduinfo->he_mu_flags) { rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; rxs->encoding = RX_ENC_HE; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 2da16f27e76c..959f2283294c 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -71,6 +71,8 @@ enum hal_rx_preamble { HAL_RX_PREAMBLE_11N, HAL_RX_PREAMBLE_11AC, HAL_RX_PREAMBLE_11AX, + HAL_RX_PREAMBLE_11BA, + HAL_RX_PREAMBLE_11BE, HAL_RX_PREAMBLE_MAX, }; @@ -150,6 +152,37 @@ struct hal_rx_user_status { #define HAL_MAX_UL_MU_USERS 37 +struct hal_rx_u_sig_info { + bool ul_dl; + u8 bw; + u8 ppdu_type_comp_mode; + u8 eht_sig_mcs; + u8 num_eht_sig_sym; + struct ieee80211_radiotap_eht_usig usig; +}; + +#define HAL_RX_MON_MAX_AGGR_SIZE 128 + +struct hal_rx_tlv_aggr_info { + bool in_progress; + u16 cur_len; + u16 tlv_tag; + u8 buf[HAL_RX_MON_MAX_AGGR_SIZE]; +}; + +struct hal_rx_radiotap_eht { + __le32 known; + __le32 data[9]; +}; + +#define EHT_MAX_USER_INFO 4 + +struct hal_rx_eht_info { + u8 num_user_info; + struct hal_rx_radiotap_eht eht; + u32 user_info[EHT_MAX_USER_INFO]; +}; + struct hal_rx_mon_ppdu_info { u32 ppdu_id; u32 last_ppdu_id; @@ -236,7 +269,10 @@ struct hal_rx_mon_ppdu_info { u8 medium_prot_type; bool ppdu_continuation; bool eht_usig; - struct ieee80211_radiotap_eht_usig usig; + struct hal_rx_u_sig_info u_sig_info; + bool is_eht; + struct hal_rx_eht_info eht_info; + struct hal_rx_tlv_aggr_info tlv_aggr; }; #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) @@ -647,6 +683,104 @@ struct hal_rx_resp_req_info { #define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6) #define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS GENMASK(10, 7) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED BIT(11) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD GENMASK(13, 12) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC GENMASK(17, 14) + +struct hal_eht_sig_ndp_cmn_eb { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD GENMASK(16, 13) + +struct hal_eht_sig_usig_overflow { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_VALIDATE BIT(15) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS GENMASK(19, 16) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED BIT(20) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING BIT(21) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CRC GENMASK(25, 22) + +struct hal_eht_sig_non_mu_mimo { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING BIT(15) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING GENMASK(22, 16) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CRC GENMASK(26, 23) + +struct hal_eht_sig_mu_mimo { + __le32 info0; +} __packed; + +union hal_eht_sig_user_field { + struct hal_eht_sig_mu_mimo mu_mimo; + struct hal_eht_sig_non_mu_mimo n_mu_mimo; +}; + +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISREGARD GENMASK(16, 13) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS GENMASK(19, 17) + +struct hal_eht_sig_non_ofdma_cmn_eb { + __le32 info0; + union hal_eht_sig_user_field user_field; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB1_SPATIAL_REUSE GENMASK_ULL(3, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB1_GI_LTF GENMASK_ULL(5, 4) +#define HAL_RX_EHT_SIG_OFDMA_EB1_NUM_LFT_SYM GENMASK_ULL(8, 6) +#define HAL_RX_EHT_SIG_OFDMA_EB1_LDPC_EXTRA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_FEC_PAD_FACTOR GENMASK_ULL(11, 10) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OFDMA_EB1_DISREGARD GENMASK_ULL(16, 13) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1 GENMASK_ULL(25, 17) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2 GENMASK_ULL(34, 26) +#define HAL_RX_EHT_SIG_OFDMA_EB1_CRC GENMASK_ULL(30, 27) + +struct hal_eht_sig_ofdma_cmn_eb1 { + __le64 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1 GENMASK_ULL(8, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2 GENMASK_ULL(17, 9) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3 GENMASK_ULL(26, 18) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45) +#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54) + +struct hal_eht_sig_ofdma_cmn_eb2 { + __le64 info0; +} __packed; + +struct hal_eht_sig_ofdma_cmn_eb { + struct hal_eht_sig_ofdma_cmn_eb1 eb1; + struct hal_eht_sig_ofdma_cmn_eb2 eb2; + union hal_eht_sig_user_field user_field; +} __packed; + static inline enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) { From 2886cb26aaa9f844723b7ec390df7c46b2225a60 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 168/465] wifi: ath12k: Add HAL_RX_PPDU_START_USER_INFO TLV parsing support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e6fa62bada8796d13f04e2538297b81decc1e335 Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:48 2025 +0530 wifi: ath12k: Add HAL_RX_PPDU_START_USER_INFO TLV parsing support Currently, monitor is not enabled. However, in the future, the monitor will be enabled. Therefore, add necessary HAL_RX_PPDU_START_USER_INFO TLV parsing support in monitor Rx path, which help to populate the EHT radiotap data. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-4-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 307 +++++++++++++++++++++- drivers/net/wireless/ath/ath12k/hal_rx.h | 291 +++++++++++++++++--- drivers/net/wireless/ath/ath12k/rx_desc.h | 9 - 3 files changed, 551 insertions(+), 56 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 0b12701beaef..76b6d5861e25 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -81,7 +81,7 @@ ath12k_dp_mon_rx_populate_mu_user_info(const struct hal_rx_ppdu_end_user_stats * static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vht_sig, struct hal_rx_mon_ppdu_info *ppdu_info) { - u32 nsts, group_id, info0, info1; + u32 nsts, info0, info1; u8 gi_setting; info0 = __le32_to_cpu(vht_sig->info0); @@ -109,12 +109,8 @@ static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vh ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW); ppdu_info->beamformed = u32_get_bits(info1, HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED); - group_id = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); - if (group_id == 0 || group_id == 63) - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; - else - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; - ppdu_info->vht_flag_values5 = group_id; + ppdu_info->vht_flag_values5 = u32_get_bits(info0, + HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) | ppdu_info->nss); ppdu_info->vht_flag_values2 = ppdu_info->bw; @@ -134,7 +130,6 @@ static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig, ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING); ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI); ppdu_info->nss = (ppdu_info->mcs >> 3); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, @@ -166,7 +161,6 @@ static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, ppdu_info->rate = rate; ppdu_info->cck_flag = 1; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, @@ -206,7 +200,6 @@ static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, } ppdu_info->rate = rate; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void @@ -243,7 +236,6 @@ ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *of ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS); ppdu_info->beamformed = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; } static void @@ -283,7 +275,6 @@ ath12k_dp_mon_parse_he_sig_b1_mu(const struct hal_rx_he_sig_b1_mu_info *he_sig_b HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION); ppdu_info->ru_alloc = ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ppdu_info->he_RU[0] = ru_tones; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } static void @@ -417,7 +408,6 @@ ath12k_dp_mon_parse_he_sig_mu(const struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_ ppdu_info->is_stbc = info1 & HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *he_sig_a, @@ -565,7 +555,6 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info * dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM); ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS); ppdu_info->dcm = dcm; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void @@ -1141,6 +1130,292 @@ ath12k_dp_mon_parse_eht_sig_hdr(struct hal_rx_mon_ppdu_info *ppdu_info, ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(tlv_data, ppdu_info); } +static inline enum ath12k_eht_ru_size +hal_rx_mon_hal_ru_size_to_ath12k_ru_size(u32 hal_ru_size) +{ + switch (hal_ru_size) { + case HAL_EHT_RU_26: + return ATH12K_EHT_RU_26; + case HAL_EHT_RU_52: + return ATH12K_EHT_RU_52; + case HAL_EHT_RU_78: + return ATH12K_EHT_RU_52_26; + case HAL_EHT_RU_106: + return ATH12K_EHT_RU_106; + case HAL_EHT_RU_132: + return ATH12K_EHT_RU_106_26; + case HAL_EHT_RU_242: + return ATH12K_EHT_RU_242; + case HAL_EHT_RU_484: + return ATH12K_EHT_RU_484; + case HAL_EHT_RU_726: + return ATH12K_EHT_RU_484_242; + case HAL_EHT_RU_996: + return ATH12K_EHT_RU_996; + case HAL_EHT_RU_996x2: + return ATH12K_EHT_RU_996x2; + case HAL_EHT_RU_996x3: + return ATH12K_EHT_RU_996x3; + case HAL_EHT_RU_996x4: + return ATH12K_EHT_RU_996x4; + case HAL_EHT_RU_NONE: + return ATH12K_EHT_RU_INVALID; + case HAL_EHT_RU_996_484: + return ATH12K_EHT_RU_996_484; + case HAL_EHT_RU_996x2_484: + return ATH12K_EHT_RU_996x2_484; + case HAL_EHT_RU_996x3_484: + return ATH12K_EHT_RU_996x3_484; + case HAL_EHT_RU_996_484_242: + return ATH12K_EHT_RU_996_484_242; + default: + return ATH12K_EHT_RU_INVALID; + } +} + +static inline u32 +hal_rx_ul_ofdma_ru_size_to_width(enum ath12k_eht_ru_size ru_size) +{ + switch (ru_size) { + case ATH12K_EHT_RU_26: + return RU_26; + case ATH12K_EHT_RU_52: + return RU_52; + case ATH12K_EHT_RU_52_26: + return RU_52_26; + case ATH12K_EHT_RU_106: + return RU_106; + case ATH12K_EHT_RU_106_26: + return RU_106_26; + case ATH12K_EHT_RU_242: + return RU_242; + case ATH12K_EHT_RU_484: + return RU_484; + case ATH12K_EHT_RU_484_242: + return RU_484_242; + case ATH12K_EHT_RU_996: + return RU_996; + case ATH12K_EHT_RU_996_484: + return RU_996_484; + case ATH12K_EHT_RU_996_484_242: + return RU_996_484_242; + case ATH12K_EHT_RU_996x2: + return RU_2X996; + case ATH12K_EHT_RU_996x2_484: + return RU_2X996_484; + case ATH12K_EHT_RU_996x3: + return RU_3X996; + case ATH12K_EHT_RU_996x3_484: + return RU_3X996_484; + case ATH12K_EHT_RU_996x4: + return RU_4X996; + default: + return RU_INVALID; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *rx_usr_info, + u16 user_id, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_user_status *mon_rx_user_status = NULL; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ath12k_eht_ru_size rtap_ru_size = ATH12K_EHT_RU_INVALID; + u32 ru_width, reception_type, ru_index = HAL_EHT_RU_INVALID; + u32 ru_type_80_0, ru_start_index_80_0; + u32 ru_type_80_1, ru_start_index_80_1; + u32 ru_type_80_2, ru_start_index_80_2; + u32 ru_type_80_3, ru_start_index_80_3; + u32 ru_size = 0, num_80mhz_with_ru = 0; + u64 ru_index_320mhz = 0; + u32 ru_index_per80mhz; + + reception_type = le32_get_bits(rx_usr_info->info0, + HAL_RX_USR_INFO0_RECEPTION_TYPE); + + switch (reception_type) { + case HAL_RECEPTION_TYPE_SU: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + case HAL_RECEPTION_TYPE_DL_MU_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFMA: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO; + } + + ppdu_info->is_stbc = le32_get_bits(rx_usr_info->info0, HAL_RX_USR_INFO0_STBC); + ppdu_info->ldpc = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_LDPC); + ppdu_info->dcm = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_STA_DCM); + ppdu_info->bw = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_RX_BW); + ppdu_info->mcs = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_MCS); + ppdu_info->nss = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_NSS) + 1; + + if (user_id < HAL_MAX_UL_MU_USERS) { + mon_rx_user_status = &ppdu_info->userstats[user_id]; + mon_rx_user_status->mcs = ppdu_info->mcs; + mon_rx_user_status->nss = ppdu_info->nss; + } + + if (!(ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_MIMO || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO)) + return; + + /* RU allocation present only for OFDMA reception */ + ru_type_80_0 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_0); + ru_start_index_80_0 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_0); + if (ru_type_80_0 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_0; + ru_index_per80mhz = ru_start_index_80_0; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_0, 0, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_1 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_1); + ru_start_index_80_1 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_1); + if (ru_type_80_1 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_1; + ru_index_per80mhz = ru_start_index_80_1; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_1, 1, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_2 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_2); + ru_start_index_80_2 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_2); + if (ru_type_80_2 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_2; + ru_index_per80mhz = ru_start_index_80_2; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_2, 2, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_3 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_3); + ru_start_index_80_3 = le32_get_bits(rx_usr_info->info2, + HAL_RX_USR_INFO3_RU_START_IDX_80_3); + if (ru_type_80_3 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_3; + ru_index_per80mhz = ru_start_index_80_3; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_3, 3, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + if (num_80mhz_with_ru > 1) { + /* Calculate the MRU index */ + switch (ru_index_320mhz) { + case HAL_EHT_RU_996_484_0: + case HAL_EHT_RU_996x2_484_0: + case HAL_EHT_RU_996x3_484_0: + ru_index = 0; + break; + case HAL_EHT_RU_996_484_1: + case HAL_EHT_RU_996x2_484_1: + case HAL_EHT_RU_996x3_484_1: + ru_index = 1; + break; + case HAL_EHT_RU_996_484_2: + case HAL_EHT_RU_996x2_484_2: + case HAL_EHT_RU_996x3_484_2: + ru_index = 2; + break; + case HAL_EHT_RU_996_484_3: + case HAL_EHT_RU_996x2_484_3: + case HAL_EHT_RU_996x3_484_3: + ru_index = 3; + break; + case HAL_EHT_RU_996_484_4: + case HAL_EHT_RU_996x2_484_4: + case HAL_EHT_RU_996x3_484_4: + ru_index = 4; + break; + case HAL_EHT_RU_996_484_5: + case HAL_EHT_RU_996x2_484_5: + case HAL_EHT_RU_996x3_484_5: + ru_index = 5; + break; + case HAL_EHT_RU_996_484_6: + case HAL_EHT_RU_996x2_484_6: + case HAL_EHT_RU_996x3_484_6: + ru_index = 6; + break; + case HAL_EHT_RU_996_484_7: + case HAL_EHT_RU_996x2_484_7: + case HAL_EHT_RU_996x3_484_7: + ru_index = 7; + break; + case HAL_EHT_RU_996x2_484_8: + ru_index = 8; + break; + case HAL_EHT_RU_996x2_484_9: + ru_index = 9; + break; + case HAL_EHT_RU_996x2_484_10: + ru_index = 10; + break; + case HAL_EHT_RU_996x2_484_11: + ru_index = 11; + break; + default: + ru_index = HAL_EHT_RU_INVALID; + break; + } + + ru_size += 4; + } + + rtap_ru_size = hal_rx_mon_hal_ru_size_to_ath12k_ru_size(ru_size); + if (rtap_ru_size != ATH12K_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_SIZE_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_SIZE); + eht->data[1] = cpu_to_le32(data); + } + + if (ru_index != HAL_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_INDEX_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_INDEX); + eht->data[1] = cpu_to_le32(data); + } + + if (mon_rx_user_status && ru_index != HAL_EHT_RU_INVALID && + rtap_ru_size != ATH12K_EHT_RU_INVALID) { + mon_rx_user_status->ul_ofdma_ru_start_index = ru_index; + mon_rx_user_status->ul_ofdma_ru_size = rtap_ru_size; + + ru_width = hal_rx_ul_ofdma_ru_size_to_width(rtap_ru_size); + + mon_rx_user_status->ul_ofdma_ru_width = ru_width; + mon_rx_user_status->ofdma_info_valid = 1; + } +} + static enum hal_rx_mon_status ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, @@ -1324,6 +1599,10 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW); break; } + case HAL_RX_PPDU_START_USER_INFO: + ath12k_dp_mon_hal_rx_parse_user_info(tlv_data, userid, ppdu_info); + break; + case HAL_RXPCU_PPDU_END_INFO: { const struct hal_rx_ppdu_end_duration *ppdu_rx_duration = tlv_data; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 959f2283294c..765b53741861 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -781,39 +781,6 @@ struct hal_eht_sig_ofdma_cmn_eb { union hal_eht_sig_user_field user_field; } __packed; -static inline -enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) -{ - enum nl80211_he_ru_alloc ret; - - switch (ru_tones) { - case RU_52: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; - break; - case RU_106: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; - break; - case RU_242: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; - break; - case RU_484: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; - break; - case RU_996: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; - break; - case RU_2X996: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; - break; - case RU_26: - fallthrough; - default: - ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; - break; - } - return ret; -} - enum hal_eht_bw { HAL_EHT_BW_20, HAL_EHT_BW_40, @@ -872,6 +839,264 @@ struct hal_mon_usig_hdr { union hal_mon_usig_non_cmn non_cmn; } __packed; +#define HAL_RX_USR_INFO0_PHY_PPDU_ID GENMASK(15, 0) +#define HAL_RX_USR_INFO0_USR_RSSI GENMASK(23, 16) +#define HAL_RX_USR_INFO0_PKT_TYPE GENMASK(27, 24) +#define HAL_RX_USR_INFO0_STBC BIT(28) +#define HAL_RX_USR_INFO0_RECEPTION_TYPE GENMASK(31, 29) + +#define HAL_RX_USR_INFO1_MCS GENMASK(3, 0) +#define HAL_RX_USR_INFO1_SGI GENMASK(5, 4) +#define HAL_RX_USR_INFO1_HE_RANGING_NDP BIT(6) +#define HAL_RX_USR_INFO1_MIMO_SS_BITMAP GENMASK(15, 8) +#define HAL_RX_USR_INFO1_RX_BW GENMASK(18, 16) +#define HAL_RX_USR_INFO1_DL_OFMDA_USR_IDX GENMASK(31, 24) + +#define HAL_RX_USR_INFO2_DL_OFDMA_CONTENT_CHAN BIT(0) +#define HAL_RX_USR_INFO2_NSS GENMASK(10, 8) +#define HAL_RX_USR_INFO2_STREAM_OFFSET GENMASK(13, 11) +#define HAL_RX_USR_INFO2_STA_DCM BIT(14) +#define HAL_RX_USR_INFO2_LDPC BIT(15) +#define HAL_RX_USR_INFO2_RU_TYPE_80_0 GENMASK(19, 16) +#define HAL_RX_USR_INFO2_RU_TYPE_80_1 GENMASK(23, 20) +#define HAL_RX_USR_INFO2_RU_TYPE_80_2 GENMASK(27, 24) +#define HAL_RX_USR_INFO2_RU_TYPE_80_3 GENMASK(31, 28) + +#define HAL_RX_USR_INFO3_RU_START_IDX_80_0 GENMASK(5, 0) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_1 GENMASK(13, 8) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_2 GENMASK(21, 16) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_3 GENMASK(29, 24) + +struct hal_receive_user_info { + __le32 info0; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 user_fd_rssi_seg0; + __le32 user_fd_rssi_seg1; + __le32 user_fd_rssi_seg2; + __le32 user_fd_rssi_seg3; +} __packed; + +enum hal_mon_reception_type { + HAL_RECEPTION_TYPE_SU, + HAL_RECEPTION_TYPE_DL_MU_MIMO, + HAL_RECEPTION_TYPE_DL_MU_OFMA, + HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + HAL_RECEPTION_TYPE_UL_MU_MIMO, + HAL_RECEPTION_TYPE_UL_MU_OFDMA, + HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +/* Different allowed RU in 11BE */ +#define HAL_EHT_RU_26 0ULL +#define HAL_EHT_RU_52 1ULL +#define HAL_EHT_RU_78 2ULL +#define HAL_EHT_RU_106 3ULL +#define HAL_EHT_RU_132 4ULL +#define HAL_EHT_RU_242 5ULL +#define HAL_EHT_RU_484 6ULL +#define HAL_EHT_RU_726 7ULL +#define HAL_EHT_RU_996 8ULL +#define HAL_EHT_RU_996x2 9ULL +#define HAL_EHT_RU_996x3 10ULL +#define HAL_EHT_RU_996x4 11ULL +#define HAL_EHT_RU_NONE 15ULL +#define HAL_EHT_RU_INVALID 31ULL +/* MRUs spanning above 80Mhz + * HAL_EHT_RU_996_484 = HAL_EHT_RU_484 + HAL_EHT_RU_996 + 4 (reserved) + */ +#define HAL_EHT_RU_996_484 18ULL +#define HAL_EHT_RU_996x2_484 28ULL +#define HAL_EHT_RU_996x3_484 40ULL +#define HAL_EHT_RU_996_484_242 23ULL + +#define NUM_RU_BITS_PER80 16 +#define NUM_RU_BITS_PER20 4 + +/* Different per_80Mhz band in 320Mhz bandwidth */ +#define HAL_80_0 0 +#define HAL_80_1 1 +#define HAL_80_2 2 +#define HAL_80_3 3 + +#define HAL_RU_80MHZ(num_band) ((num_band) * NUM_RU_BITS_PER80) +#define HAL_RU_20MHZ(idx_per_80) ((idx_per_80) * NUM_RU_BITS_PER20) + +#define HAL_RU_SHIFT(num_band, idx_per_80) \ + (HAL_RU_80MHZ(num_band) + HAL_RU_20MHZ(idx_per_80)) + +#define HAL_RU(ru, num_band, idx_per_80) \ + ((u64)(ru) << HAL_RU_SHIFT(num_band, idx_per_80)) + +/* MRU-996+484 */ +#define HAL_EHT_RU_996_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_2 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1)) +#define HAL_EHT_RU_996_484_3 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_4 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_5 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_6 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996_484_7 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x2+484 */ +#define HAL_EHT_RU_996x2_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_2 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_3 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_4 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1)) +#define HAL_EHT_RU_996x2_484_5 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_6 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_7 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_8 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_9 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_10 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x2_484_11 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x3+484 */ +#define HAL_EHT_RU_996x3_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_2 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_3 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_4 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_5 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_6 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x3_484_7 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +#define HAL_RU_PER80(ru_per80, num_80mhz, ru_idx_per80mhz) \ + (HAL_RU(ru_per80, num_80mhz, ru_idx_per80mhz)) + +#define RU_INVALID 0 +#define RU_26 1 +#define RU_52 2 +#define RU_106 4 +#define RU_242 9 +#define RU_484 18 +#define RU_996 37 +#define RU_2X996 74 +#define RU_3X996 111 +#define RU_4X996 148 +#define RU_52_26 (RU_52 + RU_26) +#define RU_106_26 (RU_106 + RU_26) +#define RU_484_242 (RU_484 + RU_242) +#define RU_996_484 (RU_996 + RU_484) +#define RU_996_484_242 (RU_996 + RU_484_242) +#define RU_2X996_484 (RU_2X996 + RU_484) +#define RU_3X996_484 (RU_3X996 + RU_484) + +enum ath12k_eht_ru_size { + ATH12K_EHT_RU_26, + ATH12K_EHT_RU_52, + ATH12K_EHT_RU_106, + ATH12K_EHT_RU_242, + ATH12K_EHT_RU_484, + ATH12K_EHT_RU_996, + ATH12K_EHT_RU_996x2, + ATH12K_EHT_RU_996x4, + ATH12K_EHT_RU_52_26, + ATH12K_EHT_RU_106_26, + ATH12K_EHT_RU_484_242, + ATH12K_EHT_RU_996_484, + ATH12K_EHT_RU_996_484_242, + ATH12K_EHT_RU_996x2_484, + ATH12K_EHT_RU_996x3, + ATH12K_EHT_RU_996x3_484, + + /* Keep last */ + ATH12K_EHT_RU_INVALID, +}; + +#define HAL_RX_RU_ALLOC_TYPE_MAX ATH12K_EHT_RU_INVALID + +static inline +enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) +{ + enum nl80211_he_ru_alloc ret; + + switch (ru_tones) { + case RU_52: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; + break; + case RU_106: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; + break; + case RU_242: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; + break; + case RU_484: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; + break; + case RU_996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case RU_2X996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + case RU_26: + fallthrough; + default: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; + break; + } + return ret; +} + void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv, struct hal_reo_status *status); diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 7367935ee68b..6c600473b402 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1541,13 +1541,4 @@ struct hal_rx_desc { #define MAX_MU_GROUP_SHOW 16 #define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW) -#define HAL_RX_RU_ALLOC_TYPE_MAX 6 -#define RU_26 1 -#define RU_52 2 -#define RU_106 4 -#define RU_242 9 -#define RU_484 18 -#define RU_996 37 -#define RU_2X996 74 - #endif /* ATH12K_RX_DESC_H */ From fc904e46c8f8e16f9bfbeeced0baabaafab2b11d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 169/465] wifi: ath12k: Add HAL_PHYRX_OTHER_RECEIVE_INFO TLV parsing support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d939919a36f4606fb8312c502a53c0ecfbd85b2d Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:49 2025 +0530 wifi: ath12k: Add HAL_PHYRX_OTHER_RECEIVE_INFO TLV parsing support Currently, monitor is not enabled. However, in the future, the monitor will be enabled. Therefore, add the necessary HAL_PHYRX_OTHER_RECEIVE_INFO TLV parsing support in the monitor Rx path, which helps to populate the extended Rx statistics. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-5-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 7 +++++++ drivers/net/wireless/ath/ath12k/hal_rx.h | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 76b6d5861e25..0bd7dfef4310 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1599,6 +1599,13 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW); break; } + case HAL_PHYRX_OTHER_RECEIVE_INFO: { + const struct hal_phyrx_common_user_info *cmn_usr_info = tlv_data; + + ppdu_info->gi = le32_get_bits(cmn_usr_info->info0, + HAL_RX_PHY_CMN_USER_INFO0_GI); + break; + } case HAL_RX_PPDU_START_USER_INFO: ath12k_dp_mon_hal_rx_parse_user_info(tlv_data, userid, ppdu_info); break; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 765b53741861..445d1c33b41f 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -683,6 +683,14 @@ struct hal_rx_resp_req_info { #define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6) #define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) +#define HAL_RX_PHY_CMN_USER_INFO0_GI GENMASK(17, 16) + +struct hal_phyrx_common_user_info { + __le32 rsvd[2]; + __le32 info0; + __le32 rsvd1; +} __packed; + #define HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE GENMASK(3, 0) #define HAL_RX_EHT_SIG_NDP_CMN_INFO0_GI_LTF GENMASK(5, 4) #define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM GENMASK(8, 6) From 66b09c615a518ed0f222c2efe0648ec6f39a1138 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 170/465] wifi: ath12k: Update the peer id in PPDU end user stats TLV JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0cded0e413468183a3b2dd445ab3bdc4d4375967 Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:50 2025 +0530 wifi: ath12k: Update the peer id in PPDU end user stats TLV Currently, peer id get reported in the PPDU end user TLV tag. But the monitor status handler is inherited from ath11k, but it was not updated to incorporate the changes made to ath12k 802.11be hardware architecture. Therefore, update the peer id from the PPDU end user TLV data to get latest peer id update, it helps to populate accurate peer information on the statistics data. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-6-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 0bd7dfef4310..a99a124f413c 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1504,6 +1504,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, ppdu_info->num_mpdu_fcs_err = u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR); + ppdu_info->peer_id = + u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_PEER_ID); + switch (ppdu_info->preamble_type) { case HAL_RX_PREAMBLE_11N: ppdu_info->ht_flags = 1; From 7f4297ddef174310c46f3eb2fab447f88e2ff1dd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 171/465] wifi: ath12k: fix the ampdu id fetch in the HAL_RX_MPDU_START TLV JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dff4f278ee1ef12d822b7ed2a1048d27037209bb Author: P Praneesh Date: Thu Feb 6 07:08:51 2025 +0530 wifi: ath12k: fix the ampdu id fetch in the HAL_RX_MPDU_START TLV Currently, ampdu id is update with peer id mask which is incorrect. Therefore, update the ampdu id with PPDU id mask value. Also move the ampdu_id field inside the user stats since it is a user id based statistics. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-7-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_mon.c | 16 ++++++++++------ drivers/net/wireless/ath/ath12k/hal_rx.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index a99a124f413c..a7e85d131ce3 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_mon.h" @@ -1527,6 +1527,11 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, if (userid < HAL_MAX_UL_MU_USERS) { struct hal_rx_user_status *rxuser_stats = &ppdu_info->userstats[userid]; + + if (ppdu_info->num_mpdu_fcs_ok > 1 || + ppdu_info->num_mpdu_fcs_err > 1) + ppdu_info->userstats[userid].ampdu_present = true; + ppdu_info->num_users += 1; ath12k_dp_mon_rx_handle_ofdma_info(eu_stats, rxuser_stats); @@ -1638,8 +1643,8 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, if (userid < HAL_MAX_UL_MU_USERS) { info[0] = __le32_to_cpu(mpdu_start->info0); ppdu_info->userid = userid; - ppdu_info->ampdu_id[userid] = - u32_get_bits(info[0], HAL_RX_MPDU_START_INFO1_PEERID); + ppdu_info->userstats[userid].ampdu_id = + u32_get_bits(info[0], HAL_RX_MPDU_START_INFO0_PPDU_ID); } break; @@ -1857,15 +1862,14 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, { struct ieee80211_supported_band *sband; u8 *ptr = NULL; - u16 ampdu_id = ppduinfo->ampdu_id[ppduinfo->userid]; rxs->flag |= RX_FLAG_MACTIME_START; rxs->signal = ppduinfo->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; rxs->nss = ppduinfo->nss + 1; - if (ampdu_id) { + if (ppduinfo->userstats[ppduinfo->userid].ampdu_present) { rxs->flag |= RX_FLAG_AMPDU_DETAILS; - rxs->ampdu_reference = ampdu_id; + rxs->ampdu_reference = ppduinfo->userstats[ppduinfo->userid].ampdu_id; } if (ppduinfo->is_eht || ppduinfo->eht_usig) { diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 445d1c33b41f..163f9235fcf0 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -148,6 +148,8 @@ struct hal_rx_user_status { u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; u32 mpdu_ok_byte_count; u32 mpdu_err_byte_count; + bool ampdu_present; + u16 ampdu_id; }; #define HAL_MAX_UL_MU_USERS 37 @@ -263,7 +265,6 @@ struct hal_rx_mon_ppdu_info { u8 addr4[ETH_ALEN]; struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; u8 userid; - u16 ampdu_id[HAL_MAX_UL_MU_USERS]; bool first_msdu_in_mpdu; bool is_ampdu; u8 medium_prot_type; From 860b4924fb562b6e0286f479360a203882af45db Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 172/465] wifi: ath12k: Add EHT MCS support in Extended Rx statistics JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 03ac9e9144c59dcfaeb213f80e7e00974325045a Author: Balamurugan Mahalingam Date: Thu Feb 6 07:08:52 2025 +0530 wifi: ath12k: Add EHT MCS support in Extended Rx statistics Currently, EHT MCS information is not populated. Therefore, add the EHT MCS counter array to the peer rate statistics and update the EHT MCS statistics from the status TLV data in the monitor Rx path. In the future, this information will be used in the peer extended Rx statistics dump. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Balamurugan Mahalingam Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-8-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 2 ++ drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 3dcdedf9e9de..ef6ef6ad914d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -361,6 +361,7 @@ struct ath12k_vif_iter { #define HAL_RX_MAX_MCS_HT 31 #define HAL_RX_MAX_MCS_VHT 9 #define HAL_RX_MAX_MCS_HE 11 +#define HAL_RX_MAX_MCS_BE 15 #define HAL_RX_MAX_NSS 8 #define HAL_RX_MAX_NUM_LEGACY_RATES 12 #define ATH12K_RX_RATE_TABLE_11AX_NUM 576 @@ -370,6 +371,7 @@ struct ath12k_rx_peer_rate_stats { u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1]; u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1]; u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1]; + u64 be_mcs_count[HAL_RX_MAX_MCS_BE + 1]; u64 nss_count[HAL_RX_MAX_NSS]; u64 bw_count[HAL_RX_BW_MAX]; u64 gi_count[HAL_RX_GI_MAX]; diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index a7e85d131ce3..514bbd63db1c 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -3029,6 +3029,12 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, rx_stats->byte_stats.he_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; } + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE && + ppdu_info->mcs <= HAL_RX_MAX_MCS_BE) { + rx_stats->pkt_stats.be_mcs_count[ppdu_info->mcs] += num_msdu; + rx_stats->byte_stats.be_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; + } + if ((ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) && ppdu_info->rate < HAL_RX_LEGACY_RATE_INVALID) { From 0be33ad973a0923cb9dc1a3483fe145fae84fde2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 173/465] wifi: ath12k: Refactor the format of peer rate table information JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 98677080467c2a6529a3288f430acb8c44f583a0 Author: Balamurugan Mahalingam Date: Thu Feb 6 07:08:53 2025 +0530 wifi: ath12k: Refactor the format of peer rate table information Currently, peer rate table information involves complex computation for the rate index to update the rate table. To simplify this process, avoid the rate index calculation by defining the rate table with bandwidth, GI, NSS, MCS. Therefore, update the rate information based on the bandwidth, GI, NSS and MCS information from the TLV data of monitor status Rx path. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Balamurugan Mahalingam Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-9-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 6 +---- drivers/net/wireless/ath/ath12k/dp_mon.c | 29 +++++++++++------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ef6ef6ad914d..bc5b1b3a056c 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -53,8 +53,6 @@ #define ATH12K_INVALID_HW_MAC_ID 0xFF #define ATH12K_CONNECTION_LOSS_HZ (3 * HZ) -#define ATH12K_RX_RATE_TABLE_NUM 320 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 #define ATH12K_MON_TIMER_INTERVAL 10 #define ATH12K_RESET_TIMEOUT_HZ (20 * HZ) @@ -364,8 +362,6 @@ struct ath12k_vif_iter { #define HAL_RX_MAX_MCS_BE 15 #define HAL_RX_MAX_NSS 8 #define HAL_RX_MAX_NUM_LEGACY_RATES 12 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 -#define ATH12K_RX_RATE_TABLE_NUM 320 struct ath12k_rx_peer_rate_stats { u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1]; @@ -376,7 +372,7 @@ struct ath12k_rx_peer_rate_stats { u64 bw_count[HAL_RX_BW_MAX]; u64 gi_count[HAL_RX_GI_MAX]; u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES]; - u64 rx_rate[ATH12K_RX_RATE_TABLE_11AX_NUM]; + u64 rx_rate[HAL_RX_BW_MAX][HAL_RX_GI_MAX][HAL_RX_MAX_NSS][HAL_RX_MAX_MCS_HT + 1]; }; struct ath12k_rx_peer_stats { diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 514bbd63db1c..d22800e89485 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -2915,34 +2915,31 @@ ath12k_dp_mon_rx_update_peer_rate_table_stats(struct ath12k_rx_peer_stats *rx_st struct hal_rx_user_status *user_stats, u32 num_msdu) { - u32 rate_idx = 0; + struct ath12k_rx_peer_rate_stats *stats; u32 mcs_idx = (user_stats) ? user_stats->mcs : ppdu_info->mcs; u32 nss_idx = (user_stats) ? user_stats->nss - 1 : ppdu_info->nss - 1; u32 bw_idx = ppdu_info->bw; u32 gi_idx = ppdu_info->gi; + u32 len; - if ((mcs_idx > HAL_RX_MAX_MCS_HE) || (nss_idx >= HAL_RX_MAX_NSS) || - (bw_idx >= HAL_RX_BW_MAX) || (gi_idx >= HAL_RX_GI_MAX)) { + if (mcs_idx > HAL_RX_MAX_MCS_HT || nss_idx >= HAL_RX_MAX_NSS || + bw_idx >= HAL_RX_BW_MAX || gi_idx >= HAL_RX_GI_MAX) { return; } - if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N || - ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC) { - rate_idx = mcs_idx * 8 + 8 * 10 * nss_idx; - rate_idx += bw_idx * 2 + gi_idx; - } else if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX) { + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX || + ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE) gi_idx = ath12k_he_gi_to_nl80211_he_gi(ppdu_info->gi); - rate_idx = mcs_idx * 12 + 12 * 12 * nss_idx; - rate_idx += bw_idx * 3 + gi_idx; - } else { - return; - } - rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu; + rx_stats->pkt_stats.rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += num_msdu; + stats = &rx_stats->byte_stats; + if (user_stats) - rx_stats->byte_stats.rx_rate[rate_idx] += user_stats->mpdu_ok_byte_count; + len = user_stats->mpdu_ok_byte_count; else - rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; + len = ppdu_info->mpdu_len; + + stats->rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += len; } static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, From 47686292b688d218edd13f34e324e1ccd07a29e7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 174/465] wifi: ath12k: Add peer extended Rx statistics debugfs support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a412547f2a8bfcf6337691b6d498536f29dbf4b5 Author: Karthikeyan Periyasamy Date: Thu Feb 6 07:08:54 2025 +0530 wifi: ath12k: Add peer extended Rx statistics debugfs support Currently, peer extended Rx statistics are not supported. Therefore, expose peer extended Rx statistics support through debugfs, allowing users to enable or disable the collection of statistics information. After that the statistics information can be dumped through debugfs. Below are the debugfs commands exposed. Enable/Disable: echo <1/0> > /sys/kernel/debug/ieee80211/phyX/ath12k/ext_rx_stats Dump: cat /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/stations//rx_stats Sample output: ============== RX peer stats: Num of MSDUs: 1087 Num of MSDUs with TCP L4: 0 Num of MSDUs with UDP L4: 13 Num of other MSDUs: 1074 Num of MSDUs part of AMPDU: 363 Num of MSDUs not part of AMPDU: 724 Num of MSDUs using STBC: 0 Num of MSDUs beamformed: 0 Num of MPDUs with FCS ok: 695 Num of MPDUs with FCS error: 0 preamble: 11A 395 11B 0 11N 0 11AC 0 11AX 692 11BE 0 reception type: SU 1087 MU_MIMO 0 MU_OFDMA 0 MU_OFDMA_MIMO 0 TID(0-15) Legacy TID(16):690 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 395 RX Duration:39537 DCM: 0 RU26: 0 RU52: 0 RU106: 0 RU242: 0 RU484: 0 RU996: 0 RX success packet stats: EHT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 MCS 10: 0 MCS 11: 0 MCS 12: 0 MCS 13: 0 MCS 14: 0 MCS 15: 0 HE stats: MCS 0: 1 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 66 MCS 7: 46 MCS 8: 46 MCS 9: 34 MCS 10: 28 MCS 11: 471 VHT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 HT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 MCS 10: 0 MCS 11: 0 MCS 12: 0 MCS 13: 0 MCS 14: 0 MCS 15: 0 MCS 16: 0 MCS 17: 0 MCS 18: 0 MCS 19: 0 MCS 20: 0 MCS 21: 0 MCS 22: 0 MCS 23: 0 MCS 24: 0 MCS 25: 0 MCS 26: 0 MCS 27: 0 MCS 28: 0 MCS 29: 0 MCS 30: 0 MCS 31: 0 Legacy stats: 1 Mbps: 0 2 Mbps: 0 5.5 Mbps: 0 6 Mbps: 395 9 Mbps: 0 11 Mbps: 0 12 Mbps: 0 18 Mbps: 0 24 Mbps: 0 36 Mbps: 0 48 Mbps: 0 54 Mbps: 0 NSS stats: 1x1: 1086 2x2: 0 3x3: 0 4x4: 0 5x5: 0 6x6: 0 7x7: 0 8x8: 0 GI: 0.8 us 0 0.4 us 396 1.6 us 691 3.2 us 0 BW: 20 MHz 785 40 MHz 2 80 MHz 300 160 MHz 0 320 MHz 0 20 Mhz gi 1 us 1x1 : 6:5 7:3 8:3 9:4 10:4 11:374 12:391 40 Mhz gi 1 us 1x1 : 12:2 80 Mhz gi 1 us 1x1 : 6:61 7:43 8:43 9:30 10:24 11:97 12:2 RX success byte stats: EHT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 MCS 10: 0 MCS 11: 0 MCS 12: 0 MCS 13: 0 MCS 14: 0 MCS 15: 0 HE stats: MCS 0: 41 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 1435 MCS 7: 943 MCS 8: 697 MCS 9: 533 MCS 10: 492 MCS 11: 8159 VHT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 HT stats: MCS 0: 0 MCS 1: 0 MCS 2: 0 MCS 3: 0 MCS 4: 0 MCS 5: 0 MCS 6: 0 MCS 7: 0 MCS 8: 0 MCS 9: 0 MCS 10: 0 MCS 11: 0 MCS 12: 0 MCS 13: 0 MCS 14: 0 MCS 15: 0 MCS 16: 0 MCS 17: 0 MCS 18: 0 MCS 19: 0 MCS 20: 0 MCS 21: 0 MCS 22: 0 MCS 23: 0 MCS 24: 0 MCS 25: 0 MCS 26: 0 MCS 27: 0 MCS 28: 0 MCS 29: 0 MCS 30: 0 MCS 31: 0 Legacy stats: 1 Mbps: 0 2 Mbps: 0 5.5 Mbps: 0 6 Mbps: 16195 9 Mbps: 0 11 Mbps: 0 12 Mbps: 0 18 Mbps: 0 24 Mbps: 0 36 Mbps: 0 48 Mbps: 0 54 Mbps: 0 NSS stats: 1x1: 28454 2x2: 0 3x3: 0 4x4: 0 5x5: 0 6x6: 0 7x7: 0 8x8: 0 GI: 0.8 us 0 0.4 us 16236 1.6 us 12259 3.2 us 0 BW: 20 MHz 24108 40 MHz 82 80 MHz 4305 160 MHz 0 320 MHz 0 20 Mhz gi 1 us 1x1 : 6:205 7:123 8:123 9:164 10:164 11:7257 12:16031 40 Mhz gi 1 us 1x1 : 12:82 80 Mhz gi 1 us 1x1 : 6:1230 7:820 8:574 9:369 10:328 11:902 12:82 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: P Praneesh Signed-off-by: P Praneesh Co-developed-by: Balamurugan Mahalingam Signed-off-by: Balamurugan Mahalingam Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250206013854.174765-10-quic_periyasa@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/Makefile | 2 +- drivers/net/wireless/ath/ath12k/core.h | 2 + drivers/net/wireless/ath/ath12k/debugfs.c | 97 +++++ drivers/net/wireless/ath/ath12k/debugfs.h | 21 +- drivers/net/wireless/ath/ath12k/debugfs_sta.c | 337 ++++++++++++++++++ drivers/net/wireless/ath/ath12k/debugfs_sta.h | 24 ++ drivers/net/wireless/ath/ath12k/hal_rx.h | 3 - drivers/net/wireless/ath/ath12k/mac.c | 18 +- drivers/net/wireless/ath/ath12k/mac.h | 4 +- 9 files changed, 497 insertions(+), 11 deletions(-) create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_sta.c create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_sta.h diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index 4a7f5e87384c..60644cb42c76 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -23,7 +23,7 @@ ath12k-y += core.o \ fw.o \ p2p.o -ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o +ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o ath12k-$(CONFIG_PM) += wow.o diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index bc5b1b3a056c..b60a636a6e19 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -581,6 +581,8 @@ struct ath12k_debug { bool tpc_request; struct completion tpc_complete; struct wmi_tpc_stats_arg *tpc_stats; + u32 rx_filter; + bool extd_rx_stats; }; struct ath12k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index d38ece46f1d0..57002215ddf1 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -5,6 +5,7 @@ */ #include "core.h" +#include "dp_tx.h" #include "debug.h" #include "debugfs.h" #include "debugfs_htt_stats.h" @@ -740,6 +741,98 @@ static const struct file_operations fops_tpc_stats_type = { .llseek = default_llseek, }; +static ssize_t ath12k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 ring_id, rx_filter = 0; + bool enable; + int ret, i; + + if (kstrtobool_from_user(ubuf, count, &enable)) + return -EINVAL; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + if (!ar->ab->hw_params->rxdma1_enable) { + ret = count; + goto exit; + } + + if (ar->ah->state != ATH12K_HW_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath12k_mac_mon_status_filter_default; + } + + ar->debug.rx_filter = tlv_filter.rx_filter; + + for (i = 0; i < ar->ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } + } + + ar->debug.extd_rx_stats = !!enable; + ret = count; +exit: + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + return ret; +} + +static ssize_t ath12k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + char buf[32]; + int len = 0; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath12k_read_extd_rx_stats, + .write = ath12k_write_extd_rx_stats, + .open = simple_open, +}; + void ath12k_debugfs_soc_create(struct ath12k_base *ab) { bool dput_needed; @@ -1184,6 +1277,10 @@ void ath12k_debugfs_register(struct ath12k *ar) ath12k_debugfs_htt_stats_register(ar); ath12k_debugfs_fw_stats_register(ar); + + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); } void ath12k_debugfs_unregister(struct ath12k *ar) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index fb85113fc625..d7041297d5d8 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -16,6 +16,16 @@ void ath12k_debugfs_fw_stats_process(struct ath12k *ar, struct ath12k_fw_stats *stats); void ath12k_debugfs_fw_stats_reset(struct ath12k *ar); +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return ar->debug.extd_rx_stats; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return ar->debug.rx_filter; +} + #define ATH12K_CCK_RATES 4 #define ATH12K_OFDM_RATES 8 #define ATH12K_HT_RATES 8 @@ -97,7 +107,6 @@ enum ath12k_debug_tpc_stats_support_modes { ATH12K_TPC_STATS_SUPPORT_BE, ATH12K_TPC_STATS_SUPPORT_BE_PUNC, }; - #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { @@ -123,6 +132,16 @@ static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar, static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) { } + +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return false; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return 0; +} #endif /* CONFIG_ATH12K_DEBUGFS */ #endif /* _ATH12K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.c b/drivers/net/wireless/ath/ath12k/debugfs_sta.c new file mode 100644 index 000000000000..5bd2bf4c9dac --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#include "debugfs_sta.h" +#include "core.h" +#include "peer.h" +#include "debug.h" +#include "debugfs_htt_stats.h" +#include "debugfs.h" + +static +u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size, + bool he_rates_avail, + const struct ath12k_rx_peer_rate_stats *stats) +{ + static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = { + "1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps", + "9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps", + "24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"}; + u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS; + int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0; + u32 i, len = offset, max = max_bw * max_gi * max_mcs; + bool found; + + len += scnprintf(buf + len, size - len, "\nEHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->be_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHE stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->he_mcs_count[i], + (i + 1) % 6 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nVHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->vht_mcs_count[i], + (i + 1) % 5 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->ht_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nLegacy stats:\n"); + for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) + len += scnprintf(buf + len, size - len, + "%s: %llu%s", legacy_rate_str[i], + stats->legacy_count[i], + (i + 1) % 4 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nNSS stats:\n"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + len += scnprintf(buf + len, size - len, + "%dx%d: %llu ", i + 1, i + 1, + stats->nss_count[i]); + + len += scnprintf(buf + len, size - len, + "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n", + stats->gi_count[0], + stats->gi_count[1], + stats->gi_count[2], + stats->gi_count[3]); + + len += scnprintf(buf + len, size - len, + "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n", + stats->bw_count[0], + stats->bw_count[1], + stats->bw_count[2], + stats->bw_count[3], + stats->bw_count[4]); + + for (i = 0; i < max; i++) { + found = false; + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) { + found = true; + break; + } + } + + if (!found) + goto skip_report; + + switch (bw) { + case HAL_RX_BW_20MHZ: + bw_num = 20; + break; + case HAL_RX_BW_40MHZ: + bw_num = 40; + break; + case HAL_RX_BW_80MHZ: + bw_num = 80; + break; + case HAL_RX_BW_160MHZ: + bw_num = 160; + break; + case HAL_RX_BW_320MHZ: + bw_num = 320; + break; + } + + len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ", + bw_num, gi, nss + 1, nss + 1); + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) + len += scnprintf(buf + len, size - len, + " %d:%llu", mcs, + stats->rx_rate[bw][gi][nss][mcs]); + } + +skip_report: + if (nss++ >= max_mcs - 1) { + nss = 0; + if (gi++ >= max_gi - 1) { + gi = 0; + if (bw < max_bw - 1) + bw++; + } + } + } + + len += scnprintf(buf + len, size - len, "\n"); + + return len - offset; +} + +static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + const int size = ATH12K_STA_RX_STATS_BUF_SIZE; + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + int len = 0, i, ret = 0; + bool he_rates_avail; + struct ath12k *ar; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + ar = arsta->arvif->ar; + + u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) { + ret = -ENOENT; + goto out; + } + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + ret = -ENOENT; + goto unlock; + } + + len += scnprintf(buf + len, size - len, "RX peer stats:\n\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n", + rx_stats->other_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + + he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false; + + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + + len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n", + rx_stats->rx_duration); + + len += scnprintf(buf + len, size - len, + "\nDCM: %llu\nRU26: %llu\nRU52: %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n", + rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], + rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], + rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], + rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]); + + len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->pkt_stats); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->byte_stats); + +unlock: + spin_unlock_bh(&ar->ab->base_lock); + + if (len) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_rx_stats = { + .read = ath12k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + struct ath12k *ar; + bool reset; + int ret; + + ret = kstrtobool_from_user(buf, count, &reset); + if (ret) + return ret; + + if (!reset) + return -EINVAL; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + ret = -ENOENT; + goto out; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + ret = -ENOENT; + goto out; + } + + ar = arsta->arvif->ar; + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + spin_unlock_bh(&ar->ab->base_lock); + ret = -ENOENT; + goto out; + } + + memset(rx_stats, 0, sizeof(*rx_stats)); + spin_unlock_bh(&ar->ab->base_lock); + + ret = count; +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_reset_rx_stats = { + .write = ath12k_dbg_sta_reset_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + struct ath12k *ar; + + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id); + if (!ar) + return; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) { + debugfs_create_file("rx_stats", 0400, dir, link_sta, + &fops_rx_stats); + debugfs_create_file("reset_rx_stats", 0200, dir, link_sta, + &fops_reset_rx_stats); + } +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h new file mode 100644 index 000000000000..8de924f4d7d5 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _ATH12K_DEBUGFS_STA_H_ +#define _ATH12K_DEBUGFS_STA_H_ + +#include + +#include "core.h" + +#define ATH12K_STA_RX_STATS_BUF_SIZE (1024 * 16) + +#ifdef CONFIG_ATH12K_DEBUGFS + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); + +#endif /* CONFIG_ATH12K_DEBUGFS */ + +#endif /* _ATH12K_DEBUGFS_STA_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index 163f9235fcf0..6bdcd0867d86 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -22,9 +22,6 @@ struct hal_rx_wbm_rel_info { #define HAL_INVALID_PEERID 0x3fff #define VHT_SIG_SU_NSS_MASK 0x7 -#define HAL_RX_MAX_MCS 12 -#define HAL_RX_MAX_NSS 8 - #define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \ le32_get_bits((__val), GENMASK(7, 0)) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index fc5cfd860d4d..664b13c25965 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -20,6 +20,7 @@ #include "debugfs.h" #include "hif.h" #include "wow.h" +#include "debugfs_sta.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ @@ -805,9 +806,9 @@ static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw, return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan); } -static struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u8 link_id) +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id) { struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_hw *ah = ath12k_hw_to_ah(hw); @@ -7344,10 +7345,14 @@ static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) if (!ab->hw_params->rxdma1_enable) return ret; - if (enable) + if (enable) { tlv_filter = ath12k_mac_mon_status_filter_default; - else + + if (ath12k_debugfs_rx_filter(ar)) + tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); + } else { tlv_filter.rxmon_disable = true; + } for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; @@ -10451,6 +10456,9 @@ static const struct ieee80211_ops ath12k_ops = { .set_wakeup = ath12k_wow_op_set_wakeup, #endif CFG80211_TESTMODE_CMD(ath12k_tm_cmd) +#ifdef CONFIG_ATH12K_DEBUGFS + .link_sta_add_debugfs = ath12k_debugfs_link_sta_op_add, +#endif }; static void ath12k_mac_update_ch_list(struct ath12k *ar, diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 5a6e3c3316be..ae35b73312bf 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -112,5 +112,7 @@ u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones); enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones); enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi); struct ieee80211_bss_conf *ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif); - +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id); #endif From 4afea3b0abcb024bb33456e6085ad2e7b58e51c6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 175/465] wifi: ath12k: eliminate redundant debug mask check in ath12k_dbg() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cc5faf08aa3afc6f60d7df1a0edf041c4384fc95 Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:07 2025 +0530 wifi: ath12k: eliminate redundant debug mask check in ath12k_dbg() The current implementation includes a debug mask check both in the macro expansion and in the function __ath12k_dbg(), which is unnecessary. Simplify the code by removing the redundant check from the helper function __ath12k_dbg(). While at this, rename the first argument in macro from ar to ab since the first argument name in the function __ath12k_dbg() is ab. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-1-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/debug.c | 5 ++--- drivers/net/wireless/ath/ath12k/debug.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debug.c b/drivers/net/wireless/ath/ath12k/debug.c index ff6eaeafa092..fd9796b5ad3b 100644 --- a/drivers/net/wireless/ath/ath12k/debug.c +++ b/drivers/net/wireless/ath/ath12k/debug.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -63,8 +63,7 @@ void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask, vaf.fmt = fmt; vaf.va = &args; - if (ath12k_debug_mask & mask) - dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); /* TODO: trace log */ diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index 0aa7c8ccb14c..ba0e4da3bb76 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -62,11 +62,11 @@ static inline void ath12k_dbg_dump(struct ath12k_base *ab, } #endif /* CONFIG_ATH12K_DEBUG */ -#define ath12k_dbg(ar, dbg_mask, fmt, ...) \ +#define ath12k_dbg(ab, dbg_mask, fmt, ...) \ do { \ typeof(dbg_mask) mask = (dbg_mask); \ if (ath12k_debug_mask & mask) \ - __ath12k_dbg(ar, mask, fmt, ##__VA_ARGS__); \ + __ath12k_dbg(ab, mask, fmt, ##__VA_ARGS__); \ } while (0) #endif /* _ATH12K_DEBUG_H_ */ From 35ce2cb8c96c3f61cfd47162a6596db93c29ef92 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 176/465] wifi: ath12k: introduce ath12k_generic_dbg() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 80f816d07ba0cf80a9b6aa90a30af2b2073c6cb7 Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:08 2025 +0530 wifi: ath12k: introduce ath12k_generic_dbg() There might be instances where ath12k_dbg() is needed, but access to struct ath12k_base (ab) is not readily available. To address this, add support to print the debug message using printk() when ab is not present. To avoid the need to explicitly pass NULL each time, introduce a new macro ath12k_generic_dbg() which resolves to ath12k_dbg() with ab set to NULL. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-2-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/debug.c | 5 ++++- drivers/net/wireless/ath/ath12k/debug.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debug.c b/drivers/net/wireless/ath/ath12k/debug.c index fd9796b5ad3b..5ce100cd9a9d 100644 --- a/drivers/net/wireless/ath/ath12k/debug.c +++ b/drivers/net/wireless/ath/ath12k/debug.c @@ -63,7 +63,10 @@ void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask, vaf.fmt = fmt; vaf.va = &args; - dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + if (likely(ab)) + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + else + printk(KERN_DEBUG "ath12k: %pV", &vaf); /* TODO: trace log */ diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index ba0e4da3bb76..48916e4e1f60 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -69,4 +69,7 @@ do { \ __ath12k_dbg(ab, mask, fmt, ##__VA_ARGS__); \ } while (0) +#define ath12k_generic_dbg(dbg_mask, fmt, ...) \ + ath12k_dbg(NULL, dbg_mask, fmt, ##__VA_ARGS__) + #endif /* _ATH12K_DEBUG_H_ */ From e3bedda5e083792bca4be90be7d963e237d17a10 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 177/465] wifi: ath12k: remove redundant vif settings during link interface creation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ab6270c4dec3b1a506291c3b0050230b32464781 Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:09 2025 +0530 wifi: ath12k: remove redundant vif settings during link interface creation Currently, vif level settings are done in ath12k_mac_assign_link_vif() as well as in ath12k_mac_op_add_interface(). Since it is vif level settings, doing this on per link does not make sense and it contributes to redundant code. Get rid of this redundant code from ath12k_mac_assign_link_vif(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-3-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 664b13c25965..7168b268b4a1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -4023,13 +4023,6 @@ static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, sizeof(arvif->bitrate_mask.control[i].vht_mcs)); } - /* Allocate Default Queue now and reassign during actual vdev create */ - vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; - for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) - vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE; - - vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; - rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); ahvif->links_map |= BIT(link_id); synchronize_rcu(); From 0fa233e160bc8bb6013e27d693276bdc4d76de18 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 178/465] wifi: ath12k: remove redundant logic for initializing arvif JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8acc7ec53ac43aea8f49573b5a173570f308b775 Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:10 2025 +0530 wifi: ath12k: remove redundant logic for initializing arvif The current logic for initializing arvif is present in both the add interface operation callback and ath12k_mac_assign_link_vif(). The former handles deflink initialization, while the latter is responsible for other links. This redundancy could be avoided by using a common helper function. Hence, add a new helper ath12k_mac_init_arvif() which initializes a given arvif. Since synchronizing rcu is not required after adding a rcu pointer, remove that now. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-4-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 80 ++++++++++++++++----------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 7168b268b4a1..b7f605eeb254 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3974,13 +3974,59 @@ static void ath12k_mac_op_link_info_changed(struct ieee80211_hw *hw, ath12k_mac_bss_info_changed(ar, arvif, info, changed); } +static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif, + struct ath12k_link_vif *arvif, int link_id) +{ + struct ath12k_hw *ah = ahvif->ah; + u8 _link_id; + int i; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (WARN_ON(!arvif)) + return; + + if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) + return; + + if (link_id < 0) + _link_id = 0; + else + _link_id = link_id; + + arvif->ahvif = ahvif; + arvif->link_id = _link_id; + + INIT_LIST_HEAD(&arvif->list); + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath12k_mac_vif_sta_connection_loss_work); + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + /* Handle MLO related assignments */ + if (link_id >= 0) { + rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); + ahvif->links_map |= BIT(_link_id); + } + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x", + _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr, + ahvif->links_map); +} + static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, struct ieee80211_vif *vif, u8 link_id) { struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_link_vif *arvif; - int i; lockdep_assert_wiphy(ah->hw->wiphy); @@ -4007,25 +4053,8 @@ static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, } } - arvif->ahvif = ahvif; - arvif->link_id = link_id; - ahvif->links_map |= BIT(link_id); + ath12k_mac_init_arvif(ahvif, arvif, link_id); - INIT_LIST_HEAD(&arvif->list); - INIT_DELAYED_WORK(&arvif->connection_loss_work, - ath12k_mac_vif_sta_connection_loss_work); - - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); - } - - rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); - ahvif->links_map |= BIT(link_id); - synchronize_rcu(); return arvif; } @@ -8318,19 +8347,8 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, ahvif->ah = ah; ahvif->vif = vif; arvif = &ahvif->deflink; - arvif->ahvif = ahvif; - INIT_LIST_HEAD(&arvif->list); - INIT_DELAYED_WORK(&arvif->connection_loss_work, - ath12k_mac_vif_sta_connection_loss_work); - - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); - } + ath12k_mac_init_arvif(ahvif, arvif, -1); /* Allocate Default Queue now and reassign during actual vdev create */ vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; From 4f69684c4433b2e8ad7cb84f5e89e173e7610807 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 179/465] wifi: ath12k: use arvif instead of link_conf in ath12k_mac_set_key() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 00e4dc11695d48322780812b503314682659e98b Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:11 2025 +0530 wifi: ath12k: use arvif instead of link_conf in ath12k_mac_set_key() Currently, in ath12k_mac_set_key(), if sta is not present, the address is retrieved from link_conf's bssid or addr member, depending on the interface type. When operating as an ML station and during shutdown, link_conf will not be available. This can result in the following error: ath12k_pci 0004:01:00.0: unable to access bss link conf in set key for vif AA:BB:CC:DD:EE:FF link 1 The primary purpose of accessing link_conf is to obtain the address for finding the peer. However, since arvif is always valid in this call, it can be used instead. Add change to use arvif instead of link_conf. A subsequent change will expose this issue but since tear down will give error, this is included first. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-5-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b7f605eeb254..97d7f95a3796 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -4668,9 +4668,6 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, struct ath12k_link_sta *arsta, struct ieee80211_key_conf *key) { - struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); - struct ieee80211_bss_conf *link_conf; struct ieee80211_sta *sta = NULL; struct ath12k_base *ab = ar->ab; struct ath12k_peer *peer; @@ -4687,19 +4684,10 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) return 1; - link_conf = ath12k_mac_get_link_bss_conf(arvif); - if (!link_conf) { - ath12k_warn(ab, "unable to access bss link conf in set key for vif %pM link %u\n", - vif->addr, arvif->link_id); - return -ENOLINK; - } - if (sta) peer_addr = arsta->addr; - else if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) - peer_addr = link_conf->bssid; else - peer_addr = link_conf->addr; + peer_addr = arvif->bssid; key->hw_key_idx = key->keyidx; From 0f876aec4198783b58658ada439e05a7bddfcd55 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 180/465] wifi: ath12k: relocate a few functions in mac.c JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b81c39d67fbfcaccb33a1fc5494a87b29f2724ab Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:12 2025 +0530 wifi: ath12k: relocate a few functions in mac.c An upcoming change will invoke ath12k_mac_init_arvif(), ath12k_mac_assign_link_vif(), ath12k_mac_unassign_link_vif(), and ath12k_mac_remove_link_interface() from a line located above their current definition. Hence, relocate these functions to above so that these can be invoked later on. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Signed-off-by: Aditya Kumar Singh Reviewed-by: Vasanthakumar Thiagarajan Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-6-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 250 +++++++++++++------------- 1 file changed, 125 insertions(+), 125 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 97d7f95a3796..493d3d43b121 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3470,6 +3470,131 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret); } +static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif, + struct ath12k_link_vif *arvif, int link_id) +{ + struct ath12k_hw *ah = ahvif->ah; + u8 _link_id; + int i; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (WARN_ON(!arvif)) + return; + + if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) + return; + + if (link_id < 0) + _link_id = 0; + else + _link_id = link_id; + + arvif->ahvif = ahvif; + arvif->link_id = _link_id; + + INIT_LIST_HEAD(&arvif->list); + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath12k_mac_vif_sta_connection_loss_work); + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + /* Handle MLO related assignments */ + if (link_id >= 0) { + rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); + ahvif->links_map |= BIT(_link_id); + } + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x", + _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr, + ahvif->links_map); +} + +static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw, + struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = arvif->ar; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + cancel_delayed_work_sync(&arvif->connection_loss_work); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)", + arvif->vdev_id, arvif->link_id); + + if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); + if (ret) + ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d", + arvif->vdev_id, arvif->link_id, ret); + } + ath12k_mac_vdev_delete(ar, arvif); +} + +static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, + struct ieee80211_vif *vif, + u8 link_id) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(ah->hw->wiphy); + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (arvif) + return arvif; + + if (!vif->valid_links) { + /* Use deflink for Non-ML VIFs and mark the link id as 0 + */ + link_id = 0; + arvif = &ahvif->deflink; + } else { + /* If this is the first link arvif being created for an ML VIF + * use the preallocated deflink memory except for scan arvifs + */ + if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { + arvif = &ahvif->deflink; + } else { + arvif = (struct ath12k_link_vif *) + kzalloc(sizeof(struct ath12k_link_vif), GFP_KERNEL); + if (!arvif) + return NULL; + } + } + + ath12k_mac_init_arvif(ahvif, arvif, link_id); + + return arvif; +} + +static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = ahvif->ah; + + lockdep_assert_wiphy(ah->hw->wiphy); + + rcu_assign_pointer(ahvif->link[arvif->link_id], NULL); + synchronize_rcu(); + ahvif->links_map &= ~BIT(arvif->link_id); + + if (arvif != &ahvif->deflink) + kfree(arvif); + else + memset(arvif, 0, sizeof(*arvif)); +} + static int ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3974,131 +4099,6 @@ static void ath12k_mac_op_link_info_changed(struct ieee80211_hw *hw, ath12k_mac_bss_info_changed(ar, arvif, info, changed); } -static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif, - struct ath12k_link_vif *arvif, int link_id) -{ - struct ath12k_hw *ah = ahvif->ah; - u8 _link_id; - int i; - - lockdep_assert_wiphy(ah->hw->wiphy); - - if (WARN_ON(!arvif)) - return; - - if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) - return; - - if (link_id < 0) - _link_id = 0; - else - _link_id = link_id; - - arvif->ahvif = ahvif; - arvif->link_id = _link_id; - - INIT_LIST_HEAD(&arvif->list); - INIT_DELAYED_WORK(&arvif->connection_loss_work, - ath12k_mac_vif_sta_connection_loss_work); - - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); - } - - /* Handle MLO related assignments */ - if (link_id >= 0) { - rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); - ahvif->links_map |= BIT(_link_id); - } - - ath12k_generic_dbg(ATH12K_DBG_MAC, - "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x", - _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr, - ahvif->links_map); -} - -static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, - struct ieee80211_vif *vif, - u8 link_id) -{ - struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); - struct ath12k_link_vif *arvif; - - lockdep_assert_wiphy(ah->hw->wiphy); - - arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); - if (arvif) - return arvif; - - if (!vif->valid_links) { - /* Use deflink for Non-ML VIFs and mark the link id as 0 - */ - link_id = 0; - arvif = &ahvif->deflink; - } else { - /* If this is the first link arvif being created for an ML VIF - * use the preallocated deflink memory except for scan arvifs - */ - if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { - arvif = &ahvif->deflink; - } else { - arvif = (struct ath12k_link_vif *) - kzalloc(sizeof(struct ath12k_link_vif), GFP_KERNEL); - if (!arvif) - return NULL; - } - } - - ath12k_mac_init_arvif(ahvif, arvif, link_id); - - return arvif; -} - -static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif) -{ - struct ath12k_vif *ahvif = arvif->ahvif; - struct ath12k_hw *ah = ahvif->ah; - - lockdep_assert_wiphy(ah->hw->wiphy); - - rcu_assign_pointer(ahvif->link[arvif->link_id], NULL); - synchronize_rcu(); - ahvif->links_map &= ~BIT(arvif->link_id); - - if (arvif != &ahvif->deflink) - kfree(arvif); - else - memset(arvif, 0, sizeof(*arvif)); -} - -static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw, - struct ath12k_link_vif *arvif) -{ - struct ath12k_vif *ahvif = arvif->ahvif; - struct ath12k_hw *ah = hw->priv; - struct ath12k *ar = arvif->ar; - int ret; - - lockdep_assert_wiphy(ah->hw->wiphy); - - cancel_delayed_work_sync(&arvif->connection_loss_work); - - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)", - arvif->vdev_id, arvif->link_id); - - if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { - ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); - if (ret) - ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d", - arvif->vdev_id, arvif->link_id, ret); - } - ath12k_mac_vdev_delete(ar, arvif); -} - static struct ath12k* ath12k_mac_select_scan_device(struct ieee80211_hw *hw, struct ieee80211_vif *vif, From f7756d3bb0692d93b41362f823bc6d30445da990 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 181/465] wifi: ath12k: allocate new links in change_vif_links() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 315d80be304ac1d47da38c2b0c85286c9220c23a Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:13 2025 +0530 wifi: ath12k: allocate new links in change_vif_links() Currently, links in an interface are allocated during channel assignment via assign_vif_chanctx(). Conversely, links are deleted during channel unassignment via unassign_vif_chanctx(). However, deleting links during channel unassignment does not comply with mac80211 link handling. Therefore, this process should be managed within change_vif_links(). To maintain symmetry, link addition should also be handled in change_vif_links(). Hence, add changes to allocate link arvif in change_vif_links(). Creating the link interface on firmware will still be done during channel assignment. And since link will be created but channel might not be assigned, there is a need now to test is_created flag in ath12k_mac_mlo_get_vdev_args() before accessing link_conf or else link bring up will fail. A subsequent change will handle link removal part. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Aditya Kumar Singh Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-7-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 493d3d43b121..156ea7a975d9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3601,6 +3601,31 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, u16 old_links, u16 new_links, struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS]) { + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long to_add = ~old_links & new_links; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_link_vif *arvif; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac vif link changed for MLD %pM old_links 0x%x new_links 0x%x\n", + vif->addr, old_links, new_links); + + for_each_set_bit(link_id, &to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + /* mac80211 wants to add link but driver already has the + * link. This should not happen ideally. + */ + if (WARN_ON(arvif)) + return -EINVAL; + + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + if (WARN_ON(!arvif)) + return -EINVAL; + } + return 0; } @@ -8771,6 +8796,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, if (arvif == arvif_p) continue; + if (!arvif_p->is_created) + continue; + link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, ahvif->vif->link_conf[arvif_p->link_id]); From 3ba6d48c02d55d96b644fe15e226a53ad62826cb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:41 +0200 Subject: [PATCH 182/465] wifi: ath12k: handle link removal in change_vif_links() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 81e4be30544ee7e8da80e9aae7acd69d3be6d05a Author: Aditya Kumar Singh Date: Tue Feb 4 22:35:14 2025 +0530 wifi: ath12k: handle link removal in change_vif_links() Currently, the link interface is deleted during channel unassignment, which does not align with mac80211 link handling. Therefore, add changes to only perform vdev stop during channel unassignment. The actual vdev deletion will occur in change_vif_links(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1.97421.5 # Nicolas Escande Signed-off-by: Aditya Kumar Singh Reviewed-by: Vasanthakumar Thiagarajan Tested-by: Nicolas Escande Link: https://patch.msgid.link/20250204-unlink_link_arvif_from_chanctx-v2-8-764fb5973c1a@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 156ea7a975d9..c9f7bea4e04a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3602,6 +3602,7 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS]) { struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long to_remove = old_links & ~new_links; unsigned long to_add = ~old_links & new_links; struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k_link_vif *arvif; @@ -3626,6 +3627,21 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, return -EINVAL; } + for_each_set_bit(link_id, &to_remove, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (WARN_ON(!arvif)) + return -EINVAL; + + if (!arvif->is_created) + continue; + + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } + return 0; } @@ -9444,9 +9460,6 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ar->num_started_vdevs == 1 && ar->monitor_vdev_created) ath12k_mac_monitor_stop(ar); - - ath12k_mac_remove_link_interface(hw, arvif); - ath12k_mac_unassign_link_vif(arvif); } static int From 7b7cc76357c2d7d7399fe88da16d728bd4bfc158 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 183/465] wifi: ath11k: fix RCU stall while reaping monitor destination ring JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2024-58097 commit 16c6c35c03ea73054a1f6d3302a4ce4a331b427d Author: P Praneesh Date: Thu Dec 19 19:05:30 2024 +0800 wifi: ath11k: fix RCU stall while reaping monitor destination ring While processing the monitor destination ring, MSDUs are reaped from the link descriptor based on the corresponding buf_id. However, sometimes the driver cannot obtain a valid buffer corresponding to the buf_id received from the hardware. This causes an infinite loop in the destination processing, resulting in a kernel crash. kernel log: ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309 ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309 ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed Fix this by skipping the problematic buf_id and reaping the next entry, replacing the break with the next MSDU processing. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: P Praneesh Signed-off-by: Kang Yang Acked-by: Kalle Valo Acked-by: Jeff Johnson Link: https://patch.msgid.link/20241219110531.2096-2-quic_kangyang@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/dp_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 640d617a585a..d010b25b8503 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4772,7 +4772,7 @@ u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id, if (!msdu) { ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "msdu_pop: invalid buf_id %d\n", buf_id); - break; + goto next_msdu; } rxcb = ATH11K_SKB_RXCB(msdu); if (!rxcb->unmapped) { @@ -5399,7 +5399,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, "full mon msdu_pop: invalid buf_id %d\n", buf_id); spin_unlock_bh(&rx_ring->idr_lock); - break; + goto next_msdu; } idr_remove(&rx_ring->bufs_idr, buf_id); spin_unlock_bh(&rx_ring->idr_lock); From ab9489011bc5c80eb1ef4bfc17448628c4a039ef Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 184/465] wifi: ath11k: add srng->lock for ath11k_hal_srng_* in monitor mode JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2024-58096 commit 63b7af49496d0e32f7a748b6af3361ec138b1bd3 Author: Kang Yang Date: Thu Dec 19 19:05:31 2024 +0800 wifi: ath11k: add srng->lock for ath11k_hal_srng_* in monitor mode ath11k_hal_srng_* should be used with srng->lock to protect srng data. For ath11k_dp_rx_mon_dest_process() and ath11k_dp_full_mon_process_rx(), they use ath11k_hal_srng_* for many times but never call srng->lock. So when running (full) monitor mode, warning will occur: RIP: 0010:ath11k_hal_srng_dst_peek+0x18/0x30 [ath11k] Call Trace: ? ath11k_hal_srng_dst_peek+0x18/0x30 [ath11k] ath11k_dp_rx_process_mon_status+0xc45/0x1190 [ath11k] ? idr_alloc_u32+0x97/0xd0 ath11k_dp_rx_process_mon_rings+0x32a/0x550 [ath11k] ath11k_dp_service_srng+0x289/0x5a0 [ath11k] ath11k_pcic_ext_grp_napi_poll+0x30/0xd0 [ath11k] __napi_poll+0x30/0x1f0 net_rx_action+0x198/0x320 __do_softirq+0xdd/0x319 So add srng->lock for them to avoid such warnings. Inorder to fetch the srng->lock, should change srng's definition from 'void' to 'struct hal_srng'. And initialize them elsewhere to prevent one line of code from being too long. This is consistent with other ring process functions, such as ath11k_dp_process_rx(). Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Kang Yang Acked-by: Jeff Johnson Link: https://patch.msgid.link/20241219110531.2096-3-quic_kangyang@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/dp_rx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index d010b25b8503..f2bdbac2a0b7 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -5137,7 +5137,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; const struct ath11k_hw_hal_params *hal_params; void *ring_entry; - void *mon_dst_srng; + struct hal_srng *mon_dst_srng; u32 ppdu_id; u32 rx_bufs_used; u32 ring_id; @@ -5154,6 +5154,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, spin_lock_bh(&pmon->mon_lock); + spin_lock_bh(&mon_dst_srng->lock); ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); ppdu_id = pmon->mon_ppdu_info.ppdu_id; @@ -5212,6 +5213,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, mon_dst_srng); } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); @@ -5601,7 +5603,7 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, struct hal_sw_mon_ring_entries *sw_mon_entries; struct ath11k_pdev_mon_stats *rx_mon_stats; struct sk_buff *head_msdu, *tail_msdu; - void *mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + struct hal_srng *mon_dst_srng; void *ring_entry; u32 rx_bufs_used = 0, mpdu_rx_bufs_used; int quota = 0, ret; @@ -5617,6 +5619,9 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, goto reap_status_ring; } + mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + spin_lock_bh(&mon_dst_srng->lock); + ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { head_msdu = NULL; @@ -5660,6 +5665,7 @@ next_entry: } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); if (rx_bufs_used) { From 6d1449e8fbab63b277ed723b0cf58b63521e29b2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 185/465] wifi: ath12k: Update HTT_TCL_METADATA version and bit mask definitions JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5d964966bd3ff48af92d941e1dcb581e6e572642 Author: Balamurugan Mahalingam Date: Mon Feb 3 13:26:46 2025 -0800 wifi: ath12k: Update HTT_TCL_METADATA version and bit mask definitions Update the HTT_TCL_METADATA version to the latest version (2) as the bit definitions have changed a little to support more features. This new version allows the host to submit a packet with more information to the firmware. Firmware uses this additional information to do special processing for certain frames. All the firmware binaries available in upstream/public are compatible with this HTT version update. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Balamurugan Mahalingam Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250203212647.2694566-2-quic_bmahalin@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp.h | 21 ++++++++++++++------- drivers/net/wireless/ath/ath12k/dp_tx.c | 11 +++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index f68bb78d4a11..7cdc62aa35be 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_H @@ -372,17 +372,18 @@ struct ath12k_dp { }; /* HTT definitions */ +#define HTT_TAG_TCL_METADATA_VERSION 5 -#define HTT_TCL_META_DATA_TYPE BIT(0) -#define HTT_TCL_META_DATA_VALID_HTT BIT(1) +#define HTT_TCL_META_DATA_TYPE GENMASK(1, 0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(2) /* vdev meta data */ -#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) -#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) -#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(10, 3) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(12, 11) +#define HTT_TCL_META_DATA_HOST_INSPECTED_MISSION BIT(13) /* peer meta data */ -#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 3) /* HTT tx completion is overlaid in wbm_release_ring */ #define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13) @@ -413,9 +414,15 @@ enum htt_h2t_msg_type { }; #define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) +#define HTT_OPTION_TCL_METADATA_VER_V2 2 +#define HTT_OPTION_TAG GENMASK(7, 0) +#define HTT_OPTION_LEN GENMASK(15, 8) +#define HTT_OPTION_VALUE GENMASK(31, 16) +#define HTT_TCL_METADATA_VER_SZ 4 struct htt_ver_req_cmd { __le32 ver_reg_info; + __le32 tcl_metadata_version; } __packed; enum htt_srng_ring_type { diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 94310bcc620c..da30d1054f61 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -1103,7 +1103,14 @@ int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab) skb_put(skb, len); cmd = (struct htt_ver_req_cmd *)skb->data; cmd->ver_reg_info = le32_encode_bits(HTT_H2T_MSG_TYPE_VERSION_REQ, - HTT_VER_REQ_INFO_MSG_ID); + HTT_OPTION_TAG); + + cmd->tcl_metadata_version = le32_encode_bits(HTT_TAG_TCL_METADATA_VERSION, + HTT_OPTION_TAG) | + le32_encode_bits(HTT_TCL_METADATA_VER_SZ, + HTT_OPTION_LEN) | + le32_encode_bits(HTT_OPTION_TCL_METADATA_VER_V2, + HTT_OPTION_VALUE); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { From 4b575d2e4659b9dd45ad0ea76e9bff65fe2c232b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 186/465] wifi: ath12k: Add support for MLO Multicast handling in driver JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2f50de7256777003d40931f1732e6c644ec8cb80 Author: Balamurugan Mahalingam Date: Mon Feb 3 13:26:47 2025 -0800 wifi: ath12k: Add support for MLO Multicast handling in driver For MLO netdevice, the broadcast frame should be transmitted with the same sequence number on all the links. Per IEEE 802.11be-2024 section 10.3.2.14.2 "Transmitter requirements", An AP MLD shall use SNS11 in Table 10-5 (Transmitter sequence number spaces) maintained by the MLD to determine the sequence number of a group addressed data frame that is transmitted by an AP affiliated with the AP MLD so that the same group addressed Data frame transmitted over multiple links by the AP MLD uses the same sequence number for transmission on each link. Currently the MLO multicast handling is done in the mac80211 layer. Enable support for handling MLO Multicast in the driver to update the hardware descriptors in a custom way to handle the multicast frames. Firmware expects the MLO multicast frames to the submitted to the hardware with special vdev_id (actual vdev_id + 128) to recognize it as a host inspected frame to avoid using the reinjected path and it also uses the multicast global sequence number (GSN) provided by the host in the HTT metadata to process and transmit it with the same sequence number. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Balamurugan Mahalingam Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250203212647.2694566-3-quic_bmahalin@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp.h | 6 ++ drivers/net/wireless/ath/ath12k/dp_tx.c | 16 ++- drivers/net/wireless/ath/ath12k/dp_tx.h | 4 +- drivers/net/wireless/ath/ath12k/mac.c | 123 +++++++++++++++++++++++- 5 files changed, 142 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index b60a636a6e19..e9388bdf532d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -332,6 +332,7 @@ struct ath12k_vif { u32 key_cipher; u8 tx_encap_type; bool ps; + atomic_t mcbc_gsn; struct ath12k_link_vif deflink; struct ath12k_link_vif __rcu *link[ATH12K_NUM_MAX_LINKS]; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 7cdc62aa35be..75435a931548 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -385,6 +385,12 @@ struct ath12k_dp { /* peer meta data */ #define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 3) +/* Global sequence number */ +#define HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM 3 +#define HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED BIT(2) +#define HTT_TCL_META_DATA_GLOBAL_SEQ_NUM GENMASK(14, 3) +#define HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID 128 + /* HTT tx completion is overlaid in wbm_release_ring */ #define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13) #define HTT_TX_WBM_COMP_INFO1_REINJECT_REASON GENMASK(3, 0) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index da30d1054f61..4e11b7b61b82 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -219,7 +219,7 @@ out: } int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, - struct sk_buff *skb) + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; @@ -292,13 +292,27 @@ tcl_ring_sel: msdu_ext_desc = true; } + if (gsn_valid) { + /* Reset and Initialize meta_data_flags with Global Sequence + * Number (GSN) info. + */ + ti.meta_data_flags = + u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, + HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + } + ti.encap_type = ath12k_dp_tx_get_encap_type(arvif, skb); ti.addr_search_flags = arvif->hal_addr_search_flags; ti.search_type = arvif->search_type; ti.type = HAL_TCL_DESC_TYPE_BUFFER; ti.pkt_offset = 0; ti.lmac_id = ar->lmac_id; + ti.vdev_id = arvif->vdev_id; + if (gsn_valid) + ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + ti.bss_ast_hash = arvif->ast_hash; ti.bss_ast_idx = arvif->ast_idx; ti.dscp_tid_tbl_idx = 0; diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h index 46dce23501f3..a5904097dc34 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_TX_H @@ -17,7 +17,7 @@ struct ath12k_dp_htt_wbm_tx_status { int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, - struct sk_buff *skb); + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn); void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id); int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index c9f7bea4e04a..1536328d5854 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7201,6 +7201,22 @@ static void ath12k_mac_add_p2p_noa_ie(struct ath12k *ar, spin_unlock_bh(&ar->data_lock); } +/* Note: called under rcu_read_lock() */ +static void ath12k_mlo_mcast_update_tx_link_address(struct ieee80211_vif *vif, + u8 link_id, struct sk_buff *skb, + u32 info_flags) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_bss_conf *bss_conf; + + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return; + + bss_conf = rcu_dereference(vif->link_conf[link_id]); + if (bss_conf) + ether_addr_copy(hdr->addr2, bss_conf->addr); +} + /* Note: called under rcu_read_lock() */ static u8 ath12k_mac_get_tx_link(struct ieee80211_sta *sta, struct ieee80211_vif *vif, u8 link, struct sk_buff *skb, u32 info_flags) @@ -7312,9 +7328,16 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_sta *sta = control->sta; + struct ath12k_link_vif *tmp_arvif; u32 info_flags = info->flags; - struct ath12k *ar; + struct sk_buff *msdu_copied; + struct ath12k *ar, *tmp_ar; + struct ath12k_peer *peer; + unsigned long links_map; + bool is_mcast = false; + struct ethhdr *eth; bool is_prb_rsp; + u16 mcbc_gsn; u8 link_id; int ret; @@ -7351,6 +7374,9 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { + eth = (struct ethhdr *)skb->data; + is_mcast = is_multicast_ether_addr(eth->h_dest); + skb_cb->flags |= ATH12K_SKB_HW_80211_ENCAP; } else if (ieee80211_is_mgmt(hdr->frame_control)) { ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp); @@ -7362,14 +7388,99 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, return; } + if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) + is_mcast = is_multicast_ether_addr(hdr->addr1); + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); - ret = ath12k_dp_tx(ar, arvif, skb); - if (ret) { - ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); - ieee80211_free_txskb(hw, skb); + if (!vif->valid_links || !is_mcast || + test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { + ret = ath12k_dp_tx(ar, arvif, skb, false, 0); + if (unlikely(ret)) { + ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->ah->hw, skb); + return; + } + } else { + mcbc_gsn = atomic_inc_return(&ahvif->mcbc_gsn) & 0xfff; + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, + IEEE80211_MLD_MAX_NUM_LINKS) { + tmp_arvif = rcu_dereference(ahvif->link[link_id]); + if (!tmp_arvif || !tmp_arvif->is_up) + continue; + + tmp_ar = tmp_arvif->ar; + msdu_copied = skb_copy(skb, GFP_ATOMIC); + if (!msdu_copied) { + ath12k_err(ar->ab, + "skb copy failure link_id 0x%X vdevid 0x%X\n", + link_id, tmp_arvif->vdev_id); + continue; + } + + ath12k_mlo_mcast_update_tx_link_address(vif, link_id, + msdu_copied, + info_flags); + + skb_cb = ATH12K_SKB_CB(msdu_copied); + info = IEEE80211_SKB_CB(msdu_copied); + skb_cb->link_id = link_id; + + /* For open mode, skip peer find logic */ + if (unlikely(ahvif->key_cipher == WMI_CIPHER_NONE)) + goto skip_peer_find; + + spin_lock_bh(&tmp_ar->ab->base_lock); + peer = ath12k_peer_find_by_addr(tmp_ar->ab, tmp_arvif->bssid); + if (!peer) { + spin_unlock_bh(&tmp_ar->ab->base_lock); + ath12k_warn(tmp_ar->ab, + "failed to find peer for vdev_id 0x%X addr %pM link_map 0x%X\n", + tmp_arvif->vdev_id, tmp_arvif->bssid, + ahvif->links_map); + dev_kfree_skb_any(msdu_copied); + continue; + } + + key = peer->keys[peer->mcast_keyidx]; + if (key) { + skb_cb->cipher = key->cipher; + skb_cb->flags |= ATH12K_SKB_CIPHER_SET; + info->control.hw_key = key; + + hdr = (struct ieee80211_hdr *)msdu_copied->data; + if (!ieee80211_has_protected(hdr->frame_control)) + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } + spin_unlock_bh(&tmp_ar->ab->base_lock); + +skip_peer_find: + ret = ath12k_dp_tx(tmp_ar, tmp_arvif, + msdu_copied, true, mcbc_gsn); + if (unlikely(ret)) { + if (ret == -ENOMEM) { + /* Drops are expected during heavy multicast + * frame flood. Print with debug log + * level to avoid lot of console prints + */ + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "failed to transmit frame %d\n", + ret); + } else { + ath12k_warn(ar->ab, + "failed to transmit frame %d\n", + ret); + } + + dev_kfree_skb_any(msdu_copied); + } + } + ieee80211_free_txskb(ar->ah->hw, skb); } } @@ -11150,6 +11261,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_iftypes_ext_capa[2].eml_capabilities = cap->eml_cap; ath12k_iftypes_ext_capa[2].mld_capa_and_ops = cap->mld_cap; wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + + ieee80211_hw_set(hw, MLO_MCAST_MULTI_LINK_TX); } hw->queues = ATH12K_HW_MAX_QUEUES; From 5e3dfdecb4b8c78a6fbdd425feb2dcc0b2da5433 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 187/465] wifi: ath12k: Fix locking in "QMI firmware ready" error paths JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b9c7299a3341a737622e4de45b9c27e60ad01e3b Author: Bart Van Assche Date: Thu Feb 6 14:13:17 2025 -0800 wifi: ath12k: Fix locking in "QMI firmware ready" error paths If ag->mutex has been locked, unlock it before returning. If it has not been locked, do not unlock it before returning. These bugs have been detected by the Clang thread-safety analyzer. Cc: Karthikeyan Periyasamy Cc: Jeff Johnson Fixes: ee146e11b4d9 ("wifi: ath12k: refactor core start based on hardware group") Signed-off-by: Bart Van Assche Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250206221317.3845663-1-bvanassche@acm.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 323bd5082a7d..e820462f4f1d 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1132,16 +1132,18 @@ err_core_stop: ath12k_core_stop(ab); mutex_unlock(&ab->core_lock); } + mutex_unlock(&ag->mutex); goto exit; err_dp_free: ath12k_dp_free(ab); mutex_unlock(&ab->core_lock); + mutex_unlock(&ag->mutex); + err_firmware_stop: ath12k_qmi_firmware_stop(ab); exit: - mutex_unlock(&ag->mutex); return ret; } From d4b4462f72953d6c7daab8538ca431c4ede3ebef Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 188/465] wifi: ath12k: Enable MLO setup ready and teardown commands for single split-phy device JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5cec2d86c7f4242fb30a696d8e6fd48109bf3e8f Author: Aaradhana Sahu Date: Fri Feb 7 10:33:25 2025 +0530 wifi: ath12k: Enable MLO setup ready and teardown commands for single split-phy device When multi-link operation(MLO) is enabled through follow-up patches in the single split-phy device, the firmware expects hardware links (hw_links) information from the driver. If driver does not send WMI multi-link setup and ready command to the firmware during MLO setup for single split-phy device, the firmware will be unaware of the hw_links component of the multi-link operation. This may lead to firmware assert during multi-link association. Therefore, enable WMI setup, ready and teardown commands for single split-phy PCI device. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Aaradhana Sahu Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250207050327.360987-2-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 33 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/mac.c | 9 +++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index e820462f4f1d..1195eba0950a 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -897,10 +897,41 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) ath12k_mac_destroy(ag); } +u8 ath12k_get_num_partner_link(struct ath12k *ar) +{ + struct ath12k_base *partner_ab, *ab = ar->ab; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_pdev *pdev; + u8 num_link = 0; + int i, j; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + partner_ab = ag->ab[i]; + + for (j = 0; j < partner_ab->num_radios; j++) { + pdev = &partner_ab->pdevs[j]; + + /* Avoid the self link */ + if (ar == pdev->ar) + continue; + + num_link++; + } + } + + return num_link; +} + static int __ath12k_mac_mlo_ready(struct ath12k *ar) { + u8 num_link = ath12k_get_num_partner_link(ar); int ret; + if (num_link == 0) + return 0; + ret = ath12k_wmi_mlo_ready(ar); if (ret) { ath12k_err(ar->ab, "MLO ready failed for pdev %d: %d\n", @@ -942,7 +973,7 @@ static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag) { int ret, i; - if (!ag->mlo_capable || ag->num_devices == 1) + if (!ag->mlo_capable) return 0; ret = ath12k_mac_mlo_setup(ag); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index e9388bdf532d..ff023d751bf1 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1209,6 +1209,7 @@ int ath12k_core_resume(struct ath12k_base *ab); int ath12k_core_suspend(struct ath12k_base *ab); int ath12k_core_suspend_late(struct ath12k_base *ab); void ath12k_core_hw_group_unassign(struct ath12k_base *ab); +u8 ath12k_get_num_partner_link(struct ath12k *ar); const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, const char *filename); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 1536328d5854..27d2fad1b915 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -11448,6 +11448,9 @@ static int __ath12k_mac_mlo_setup(struct ath12k *ar) } } + if (num_link == 0) + return 0; + mlo.group_id = cpu_to_le32(ag->id); mlo.partner_link_id = partner_link_id; mlo.num_partner_links = num_link; @@ -11477,10 +11480,16 @@ static int __ath12k_mac_mlo_teardown(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; int ret; + u8 num_link; if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) return 0; + num_link = ath12k_get_num_partner_link(ar); + + if (num_link == 0) + return 0; + ret = ath12k_wmi_mlo_teardown(ar); if (ret) { ath12k_warn(ab, "failed to send MLO teardown WMI command for pdev %d: %d\n", From 7d63eeea5e46ab3528e661e1a2c02eab00880859 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 189/465] wifi: ath12k: Remove dependency on single_chip_mlo_support for mlo_capable flag JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 16266b7ad6c011f760083851e8caa0d6b657f5e8 Author: Aaradhana Sahu Date: Fri Feb 7 10:33:26 2025 +0530 wifi: ath12k: Remove dependency on single_chip_mlo_support for mlo_capable flag The mlo_capable flag in structure ath12k_hw_group indicate that a device is capable of operating in multi-link mode. Currently this is enabled based on single_chip_mlo_support advertised by the firmware within ath12k_qmi_phy_cap_send(). Since the firmware advertises multi-link operation (MLO) support through the ATH12K_FW_FEATURE_MLO feature in firmware-2.bin, there is no need to rely on the QMI phy capability (single_chip_mlo_support). Therefore remove the dependency on single_chip_mlo_support to set mlo_capable flag. Below is the impact on single split-phy PCI device with and without this patch: Note: This patch does not change the existing behavior of the single split-phy PCI device. 1. Driver without this patch + firmware with single_chip_mlo_support as false: MLO is not enabled. 2. Driver without this patch + firmware with single_chip_mlo_support as true: MLO works fine. 3. Driver with this patch + firmware with single_chip_mlo_support as false: MLO is not enabled. 4. Driver with this patch + firmware with single_chip_mlo_support as true: MLO works fine. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250207050327.360987-3-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/qmi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index ef537c6bf0da..fcbe234758e1 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2265,10 +2265,6 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) goto out; } - if (resp.single_chip_mlo_support_valid && - resp.single_chip_mlo_support) - ab->single_chip_mlo_supp = true; - if (!resp.num_phy_valid) { ret = -ENODATA; goto out; @@ -2277,10 +2273,9 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = resp.num_phy; ath12k_dbg(ab, ATH12K_DBG_QMI, - "phy capability resp valid %d num_phy %d valid %d board_id %d valid %d single_chip_mlo_support %d\n", + "phy capability resp valid %d num_phy %d valid %d board_id %d\n", resp.num_phy_valid, resp.num_phy, - resp.board_id_valid, resp.board_id, - resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support); + resp.board_id_valid, resp.board_id); return; From bdfd53eb28581412dd07e8ed4cbbc1fc47924b5f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 190/465] wifi: ath12k: Enable MLO for single split-phy PCI device JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4f4bd1f8a5c2f7b1b24ecc553033b210de2a0513 Author: Aaradhana Sahu Date: Fri Feb 7 10:33:27 2025 +0530 wifi: ath12k: Enable MLO for single split-phy PCI device The single split-phy PCI device can perform multi-link operation (MLO) within its own radio, and the MLO-supporting firmware also supports MLO for split-phy PCI devices. Therefore, enable MLO for the single split-phy PCI device. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250207050327.360987-4-quic_aarasahu@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 13 ++++--------- drivers/net/wireless/ath/ath12k/core.h | 3 --- drivers/net/wireless/ath/ath12k/qmi.c | 3 +-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 1195eba0950a..0c556023141b 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1847,14 +1847,11 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) /* If more than one devices are grouped, then inter MLO * functionality can work still independent of whether internally * each device supports single_chip_mlo or not. - * Only when there is one device, then it depends whether the - * device can support intra chip MLO or not + * Only when there is one device, then disable for WCN chipsets + * till the required driver implementation is in place. */ - if (ag->num_devices > 1) { - ag->mlo_capable = true; - } else { + if (ag->num_devices == 1) { ab = ag->ab[0]; - ag->mlo_capable = ab->single_chip_mlo_supp; /* WCN chipsets does not advertise in firmware features * hence skip checking @@ -1863,8 +1860,7 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) return; } - if (!ag->mlo_capable) - return; + ag->mlo_capable = true; for (i = 0; i < ag->num_devices; i++) { ab = ag->ab[i]; @@ -1980,7 +1976,6 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, ab->dev = dev; ab->hif.bus = bus; ab->qmi.num_radios = U8_MAX; - ab->single_chip_mlo_supp = false; /* Device index used to identify the devices in a group. * diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ff023d751bf1..3fac4f00d383 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1055,9 +1055,6 @@ struct ath12k_base { const struct hal_rx_ops *hal_rx_ops; - /* Denotes the whether MLO is possible within the chip */ - bool single_chip_mlo_supp; - struct completion restart_completed; #ifdef CONFIG_ACPI diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index fcbe234758e1..348dbc81bad8 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2056,8 +2056,7 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, } if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { - ab->single_chip_mlo_supp = false; - + ag->mlo_capable = false; ath12k_dbg(ab, ATH12K_DBG_QMI, "skip QMI MLO cap due to invalid num_radio %d\n", ab->qmi.num_radios); From a04279d473b3a4ccec2d851a1e9e4ca65b5c1563 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:42 +0200 Subject: [PATCH 191/465] wifi: ath12k: Support Sounding Stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5c1963119b82b369b902091502a2135b238c9793 Author: Dinesh Karthikeyan Date: Tue Feb 4 12:14:13 2025 +0530 wifi: ath12k: Support Sounding Stats Add support to request sounding stats from firmware through HTT stats type 22. These stats give sounding information of different Wi-Fi standards, channel vector upload status and correlation details. Sample output: ------------- echo 22 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_TX_AC_SOUNDING_STATS_TLV: ac_cbf_20 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 ac_cbf_40 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 ..... Sounding User_0 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0 Sounding User_1 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0 ..... HTT_TX_AX_SOUNDING_STATS_TLV: ax_cbf_20 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 ax_cbf_40 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 ..... Sounding User_0 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0 Sounding User_1 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0 ..... HTT_TX_BE_SOUNDING_STATS_TLV: be_cbf_20 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 be_cbf_40 = IBF: 0, SU_SIFS: 0, SU_RBO: 0, MU_SIFS: 0, MU_RBO: 0 ..... Sounding User_0 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0, 320MHz: 0 Sounding User_1 = 20MHz: 0, 40MHz: 0, 80MHz: 0, 160MHz: 0, 320MHz: 0 ..... CV UPLOAD HANDLER STATS: cv_nc_mismatch_err = 0 cv_fcs_err = 0 cv_frag_idx_mismatch = 0 cv_invalid_peer_id = 0 ..... CV QUERY STATS: cv_total_query = 0 cv_total_pattern_query = 0 cv_total_bw_query = 0 cv_invalid_bw_coding = 0 ..... Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Link: https://patch.msgid.link/20250204064417.3671928-2-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 265 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 78 ++++++ 2 files changed, 343 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index bad1bd21d67d..9998c5cf7ab0 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -2511,6 +2511,268 @@ ath12k_htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_tx_sounding_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + const __le32 *cbf_20, *cbf_40, *cbf_80, *cbf_160, *cbf_320; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 tx_sounding_mode; + u8 i, u; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + cbf_20 = htt_stats_buf->cbf_20; + cbf_40 = htt_stats_buf->cbf_40; + cbf_80 = htt_stats_buf->cbf_80; + cbf_160 = htt_stats_buf->cbf_160; + cbf_320 = htt_stats_buf->cbf_320; + tx_sounding_mode = le32_to_cpu(htt_stats_buf->tx_sounding_mode); + + if (tx_sounding_mode == ATH12K_HTT_TX_AC_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_AX_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_BE_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_BE_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_320 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_320[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, + "160MHz: %u, 320MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++]), + le32_to_cpu(htt_stats_buf->sounding_320[u])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_CMN_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nCV UPLOAD HANDLER STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch_err)); + len += scnprintf(buf + len, buf_len - len, "cv_fcs_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_fcs_err)); + len += scnprintf(buf + len, buf_len - len, "cv_frag_idx_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_frag_idx_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_peer_id = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_peer_id)); + len += scnprintf(buf + len, buf_len - len, "cv_no_txbf_setup = %u\n", + le32_to_cpu(htt_stats_buf->cv_no_txbf_setup)); + len += scnprintf(buf + len, buf_len - len, "cv_expiry_in_update = %u\n", + le32_to_cpu(htt_stats_buf->cv_expiry_in_update)); + len += scnprintf(buf + len, buf_len - len, "cv_pkt_bw_exceed = %u\n", + le32_to_cpu(htt_stats_buf->cv_pkt_bw_exceed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_not_done_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_not_done_err)); + len += scnprintf(buf + len, buf_len - len, "cv_update_failed = %u\n", + le32_to_cpu(htt_stats_buf->cv_update_failed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_timeout_error = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_timeout_error)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ibf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ibf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ebf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ebf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_received = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_received)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_fed_back = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_buf_fed_back)); + + len += scnprintf(buf + len, buf_len - len, "CV QUERY STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_total_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query)); + len += scnprintf(buf + len, buf_len - len, + "cv_total_pattern_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_pattern_query)); + len += scnprintf(buf + len, buf_len - len, "cv_total_bw_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_bw_query)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_bw_coding = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_bw_coding)); + len += scnprintf(buf + len, buf_len - len, "cv_forced_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_forced_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_standalone_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_standalone_sounding)); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_fb_type_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_fb_type_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_ofdma_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_ofdma_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_pattern_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_pattern_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_preamble_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_preamble_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_nr_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nr_mismatch)); + len += scnprintf(buf + len, buf_len - len, + "cv_in_use_cnt_exceeded = %u\n", + le32_to_cpu(htt_stats_buf->cv_in_use_cnt_exceeded)); + len += scnprintf(buf + len, buf_len - len, "cv_ntbr_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_ntbr_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_found_upload_in_progress = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_upload_in_progress)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query)); + len += scnprintf(buf + len, buf_len - len, "cv_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_found)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found)); + len += scnprintf(buf + len, buf_len - len, "cv_total_query_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found_ibf)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query_ibf = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query_ibf)); + } + + stats_req->buf_len = len; +} + static void ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) @@ -4473,6 +4735,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_PDEV_CCA_COUNTERS_TAG: ath12k_htt_print_pdev_stats_cca_counters_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_SOUNDING_STATS_TAG: + ath12k_htt_print_tx_sounding_stats_tlv(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_OBSS_PD_TAG: ath12k_htt_print_pdev_obss_pd_stats_tlv(tag_buf, len, stats_req); break; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index e557a5807bc6..a4dd894df077 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -136,6 +136,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, @@ -200,6 +201,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, HTT_STATS_PDEV_OBSS_PD_TAG = 88, @@ -1240,6 +1242,82 @@ struct ath12k_htt_pdev_cca_stats_hist_v1_tlv { __le32 collection_interval; } __packed; +#define ATH12K_HTT_TX_CV_CORR_MAX_NUM_COLUMNS 8 +#define ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS 4 +#define ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_TX_NUM_EXTRA_MCS_CNTRS 2 + +#define ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS) + +enum ath12k_htt_txbf_sound_steer_modes { + ATH12K_HTT_IMPL_STEER_STATS = 0, + ATH12K_HTT_EXPL_SUSIFS_STEER_STATS = 1, + ATH12K_HTT_EXPL_SURBO_STEER_STATS = 2, + ATH12K_HTT_EXPL_MUSIFS_STEER_STATS = 3, + ATH12K_HTT_EXPL_MURBO_STEER_STATS = 4, + ATH12K_HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum ath12k_htt_stats_sounding_tx_mode { + ATH12K_HTT_TX_AC_SOUNDING_MODE = 0, + ATH12K_HTT_TX_AX_SOUNDING_MODE = 1, + ATH12K_HTT_TX_BE_SOUNDING_MODE = 2, + ATH12K_HTT_TX_CMN_SOUNDING_MODE = 3, +}; + +struct ath12k_htt_tx_sounding_stats_tlv { + __le32 tx_sounding_mode; + __le32 cbf_20[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_40[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_80[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_160[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 sounding[ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; + __le32 cv_nc_mismatch_err; + __le32 cv_fcs_err; + __le32 cv_frag_idx_mismatch; + __le32 cv_invalid_peer_id; + __le32 cv_no_txbf_setup; + __le32 cv_expiry_in_update; + __le32 cv_pkt_bw_exceed; + __le32 cv_dma_not_done_err; + __le32 cv_update_failed; + __le32 cv_total_query; + __le32 cv_total_pattern_query; + __le32 cv_total_bw_query; + __le32 cv_invalid_bw_coding; + __le32 cv_forced_sounding; + __le32 cv_standalone_sounding; + __le32 cv_nc_mismatch; + __le32 cv_fb_type_mismatch; + __le32 cv_ofdma_bw_mismatch; + __le32 cv_bw_mismatch; + __le32 cv_pattern_mismatch; + __le32 cv_preamble_mismatch; + __le32 cv_nr_mismatch; + __le32 cv_in_use_cnt_exceeded; + __le32 cv_found; + __le32 cv_not_found; + __le32 sounding_320[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 cbf_320[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cv_ntbr_sounding; + __le32 cv_found_upload_in_progress; + __le32 cv_expired_during_query; + __le32 cv_dma_timeout_error; + __le32 cv_buf_ibf_uploads; + __le32 cv_buf_ebf_uploads; + __le32 cv_buf_received; + __le32 cv_buf_fed_back; + __le32 cv_total_query_ibf; + __le32 cv_found_ibf; + __le32 cv_not_found_ibf; + __le32 cv_expired_during_query_ibf; +} __packed; + struct ath12k_htt_pdev_obss_pd_stats_tlv { __le32 num_obss_tx_ppdu_success; __le32 num_obss_tx_ppdu_failure; From 08c3f5ca49fed055f8eae412c16471c8b01526d6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 192/465] wifi: ath12k: Support Latency Stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f7c5e24bb1918677f7d6195a8825b39462e0a9fc Author: Dinesh Karthikeyan Date: Tue Feb 4 12:14:14 2025 +0530 wifi: ath12k: Support Latency Stats Add support to request latency stats from firmware through HTT stats type 25. These stats give information about count of transmitted and received MAC Protocol Data Units(PDU) and Service Data Units(SDU) and other latency stats. Sample output: ------------- echo 25 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_STATS_LATENCY_CTX_TLV: duration = 0 tx_msdu_cnt = 0 tx_mpdu_cnt = 0 rx_msdu_cnt = 0 rx_mpdu_cnt = 0 HTT_STATS_LATENCY_PROF_TLV: Latency name = PROF_SCH_ENQ_TQM_CMDS count = 0 minimum = 4294967295 maximum = 0 ..... HTT_STATS_LATENCY_CNT_TLV: prof_enable_cnt = 39 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Link: https://patch.msgid.link/20250204064417.3671928-3-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 94 +++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 34 +++++++ 2 files changed, 128 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 9998c5cf7ab0..0abca93e0802 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -2838,6 +2838,91 @@ ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_latency_prof_ctx_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_ctx_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CTX_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "duration = %u\n", + le32_to_cpu(htt_stats_buf->duration)); + len += scnprintf(buf + len, buf_len - len, "tx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "tx_mpdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_mpdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_mpdu_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->rx_mpdu_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_cnt(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_cnt_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CNT_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "prof_enable_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->prof_enable_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + if (le32_to_cpu(htt_stats_buf->print_header) == 1) { + len += scnprintf(buf + len, buf_len - len, + "HTT_STATS_LATENCY_PROF_TLV:\n"); + } + + len += scnprintf(buf + len, buf_len - len, "Latency name = %s\n", + htt_stats_buf->latency_prof_name); + len += scnprintf(buf + len, buf_len - len, "count = %u\n", + le32_to_cpu(htt_stats_buf->cnt)); + len += scnprintf(buf + len, buf_len - len, "minimum = %u\n", + le32_to_cpu(htt_stats_buf->min)); + len += scnprintf(buf + len, buf_len - len, "maximum = %u\n", + le32_to_cpu(htt_stats_buf->max)); + len += scnprintf(buf + len, buf_len - len, "last = %u\n", + le32_to_cpu(htt_stats_buf->last)); + len += scnprintf(buf + len, buf_len - len, "total = %u\n", + le32_to_cpu(htt_stats_buf->tot)); + len += scnprintf(buf + len, buf_len - len, "average = %u\n", + le32_to_cpu(htt_stats_buf->avg)); + len += scnprintf(buf + len, buf_len - len, "histogram interval = %u\n", + le32_to_cpu(htt_stats_buf->hist_intvl)); + len += print_array_to_buf(buf, len, "histogram", htt_stats_buf->hist, + ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST, "\n\n"); + + stats_req->buf_len = len; +} + static void ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) @@ -4741,6 +4826,15 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_PDEV_OBSS_PD_TAG: ath12k_htt_print_pdev_obss_pd_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_LATENCY_CTX_TAG: + ath12k_htt_print_latency_prof_ctx_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_CNT_TAG: + ath12k_htt_print_latency_prof_cnt(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_PROF_STATS_TAG: + ath12k_htt_print_latency_prof_stats_tlv(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); break; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index a4dd894df077..1b835473f419 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -138,6 +138,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, @@ -206,6 +207,9 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, HTT_STATS_PDEV_OBSS_PD_TAG = 88, HTT_STATS_HW_WAR_TAG = 89, + HTT_STATS_LATENCY_PROF_STATS_TAG = 91, + HTT_STATS_LATENCY_CTX_TAG = 92, + HTT_STATS_LATENCY_CNT_TAG = 93, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, @@ -1340,6 +1344,36 @@ struct ath12k_htt_pdev_obss_pd_stats_tlv { __le32 num_sr_ppdu_abort_flush_cnt; } __packed; +#define ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN 32 +#define ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST 3 +#define ATH12K_HTT_INTERRUPTS_LATENCY_PROFILE_MAX_HIST 3 + +struct ath12k_htt_latency_prof_stats_tlv { + __le32 print_header; + s8 latency_prof_name[ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN]; + __le32 cnt; + __le32 min; + __le32 max; + __le32 last; + __le32 tot; + __le32 avg; + __le32 hist_intvl; + __le32 hist[ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST]; +} __packed; + +struct ath12k_htt_latency_prof_ctx_tlv { + __le32 duration; + __le32 tx_msdu_cnt; + __le32 tx_mpdu_cnt; + __le32 tx_ppdu_cnt; + __le32 rx_msdu_cnt; + __le32 rx_mpdu_cnt; +} __packed; + +struct ath12k_htt_latency_prof_cnt_tlv { + __le32 prof_enable_cnt; +} __packed; + #define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 #define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 #define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 From 686d791a28f36a7cae6cd115dfd50614809a665a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 193/465] wifi: ath12k: Support Uplink OFDMA Trigger Stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e669a18b20e205dc51a71a07bd3597f123979751 Author: Dinesh Karthikeyan Date: Tue Feb 4 12:14:15 2025 +0530 wifi: ath12k: Support Uplink OFDMA Trigger Stats Add support to request uplink trigger stats from firmware through HTT stats type 26. These stats give information about uplink OFDMA bandwidth, received RSSI, power headroom, QoS, data size, PPDU info and pass/fail info for each user. Note: MCC firmware version WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 does not support tags HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG(94) and HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG(95), currently. Sample output: ------------- echo 26 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_RX_PDEV_UL_TRIGGER_STATS_TLV: mac_id = 0 rx_11ax_ul_ofdma = 0 ul_ofdma_rx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[0] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[1] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[2] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_gi[3] = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0 ul_ofdma_rx_nss = 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ul_ofdma_rx_bw = 0:0, 1:0, 2:0, 3:0 half_ul_ofdma_rx_bw = 0:0, 1:0, 2:0, 3:0 quarter_ul_ofdma_rx_bw = 0:0, 1:0, 2:0, 3:0 ..... HTT_RX_PDEV_UL_OFDMA_USER_STAS_TLV: rx_ulofdma_non_data_ppdu_0 = 0 rx_ulofdma_data_ppdu_0 = 0 rx_ulofdma_mpdu_ok_0 = 0 rx_ulofdma_mpdu_fail_0 = 0 rx_ulofdma_non_data_nusers_0 = 0 rx_ulofdma_data_nusers_0 = 0 ..... Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Link: https://patch.msgid.link/20250204064417.3671928-4-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 131 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 63 +++++++-- 2 files changed, 183 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 0abca93e0802..a235c5b6dcab 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -2923,6 +2923,131 @@ ath12k_htt_print_latency_prof_stats_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_ul_ofdma_trigger_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_TRIGGER_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u]", j); + len += print_array_to_buf(buf, len, "", + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + } + + len += print_array_to_buf_index(buf, len, "ul_ofdma_rx_nss", 1, + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; j++) { + len += scnprintf(buf + len, buf_len - len, j == 0 ? + "half_ul_ofdma_rx_bw" : + "quarter_ul_ofdma_rx_bw"); + len += print_array_to_buf(buf, len, "", htt_stats_buf->red_bw[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->non_data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "rx_rssi_track_sta_aid", + htt_stats_buf->uplink_sta_aid, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_target_rssi", + htt_stats_buf->uplink_sta_target_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_fd_rssi", + htt_stats_buf->uplink_sta_fd_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_power_headroom", + htt_stats_buf->uplink_sta_power_headroom, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_ofdma_user_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 user_index; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + user_index = __le32_to_cpu(htt_stats_buf->user_index); + + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_OFDMA_USER_STAS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_ok)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_fail)); + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_nusers_%u = %u\n", user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_nusers)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_nusers_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_nusers)); + + stats_req->buf_len = len; +} + static void ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) @@ -4835,6 +4960,12 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_LATENCY_PROF_STATS_TAG: ath12k_htt_print_latency_prof_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG: + ath12k_htt_print_ul_ofdma_trigger_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG: + ath12k_htt_print_ul_ofdma_user_stats(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); break; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 1b835473f419..ab293dff7e44 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -139,6 +139,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, @@ -210,6 +211,8 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_LATENCY_PROF_STATS_TAG = 91, HTT_STATS_LATENCY_CTX_TAG = 92, HTT_STATS_LATENCY_CNT_TAG = 93, + HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94, + HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, @@ -1374,6 +1377,55 @@ struct ath12k_htt_latency_prof_cnt_tlv { __le32 prof_enable_cnt; } __packed; +#define ATH12K_HTT_RX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_RX_NUM_GI_CNTRS 4 +#define ATH12K_HTT_RX_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_NUM_BW_CNTRS 4 +#define ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS 6 +#define ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS 7 +#define ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK 5 +#define ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES 2 + +enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2, + ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS, +}; + +struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv { + __le32 user_index; + __le32 rx_ulofdma_non_data_ppdu; + __le32 rx_ulofdma_data_ppdu; + __le32 rx_ulofdma_mpdu_ok; + __le32 rx_ulofdma_mpdu_fail; + __le32 rx_ulofdma_non_data_nusers; + __le32 rx_ulofdma_data_nusers; +} __packed; + +struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 non_data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 uplink_sta_aid[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_target_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_fd_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_power_headroom[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_bsc_trig_rx_qos_null_only; +} __packed; + #define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 #define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 #define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 @@ -1711,17 +1763,6 @@ enum ATH12K_HTT_RC_MODE { ATH12K_HTT_RC_MODE_2D_COUNT }; -enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2, - ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS -}; - enum ath12k_htt_stats_rc_mode { ATH12K_HTT_STATS_RC_MODE_DLSU = 0, ATH12K_HTT_STATS_RC_MODE_DLMUMIMO = 1, From ae741ab5981f12b9f01ae0e8b03811f8a7c2a1e2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 194/465] wifi: ath12k: Support Uplink MUMIMO Trigger Stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1dbaae78e7f23819eb75f4ba8a4484e43cf625f9 Author: Roopni Devanathan Date: Tue Feb 4 12:14:16 2025 +0530 wifi: ath12k: Support Uplink MUMIMO Trigger Stats Add support to request uplink MUMIMO trigger stats from firmware through HTT stats type 27. These stats give information about bandwidth, RSSI of signal received, dB mean of pilots received, etc., of all users. Note: MCC firmware version WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 does not support tags HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG(94) and HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG(95), currently. Sample output: ------------- echo 27 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_RX_PDEV_UL_MUMIMO_TRIG_STATS_TLV: mac_id = 0 rx_11ax_ul_mumimo = 0 ul_mumimo_rx_mcs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_mumimo_rx_gi_0 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ul_mumimo_rx_gi_1 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0 ..... ul_mumimo_rx_nss = 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ul_mumimo_rx_bw = 0:0, 1:0, 2:0, 3:0 half_ul_mumimo_rx_bw = 0:0, 1:0, 2:0, 3:0 quarter_ul_mumimo_rx_bw = 0:0, 1:0, 2:0, 3:0 ul_mumimo_rx_stbc = 0 ul_mumimo_rx_ldpc = 0 rx_ul_mumimo_rssi_in_dbm: chain0 = 0:6, 1:0, 2:0, 3:0 rx_ul_mumimo_rssi_in_dbm: chain1 = 0:0, 1:0, 2:0, 3:0 ..... rx_ul_mumimo_target_rssi: user_0 = 0:-128, 1:-128, 2:-128, 3:-128 rx_ul_mumimo_target_rssi: user_1 = 0:-128, 1:-128, 2:-128, 3:-128 ..... rx_ul_mumimo_fd_rssi: user_0 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ul_mumimo_fd_rssi: user_1 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 ..... rx_ulmumimo_pilot_evm_db_mean: user_0 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 rx_ulmumimo_pilot_evm_db_mean: user_1 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0 ..... ul_mumimo_basic_trigger_rx_qos_null_only = 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Roopni Devanathan Link: https://patch.msgid.link/20250204064417.3671928-5-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 155 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 84 ++++++---- 2 files changed, 209 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index a235c5b6dcab..935ea9852a8e 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -48,6 +48,34 @@ print_array_to_buf(u8 *buf, u32 offset, const char *header, footer); } +static u32 +print_array_to_buf_s8(u8 *buf, u32 offset, const char *header, u32 stats_index, + const s8 *array, u32 array_len, const char *footer) +{ + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + int index = 0; + u8 i; + + if (header) + index += scnprintf(buf + offset, buf_len - offset, "%s = ", header); + + for (i = 0; i < array_len; i++) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + " %u:%d,", stats_index++, array[i]); + } + + index--; + if ((offset + index) < buf_len) + buf[offset + index] = '\0'; + + if (footer) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + "%s", footer); + } + + return index; +} + static const char *ath12k_htt_ax_tx_rx_ru_size_to_str(u8 ru_size) { switch (ru_size) { @@ -3048,6 +3076,130 @@ ath12k_htt_print_ul_ofdma_user_stats(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_ul_mumimo_trig_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv *htt_stats_buf = tag_buf; + char str_buf[ATH12K_HTT_MAX_STRING_LEN] = {0}; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u16 index; + u8 i, j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_MUMIMO_TRIG_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_mumimo)); + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs[i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs_ext[i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_mcs = %s\n", str_buf); + + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + index = 0; + memset(&str_buf[index], 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_rx_gi[j][i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_gi_ext[j][i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_rx_gi_%u = %s\n", j, str_buf); + } + + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + len += print_array_to_buf_index(buf, len, "ul_mumimo_rx_nss", 1, + htt_stats_buf->ul_mumimo_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + + len += print_array_to_buf(buf, len, "ul_mumimo_rx_bw", + htt_stats_buf->ul_mumimo_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; i++) { + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (j = 0; j < ATH12K_HTT_RX_NUM_BW_CNTRS; j++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", j, + le32_to_cpu(htt_stats_buf->red_bw[i][j])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "%s = %s\n", + i == 0 ? "half_ul_mumimo_rx_bw" : + "quarter_ul_mumimo_rx_bw", str_buf); + } + + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_ldpc)); + + for (j = 0; j < ATH12K_HTT_RX_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_rssi_in_dbm: chain%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->ul_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_target_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->tgt_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_fd_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->fd[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ulmumimo_pilot_evm_db_mean: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->db[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->mumimo_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + static void ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) @@ -4966,6 +5118,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG: ath12k_htt_print_ul_ofdma_user_stats(tag_buf, len, stats_req); break; + case HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG: + ath12k_htt_print_ul_mumimo_trig_stats(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); break; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index ab293dff7e44..9700412c03e0 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -123,36 +123,37 @@ struct ath12k_htt_extd_stats_msg { /* htt_dbg_ext_stats_type */ enum ath12k_dbg_htt_ext_stats_type { - ATH12K_DBG_HTT_EXT_STATS_RESET = 0, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, - ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, - ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, - ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, - ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, - ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, - ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, - ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, - ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, - ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, - ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, - ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, - ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, - ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, - ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, - ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, - ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38, - ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40, - ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41, - ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45, - ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46, - ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49, - ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51, - ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54, + ATH12K_DBG_HTT_EXT_STATS_RESET = 0, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_MUMIMO_TRIG_STATS = 27, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, + ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, + ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, + ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, + ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38, + ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40, + ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41, + ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45, + ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46, + ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49, + ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51, + ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -213,6 +214,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_LATENCY_CNT_TAG = 93, HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94, HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95, + HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG = 97, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, @@ -1385,6 +1387,7 @@ struct ath12k_htt_latency_prof_cnt_tlv { #define ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS 7 #define ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK 5 #define ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES 2 +#define ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS 2 enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, @@ -1426,6 +1429,27 @@ struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv { __le32 ul_ofdma_bsc_trig_rx_qos_null_only; } __packed; +#define ATH12K_HTT_TX_UL_MUMIMO_USER_STATS 8 + +struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_mumimo; + __le32 ul_mumimo_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_mumimo_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_mumimo_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_mumimo_rx_stbc; + __le32 ul_mumimo_rx_ldpc; + __le32 ul_mumimo_rx_mcs_ext[ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + __le32 ul_gi_ext[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + s8 ul_rssi[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 tgt_rssi[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 fd[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + s8 db[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 mumimo_bsc_trig_rx_qos_null_only; +} __packed; + #define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 #define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 #define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 From 0f6081c06847270b0280e3b1a043ab8cedf74fc4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 195/465] wifi: ath12k: Support Received FSE Stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7b19d5adadfef791d52fd0412a75ebb8f9f27a6b Author: Dinesh Karthikeyan Date: Tue Feb 4 12:14:17 2025 +0530 wifi: ath12k: Support Received FSE Stats Add support to request received Finite State Entropy stats from firmware through HTT stats type 28. These stats give software and hardware FSE stats such as cache entry count, full cache count, current and peak occupancy count, pending search counts, etc. Sample output: ------------- echo 28 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_STATS_RX_FSE_STATS_TLV: === Software RX FSE STATS === Enable count = 0 Disable count = 0 Cache invalidate entry count = 0 Full cache invalidate count = 0 === Hardware RX FSE STATS === Cache hits count = 0 Cache no. of searches = 0 Cache occupancy peak count: [0] = 0 [1-16] = 0 [17-32] = 0 [33-48] = 0 [49-64] = 0 [65-80] = 0 [81-96] = 0 [97-112] = 0 [113-127] = 0 [128] = 0 Cache occupancy current count: [0] = 0 [1-16] = 0 [17-32] = 0 [33-48] = 0 [49-64] = 0 [65-80] = 0 [81-96] = 0 [97-112] = 0 [113-127] = 0 [128] = 0 Cache search square count: [0] = 0 [1-50] = 0 [51-100] = 0 [101-200] = 0 [201-255] = 0 [256] = 0 Cache search peak pending count: [0] = 0 [1-2] = 0 [3-4] = 0 [Greater/Equal to 5] = 0 Cache search tot pending count: [0] = 0 [1-2] = 0 [3-4] = 0 [Greater/Equal to 5] = 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Link: https://patch.msgid.link/20250204064417.3671928-6-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 91 +++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 22 +++++ 2 files changed, 113 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 935ea9852a8e..1c0d5fa39a8d 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -3200,6 +3200,94 @@ ath12k_htt_print_ul_mumimo_trig_stats(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_rx_fse_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_fse_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_RX_FSE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "=== Software RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Enable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_enable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Disable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_disable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache invalidate entry count = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_invalidate_entry_cnt)); + len += scnprintf(buf + len, buf_len - len, "Full cache invalidate count = %u\n", + le32_to_cpu(htt_stats_buf->fse_full_cache_invalidate_cnt)); + + len += scnprintf(buf + len, buf_len - len, "\n=== Hardware RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Cache hits count = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_cache_hits_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache no. of searches = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_searches_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy peak count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy current count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache search square count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-50] = %u [51-100] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[101-200] = %u [201-255] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[256] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[5])); + len += scnprintf(buf + len, buf_len - len, "Cache search peak pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[3])); + len += scnprintf(buf + len, buf_len - len, "Cache search tot pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[3])); + + stats_req->buf_len = len; +} + static void ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) @@ -5121,6 +5209,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG: ath12k_htt_print_ul_mumimo_trig_stats(tag_buf, len, stats_req); break; + case HTT_STATS_RX_FSE_STATS_TAG: + ath12k_htt_print_rx_fse_stats_tlv(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); break; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 9700412c03e0..c2a02cf8a38b 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -141,6 +141,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_MUMIMO_TRIG_STATS = 27, + ATH12K_DBG_HTT_EXT_STATS_FSE_RX = 28, ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, @@ -215,6 +216,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94, HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95, HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG = 97, + HTT_STATS_RX_FSE_STATS_TAG = 98, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, @@ -1450,6 +1452,26 @@ struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv { __le32 mumimo_bsc_trig_rx_qos_null_only; } __packed; +#define ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_SQUARE_INDEX 6 +#define ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX 4 +#define ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX 4 + +struct ath12k_htt_rx_fse_stats_tlv { + __le32 fse_enable_cnt; + __le32 fse_disable_cnt; + __le32 fse_cache_invalidate_entry_cnt; + __le32 fse_full_cache_invalidate_cnt; + __le32 fse_num_cache_hits_cnt; + __le32 fse_num_searches_cnt; + __le32 fse_cache_occupancy_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX]; + __le32 fse_cache_occupancy_curr_cnt[ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX]; + __le32 fse_search_stat_square_cnt[ATH12K_HTT_RX_NUM_SQUARE_INDEX]; + __le32 fse_search_stat_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX]; + __le32 fse_search_stat_pending_cnt[ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX]; +} __packed; + #define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 #define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 #define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 From f6b72517d52cd573e5bf0fb87fd2c1135e0bce49 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 196/465] wifi: ath11k: use union for vaddr and iaddr in target_mem_chunk JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9ee2578c343a2c8b6cf6c43f2f385cdb56a25250 Author: Miaoqing Pan Date: Tue Aug 13 09:30:27 2024 +0800 wifi: ath11k: use union for vaddr and iaddr in target_mem_chunk The value of 'ab->hw_params.fixed_mem_region' determines that only one variable 'vaddr' or 'iaddr' is used in target_mem_chunk. So use an anonymous union instead, easy to check whether the memory is set or not. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04358-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Miaoqing Pan Acked-by: Jeff Johnson Link: https://patch.msgid.link/20240813013028.2708111-1-quic_miaoqing@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/qmi.c | 19 +++++++++++-------- drivers/net/wireless/ath/ath11k/qmi.h | 9 ++++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 5759fc521316..4f8b08ed1bbc 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1957,14 +1957,16 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) int i; for (i = 0; i < ab->qmi.mem_seg_count; i++) { - if ((ab->hw_params.fixed_mem_region || - test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && - ab->qmi.target_mem[i].iaddr) - iounmap(ab->qmi.target_mem[i].iaddr); - - if (!ab->qmi.target_mem[i].vaddr) + if (!ab->qmi.target_mem[i].anyaddr) continue; + if (ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + iounmap(ab->qmi.target_mem[i].iaddr); + ab->qmi.target_mem[i].iaddr = NULL; + continue; + } + dma_free_coherent(ab->dev, ab->qmi.target_mem[i].prev_size, ab->qmi.target_mem[i].vaddr, @@ -2070,7 +2072,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) break; case BDF_MEM_REGION_TYPE: ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -2093,10 +2095,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } else { ab->qmi.target_mem[idx].paddr = ATH11K_QMI_CALDB_ADDRESS; + ab->qmi.target_mem[idx].iaddr = NULL; } } else { ab->qmi.target_mem[idx].paddr = 0; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; } ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 7e06d100af57..fdf9b5f8c19f 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_QMI_H @@ -102,8 +102,11 @@ struct target_mem_chunk { u32 prev_size; u32 prev_type; dma_addr_t paddr; - u32 *vaddr; - void __iomem *iaddr; + union { + u32 *vaddr; + void __iomem *iaddr; + void *anyaddr; + }; }; struct target_info { From 84c4a9d570b62148d7c41aae8dc48123e2d380f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 197/465] wifi: ath11k: Add firmware coredump collection support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5edbb148bc57295a6abbb31b832878c0725ed10a Author: Miaoqing Pan Date: Tue Aug 13 09:30:28 2024 +0800 wifi: ath11k: Add firmware coredump collection support In case of firmware assert snapshot of firmware memory is essential for debugging. Add firmware coredump collection support for PCI bus. Collect RDDM and firmware paging dumps from MHI and pack them in TLV format and also pack various memory shared during QMI phase in separate TLVs. Add necessary header and share the dumps to user space using dev coredump framework. Coredump collection is controlled by CONFIG_DEV_COREDUMP. Dump collected for a radio is 55 MB approximately. The changeset is mostly copied from: https://lore.kernel.org/all/20240325183414.4016663-1-quic_ssreeela@quicinc.com/. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04358-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Miaoqing Pan Acked-by: Jeff Johnson Link: https://patch.msgid.link/20240813013028.2708111-2-quic_miaoqing@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/Makefile | 1 + drivers/net/wireless/ath/ath11k/core.c | 2 + drivers/net/wireless/ath/ath11k/core.h | 5 + drivers/net/wireless/ath/ath11k/coredump.c | 52 ++++++ drivers/net/wireless/ath/ath11k/coredump.h | 79 +++++++++ drivers/net/wireless/ath/ath11k/hif.h | 7 + drivers/net/wireless/ath/ath11k/mhi.c | 5 + drivers/net/wireless/ath/ath11k/mhi.h | 1 + drivers/net/wireless/ath/ath11k/pci.c | 188 +++++++++++++++++++++ drivers/net/wireless/ath/ath11k/qmi.h | 1 + 10 files changed, 341 insertions(+) create mode 100644 drivers/net/wireless/ath/ath11k/coredump.c create mode 100644 drivers/net/wireless/ath/ath11k/coredump.h diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index 43d2d8ddcdc0..d9092414b362 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -27,6 +27,7 @@ ath11k-$(CONFIG_ATH11K_TRACING) += trace.o ath11k-$(CONFIG_THERMAL) += thermal.o ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o ath11k-$(CONFIG_PM) += wow.o +ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o ath11k_ahb-y += ahb.o diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 12dd37c2e904..65bba0ae69a9 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -2258,6 +2258,7 @@ static void ath11k_core_reset(struct work_struct *work) reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_start_count, 0); + ath11k_coredump_collect(ab); ath11k_core_pre_reconfigure_recovery(ab); reinit_completion(&ab->reconfigure_complete); @@ -2393,6 +2394,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->restart_work, ath11k_core_restart); INIT_WORK(&ab->update_11d_work, ath11k_update_11d); INIT_WORK(&ab->reset_work, ath11k_core_reset); + INIT_WORK(&ab->dump_work, ath11k_coredump_upload); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->wow.wakeup_completed); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 9842a4d65c7b..1a3d0de4afde 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -32,6 +32,7 @@ #include "spectral.h" #include "wow.h" #include "fw.h" +#include "coredump.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -904,6 +905,10 @@ struct ath11k_base { /* HW channel counters frequency value in hertz common to all MACs */ u32 cc_freq_hz; + struct ath11k_dump_file_data *dump_data; + size_t ath11k_coredump_len; + struct work_struct dump_work; + struct ath11k_htc htc; struct ath11k_dp dp; diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c new file mode 100644 index 000000000000..b8bad358cebe --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include +#include "hif.h" +#include "coredump.h" +#include "debug.h" + +enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + enum ath11k_fw_crash_dump_type dump_type; + + switch (type) { + case HOST_DDR_REGION_TYPE: + dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA; + break; + case M3_DUMP_REGION_TYPE: + dump_type = FW_CRASH_DUMP_M3_DUMP; + break; + case PAGEABLE_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_PAGEABLE_DATA; + break; + case BDF_MEM_REGION_TYPE: + case CALDB_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_NONE; + break; + default: + dump_type = FW_CRASH_DUMP_TYPE_MAX; + break; + } + + return dump_type; +} +EXPORT_SYMBOL(ath11k_coredump_get_dump_type); + +void ath11k_coredump_upload(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, dump_work); + + ath11k_info(ab, "Uploading coredump\n"); + /* dev_coredumpv() takes ownership of the buffer */ + dev_coredumpv(ab->dev, ab->dump_data, ab->ath11k_coredump_len, GFP_KERNEL); + ab->dump_data = NULL; +} + +void ath11k_coredump_collect(struct ath11k_base *ab) +{ + ath11k_hif_coredump_download(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h new file mode 100644 index 000000000000..3960d9385261 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef _ATH11K_COREDUMP_H_ +#define _ATH11K_COREDUMP_H_ + +#define ATH11K_FW_CRASH_DUMP_V2 2 + +enum ath11k_fw_crash_dump_type { + FW_CRASH_DUMP_PAGING_DATA, + FW_CRASH_DUMP_RDDM_DATA, + FW_CRASH_DUMP_REMOTE_MEM_DATA, + FW_CRASH_DUMP_PAGEABLE_DATA, + FW_CRASH_DUMP_M3_DUMP, + FW_CRASH_DUMP_NONE, + + /* keep last */ + FW_CRASH_DUMP_TYPE_MAX, +}; + +#define COREDUMP_TLV_HDR_SIZE 8 + +struct ath11k_tlv_dump_data { + /* see ath11k_fw_crash_dump_type above */ + __le32 type; + + /* in bytes */ + __le32 tlv_len; + + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; + +struct ath11k_dump_file_data { + /* "ATH11K-FW-DUMP" */ + char df_magic[16]; + /* total dump len in bytes */ + __le32 len; + /* file dump version */ + __le32 version; + /* pci device id */ + __le32 chip_id; + /* qrtr instance id */ + __le32 qrtr_id; + /* pci domain id */ + __le32 bus_id; + guid_t guid; + /* time-of-day stamp */ + __le64 tv_sec; + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + /* room for growth w/out changing binary format */ + u8 unused[128]; + u8 data[]; +} __packed; + +#ifdef CONFIG_DEV_COREDUMP +enum ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type); +void ath11k_coredump_upload(struct work_struct *work); +void ath11k_coredump_collect(struct ath11k_base *ab); +#else +static inline enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + return FW_CRASH_DUMP_TYPE_MAX; +} + +static inline void ath11k_coredump_upload(struct work_struct *work) +{ +} + +static inline void ath11k_coredump_collect(struct ath11k_base *ab) +{ +} +#endif + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index 674ff772b181..770c39ff99b4 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -31,6 +31,7 @@ struct ath11k_hif_ops { void (*ce_irq_enable)(struct ath11k_base *ab); void (*ce_irq_disable)(struct ath11k_base *ab); void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); + void (*coredump_download)(struct ath11k_base *ab); }; static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) @@ -146,4 +147,10 @@ static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, *msi_data_idx = ce_id; } +static inline void ath11k_hif_coredump_download(struct ath11k_base *ab) +{ + if (ab->hif.ops->coredump_download) + ab->hif.ops->coredump_download(ab); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index 6e45f464a429..fc77eac83e95 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -491,3 +491,8 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci) return 0; } + +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic) +{ + mhi_download_rddm_image(mhi_ctrl, in_panic); +} diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index a682aad52fc5..651470091bd5 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -26,5 +26,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab); int ath11k_mhi_suspend(struct ath11k_pci *ar_pci); int ath11k_mhi_resume(struct ath11k_pci *ar_pci); +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic); #endif diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index f1121e331719..6565ac1917b7 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "pci.h" #include "core.h" @@ -610,6 +612,187 @@ static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) PCI_EXP_LNKCTL_ASPMC); } +#ifdef CONFIG_DEV_COREDUMP +static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct ath11k_tlv_dump_data *dump_tlv; + enum ath11k_fw_crash_dump_type mem_type; + u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0; + struct ath11k_dump_file_data *file_data; + int i; + + rddm_img = mhi_ctrl->rddm_image; + if (!rddm_img) { + ath11k_err(ab, "No RDDM dump found\n"); + return 0; + } + + fw_img = mhi_ctrl->fbc_image; + + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + paging_tlv_sz += fw_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz; + + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + rddm_tlv_sz += rddm_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type); + + if (mem_type == FW_CRASH_DUMP_NONE) + continue; + + if (mem_type == FW_CRASH_DUMP_TYPE_MAX) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "target mem region type %d not supported", + ab->qmi.target_mem[i].type); + continue; + } + + if (!ab->qmi.target_mem[i].anyaddr) + continue; + + dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size; + } + + for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) { + if (!dump_seg_sz[i]) + continue; + + len += sizeof(*dump_tlv) + dump_seg_sz[i]; + } + + if (len) + len += sizeof(*file_data); + + return len; +} + +static void ath11k_pci_coredump_download(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct timespec64 timestamp; + int i, len, mem_idx; + enum ath11k_fw_crash_dump_type mem_type; + struct ath11k_dump_file_data *file_data; + struct ath11k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*file_data); + void *buf; + u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = { 0 }; + + ath11k_mhi_coredump(mhi_ctrl, false); + + len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz); + if (!len) { + ath11k_warn(ab, "No crash dump data found for devcoredump"); + return; + } + + rddm_img = mhi_ctrl->rddm_image; + fw_img = mhi_ctrl->fbc_image; + + /* dev_coredumpv() requires vmalloc data */ + buf = vzalloc(len); + if (!buf) + return; + + ab->dump_data = buf; + ab->ath11k_coredump_len = len; + file_data = ab->dump_data; + strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic)); + file_data->len = cpu_to_le32(len); + file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2); + file_data->chip_id = cpu_to_le32(ab_pci->dev_id); + file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id); + file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus)); + guid_gen(&file_data->guid); + ktime_get_real_ts64(×tamp); + file_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + buf += hdr_len; + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf, + fw_img->mhi_buf[i].len); + buf += fw_img->mhi_buf[i].len; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf, + rddm_img->mhi_buf[i].len); + buf += rddm_img->mhi_buf[i].len; + } + + mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA; + for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) { + if (mem_idx == FW_CRASH_DUMP_NONE) + continue; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type + (ab->qmi.target_mem[i].type); + + if (mem_type != mem_idx) + continue; + + if (!ab->qmi.target_mem[i].anyaddr) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "Skipping mem region type %d", + ab->qmi.target_mem[i].type); + continue; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(mem_idx); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]); + buf += COREDUMP_TLV_HDR_SIZE; + + memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr, + ab->qmi.target_mem[i].size); + + buf += ab->qmi.target_mem[i].size; + } + } + + queue_work(ab->workqueue, &ab->dump_work); +} +#endif + static int ath11k_pci_power_up(struct ath11k_base *ab) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -713,6 +896,9 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = { .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, +#ifdef CONFIG_DEV_COREDUMP + .coredump_download = ath11k_pci_coredump_download, +#endif }; static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor) @@ -981,6 +1167,8 @@ static void ath11k_pci_remove(struct pci_dev *pdev) set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->reset_work); + cancel_work_sync(&ab->dump_work); ath11k_core_deinit(ab); qmi_fail: diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index fdf9b5f8c19f..7968ab122b65 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -157,6 +157,7 @@ struct ath11k_qmi { #define BDF_MEM_REGION_TYPE 0x2 #define M3_DUMP_REGION_TYPE 0x3 #define CALDB_MEM_REGION_TYPE 0x4 +#define PAGEABLE_MEM_REGION_TYPE 0x9 struct qmi_wlanfw_host_cap_req_msg_v01 { u8 num_clients_valid; From 81ee5441acf4c89a817f133e03107d42b7e83bea Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 198/465] wifi: ath12k: cleanup ath12k_mac_mlo_ready() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ceb3b35f5ef4a0c490f54eb8b53075fd83a97d11 Author: Ethan Carter Edwards Date: Mon Feb 10 21:49:41 2025 -0500 wifi: ath12k: cleanup ath12k_mac_mlo_ready() There is a possibility for an uninitialized *ret* variable to be returned in some code paths. This explicitly returns 0 without an error. Also removes goto that returned *ret* and simply returns in place. Closes: https://scan5.scan.coverity.com/#/project-view/63541/10063?selectedIssue=1642337 Fixes: b716a10d99a2 ("wifi: ath12k: enable MLO setup and teardown from core") Signed-off-by: Ethan Carter Edwards Reviewed-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250210-ath12k-uninit-v2-1-3596f28dd380@ethancedwards.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 0c556023141b..0b2dec081c6e 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -961,12 +961,11 @@ int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag) ar = &ah->radio[j]; ret = __ath12k_mac_mlo_ready(ar); if (ret) - goto out; + return ret; } } -out: - return ret; + return 0; } static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag) From b42a24fd62aad8868188464e07d9a198ba89a018 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 199/465] wifi: ath12k: remove redundant declaration of ath12k_dp_rx_h_find_peer() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cb9a978a20a4481dd59c3ea8c6fee36b3dac2b47 Author: P Praneesh Date: Wed Feb 19 11:06:40 2025 +0530 wifi: ath12k: remove redundant declaration of ath12k_dp_rx_h_find_peer() The current code in dp_rx.h declares the ath12k_dp_rx_h_find_peer() twice. Fix this by removing one of the redundant declarations of ath12k_dp_rx_h_find_peer() to ensure that the function is declared only once. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Link: https://patch.msgid.link/20250219053640.223734-1-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_rx.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index c0aa965f47e7..88e42365a9d8 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -138,9 +138,6 @@ u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab, struct hal_rx_desc *desc); void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, struct ieee80211_rx_status *rx_status); -struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); - int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab); int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab); From 0c0904c3f8587862ccda05f4c13734820e97d489 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 200/465] wifi: ath12k: Add missing htt_metadata flag in ath12k_dp_tx() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit af1c6007a64e78b729eb5a8d149637a820077bee Author: Nicolas Escande Date: Fri Jan 24 12:33:31 2025 +0100 wifi: ath12k: Add missing htt_metadata flag in ath12k_dp_tx() When AP-VLAN support was added, the HTT_TCL_META_DATA_VALID_HTT flag was not added to the tx_info's meta_data_flags. Without this flag the firmware seems to reject all the broadcast (ap-vlan) frames. So add the flag, just as ath11k did it in the downstream QSDK project[1]. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Fixes: 26dd8ccdba4d ("wifi: ath12k: dynamic VLAN support") Signed-off-by: Nicolas Escande Link: https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/blob/win.wlan_host_opensource.3.0.r24/patches/ath11k/207-ath11k-Add-support-for-dynamic-vlan.patch # [1] Link: https://patch.msgid.link/20250124113331.93476-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 4e11b7b61b82..46a55554c19c 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -384,6 +384,7 @@ map: add_htt_metadata = true; msdu_ext_desc = true; ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT; ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW; ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; } From 4360791b82dcdabfe3ac54ddb6f224cc3ba969f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:43 +0200 Subject: [PATCH 201/465] wifi: rtw88: Add support for Mercusys MA30N and D-Link DWA-T185 rev. A1 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 80c4668d024ff7b5427d90b5fad655ce9461c7b1 Author: Zenm Chen Date: Mon Feb 10 15:36:10 2025 +0800 wifi: rtw88: Add support for Mercusys MA30N and D-Link DWA-T185 rev. A1 Add two more USB IDs found in https://github.com/RinCat/RTL88x2BU-Linux-Driver to support Mercusys MA30N and D-Link DWA-T185 rev. A1. Signed-off-by: Zenm Chen Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250210073610.4174-1-zenmchen@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/rtw8822bu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c index 8883300fc6ad..572d1f31832e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c @@ -73,6 +73,10 @@ static const struct usb_device_id rtw_8822bu_id_table[] = { .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ELECOM WDB-867DU3S */ { USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x0107, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30H */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x010a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30N */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3322, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* D-Link DWA-T185 rev. A1 */ {}, }; MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table); From 4cb2e92a67df462ef23bdac0ba7921b7e3ab6740 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 202/465] wifi: rtw89: rtw8852b{t}: fix TSSI debug timestamps JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bfc8e71ef6b7913f0129aff47951cab73a175259 Author: Dmitry Antipov Date: Thu Feb 13 12:50:06 2025 +0300 wifi: rtw89: rtw8852b{t}: fix TSSI debug timestamps Since the vendor driver is claimed to measure 'tssi_alimk_time' of 'struct rtw89_tssi_info' in microseconds, adjust rtw8852b{t}-specific '_tssi_alimentk()' to not mess the former with nanoseconds and print both per-call and accumulated times. Compile tested only. Fixes: 7f18a70d7b4d ("wifi: rtw89: 8852b: rfk: add TSSI") Signed-off-by: Dmitry Antipov Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250213095006.1308810-1-dmantipov@yandex.ru Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c | 13 +++++++------ drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c | 13 +++++++------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b3fdd8eded21..eb2a6b90c940 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5141,7 +5141,7 @@ struct rtw89_tssi_info { u32 alignment_backup_by_ch[RF_PATH_MAX][TSSI_MAX_CH_NUM][TSSI_ALIMK_VALUE_NUM]; u32 alignment_value[RF_PATH_MAX][TSSI_ALIMK_MAX][TSSI_ALIMK_VALUE_NUM]; bool alignment_done[RF_PATH_MAX][TSSI_ALIMK_MAX]; - u32 tssi_alimk_time; + u64 tssi_alimk_time; }; struct rtw89_power_trim_info { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index ef47a5facc83..fbf82d42687b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -3585,9 +3585,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel); struct rtw8852bx_bb_tssi_bak tssi_bak; s32 aliment_diff, tssi_cw_default; - u32 start_time, finish_time; u32 bb_reg_backup[8] = {0}; + ktime_t start_time; const s16 *power; + s64 this_time; u8 band; bool ok; u32 tmp; @@ -3613,7 +3614,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, return; } - start_time = ktime_get_ns(); + start_time = ktime_get(); if (chan->band_type == RTW89_BAND_2G) power = power_2g; @@ -3738,12 +3739,12 @@ out: rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak); rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); - finish_time = ktime_get_ns(); - tssi_info->tssi_alimk_time += finish_time - start_time; + this_time = ktime_us_delta(ktime_get(), start_time); + tssi_info->tssi_alimk_time += this_time; rtw89_debug(rtwdev, RTW89_DBG_RFK, - "[TSSI PA K] %s processing time = %d ms\n", __func__, - tssi_info->tssi_alimk_time); + "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", + __func__, this_time, tssi_info->tssi_alimk_time); } void rtw8852b_dpk_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c index 336a83e1d46b..6e6889eea9a0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c @@ -3663,9 +3663,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel); struct rtw8852bx_bb_tssi_bak tssi_bak; s32 aliment_diff, tssi_cw_default; - u32 start_time, finish_time; u32 bb_reg_backup[8] = {}; + ktime_t start_time; const s16 *power; + s64 this_time; u8 band; bool ok; u32 tmp; @@ -3675,7 +3676,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, "======> %s channel=%d path=%d\n", __func__, channel, path); - start_time = ktime_get_ns(); + start_time = ktime_get(); if (chan->band_type == RTW89_BAND_2G) power = power_2g; @@ -3802,12 +3803,12 @@ out: rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak); rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); - finish_time = ktime_get_ns(); - tssi_info->tssi_alimk_time += finish_time - start_time; + this_time = ktime_us_delta(ktime_get(), start_time); + tssi_info->tssi_alimk_time += this_time; rtw89_debug(rtwdev, RTW89_DBG_RFK, - "[TSSI PA K] %s processing time = %d ms\n", __func__, - tssi_info->tssi_alimk_time); + "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", + __func__, this_time, tssi_info->tssi_alimk_time); } void rtw8852bt_dpk_init(struct rtw89_dev *rtwdev) From 8998cf2dbd242b29136f06b3de0b896d5b3e990c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 203/465] wifi: rtw89: 8922a: fix incorrect STA-ID in EHT MU PPDU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bdce0574243b43b3bb2064f609c0c326df44c4c6 Author: Kuan-Chung Chen Date: Mon Feb 17 14:12:35 2025 +0800 wifi: rtw89: 8922a: fix incorrect STA-ID in EHT MU PPDU EHT MU PPDU contains user field of EHT-SIG field with STA-ID that must match AID subfield in the Associate Response. Add a necessary setting to prevent these from being inconsistent. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217061235.32031-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 35b86970db2a..6dbea18355a3 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3435,9 +3435,10 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev, CCTLINFO_G7_W5_NOMINAL_PKT_PADDING3 | CCTLINFO_G7_W5_NOMINAL_PKT_PADDING4); - h2c->w6 = le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0, + h2c->w6 = le32_encode_bits(vif->cfg.aid, CCTLINFO_G7_W6_AID12_PAID) | + le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0, CCTLINFO_G7_W6_ULDL); - h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_ULDL); + h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_AID12_PAID | CCTLINFO_G7_W6_ULDL); if (rtwsta_link) { h2c->w8 = le32_encode_bits(link_sta->he_cap.has_he, From 4a28f682ca82eebe3636cc382efd0141f08b9db3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 204/465] wifi: rtw89: mac: define registers of agg_limit and txcnt_limit to share common flow JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a5b8fd3f07d780f8ec48ca1cb0a3d26b29412b28 Author: Ping-Ke Shih Date: Mon Feb 17 14:30:52 2025 +0800 wifi: rtw89: mac: define registers of agg_limit and txcnt_limit to share common flow The agg_limit and txcnt_limit are used by BT-coexistence to reduce WiFi TX time at once to share time with Bluetooth devices. Since these registers address are different from WiFi 6 and 7 chips, define them accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217063053.38936-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/mac.c | 17 +++++++++++------ drivers/net/wireless/realtek/rtw89/mac.h | 2 ++ drivers/net/wireless/realtek/rtw89/mac_be.c | 2 ++ drivers/net/wireless/realtek/rtw89/reg.h | 7 +++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3f38f8ed6876..0b2e4ad52774 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6433,6 +6433,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ u32 tx_time) { #define MAC_AX_DFLT_TX_TIME 5280 + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time; u32 reg; @@ -6448,8 +6449,8 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); - rtw89_write32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK, + reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); + rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask, max_tx_time >> 5); } @@ -6475,6 +6476,7 @@ int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link, u32 *tx_time) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 reg; int ret = 0; @@ -6488,8 +6490,8 @@ int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); - *tx_time = rtw89_read32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK) << 5; + reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); + *tx_time = rtw89_read32_mask(rtwdev, reg, mac->agg_limit.mask) << 5; } return ret; @@ -6517,6 +6519,7 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev, int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link, u8 *tx_retry) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 reg; int ret = 0; @@ -6530,8 +6533,8 @@ int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev, return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TXCNT, mac_idx); - *tx_retry = rtw89_read32_mask(rtwdev, reg, B_AX_L_TXCNT_LMT_MASK); + reg = rtw89_mac_reg_by_idx(rtwdev, mac->txcnt_limit.addr, mac_idx); + *tx_retry = rtw89_read32_mask(rtwdev, reg, mac->txcnt_limit.mask); } return ret; @@ -6809,6 +6812,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .mask = B_AX_RXTRIG_RU26_DIS, }, .wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,}, + .agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,}, + .txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_ax, .sys_init = sys_init_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 6389f54264e5..4d5a06a2fe9b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -964,6 +964,8 @@ struct rtw89_mac_gen_def { struct rtw89_reg_def bfee_ctrl; struct rtw89_reg_def narrow_bw_ru_dis; struct rtw89_reg_def wow_ctrl; + struct rtw89_reg_def agg_limit; + struct rtw89_reg_def txcnt_limit; int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band, enum rtw89_mac_hwmod_sel sel); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 16ee378c5418..dce0a4b01440 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2585,6 +2585,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .mask = B_BE_RXTRIG_RU26_DIS, }, .wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,}, + .agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,}, + .txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_be, .sys_init = sys_init_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c992835d4b63..ec06f5bd0fab 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6618,6 +6618,13 @@ #define B_BE_RTS_LIMIT_IN_OFDM6 BIT(1) #define B_BE_CHECK_CCK_EN BIT(0) +#define R_BE_TXCNT 0x1082C +#define R_BE_TXCNT_C1 0x1482C +#define B_BE_ADD_TXCNT_BY BIT(31) +#define B_BE_TOTAL_TC_OPT BIT(30) +#define B_BE_S_TXCNT_LMT_MASK GENMASK(29, 24) +#define B_BE_L_TXCNT_LMT_MASK GENMASK(21, 16) + #define R_BE_MBSSID_DROP_0 0x1083C #define R_BE_MBSSID_DROP_0_C1 0x1483C #define B_BE_GI_LTF_FB_SEL BIT(30) From 029d34e69c376a0de6d6540ada0c00d5b3e36abf Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 205/465] wifi: rtw89: add H2C command of TX time for WiFi 7 chips JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c852d2abee30f464cbf08369e985219fe850dd0f Author: Ping-Ke Shih Date: Mon Feb 17 14:30:53 2025 +0800 wifi: rtw89: add H2C command of TX time for WiFi 7 chips BT-coexistence configure WiFi TX time to share time slots with Bluetooth devices. Since the format is different from WiFi 6 chips, add the new format accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217063053.38936-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 2 + drivers/net/wireless/realtek/rtw89/fw.c | 55 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 11 ++++ drivers/net/wireless/realtek/rtw89/mac.c | 6 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 10 files changed, 77 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index eb2a6b90c940..542390f1d28c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3676,6 +3676,8 @@ struct rtw89_chip_ops { int (*h2c_ampdu_cmac_tbl)(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); + int (*h2c_txtime_cmac_tbl)(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link); int (*h2c_default_dmac_tbl)(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 6dbea18355a3..eff94f0592c9 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3574,6 +3574,61 @@ fail: return ret; } +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl); + +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link) +{ + struct rtw89_h2c_cctlinfo_ud_g7 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for txtime_cmac_g7\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cctlinfo_ud_g7 *)skb->data; + + h2c->c0 = le32_encode_bits(rtwsta_link->mac_id, CCTLINFO_G7_C0_MACID) | + le32_encode_bits(1, CCTLINFO_G7_C0_OP); + + if (rtwsta_link->cctl_tx_time) { + h2c->w3 |= le32_encode_bits(1, CCTLINFO_G7_W3_AMPDU_TIME_SEL); + h2c->m3 |= cpu_to_le32(CCTLINFO_G7_W3_AMPDU_TIME_SEL); + + h2c->w2 |= le32_encode_bits(rtwsta_link->ampdu_max_time, + CCTLINFO_G7_W2_AMPDU_MAX_TIME); + h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_AMPDU_MAX_TIME); + } + if (rtwsta_link->cctl_tx_retry_limit) { + h2c->w2 |= le32_encode_bits(1, CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL) | + le32_encode_bits(rtwsta_link->data_tx_cnt_lmt, + CCTLINFO_G7_W2_DATA_TX_CNT_LMT); + h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL | + CCTLINFO_G7_W2_DATA_TX_CNT_LMT); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, + H2C_FUNC_MAC_CCTLINFO_UD_G7, 0, 1, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl_g7); int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 994d109a9c3c..53a4083ba128 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4588,6 +4588,8 @@ int rtw89_fw_h2c_ampdu_cmac_tbl_g7(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev, @@ -4869,6 +4871,15 @@ static inline int rtw89_chip_h2c_ampdu_cmac_tbl(struct rtw89_dev *rtwdev, return 0; } +static inline +int rtw89_chip_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return chip->ops->h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +} + static inline int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, bool valid, struct ieee80211_ampdu_params *params) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 0b2e4ad52774..513c317b286c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6441,7 +6441,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ if (rtwsta_link->cctl_tx_time) { rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9; - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); } else { ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); if (ret) { @@ -6507,9 +6507,9 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev, if (!resume) { rtwsta_link->cctl_tx_retry_limit = true; - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); } else { - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); rtwsta_link->cctl_tx_retry_limit = false; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 82289dbad1f4..3ef07d80caf1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2423,6 +2423,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 2046832d021f..bcd71d6fce29 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2149,6 +2149,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 652914a36245..2ef534b4e951 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -776,6 +776,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 6f15245b2f74..8f2dccc07bb5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -710,6 +710,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index ecc1ff358583..5876b1e8bb6b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2941,6 +2941,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 898a65a721dc..f3661eb894dc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2702,6 +2702,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_g7, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_g7, .h2c_ampdu_cmac_tbl = rtw89_fw_h2c_ampdu_cmac_tbl_g7, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl_g7, .h2c_default_dmac_tbl = rtw89_fw_h2c_default_dmac_tbl_v2, .h2c_update_beacon = rtw89_fw_h2c_update_beacon_be, .h2c_ba_cam = rtw89_fw_h2c_ba_cam_v1, From 20d86fab696b85bd648c4ee4639a07ad7e13575d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 206/465] wifi: rtw89: fw: add blacklist to avoid obsolete secure firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f11d042b3a2e92ab1aa10e0da8e290bcdcf31d39 Author: Ping-Ke Shih Date: Mon Feb 17 14:43:04 2025 +0800 wifi: rtw89: fw: add blacklist to avoid obsolete secure firmware To ensure secure chip only runs expected secure firmware, stop using obsolete firmware in blacklist which weakness or flaw was found. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217064308.43559-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 2 + drivers/net/wireless/realtek/rtw89/fw.c | 52 ++++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 12 +++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 9 files changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 542390f1d28c..865cdcfa59e1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -17,6 +17,7 @@ struct rtw89_dev; struct rtw89_pci_info; struct rtw89_mac_gen_def; struct rtw89_phy_gen_def; +struct rtw89_fw_blacklist; struct rtw89_efuse_block_cfg; struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; @@ -4259,6 +4260,7 @@ struct rtw89_chip_info { bool try_ce_fw; u8 bbmcu_nr; u32 needed_fw_elms; + const struct rtw89_fw_blacklist *fw_blacklist; u32 fifo_size; bool small_fifo_size; u32 dle_scc_rsvd_size; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index eff94f0592c9..e4231dd620e6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -38,6 +38,16 @@ struct rtw89_arp_rsp { static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C}; +const struct rtw89_fw_blacklist rtw89_fw_blacklist_default = { + .ver = 0x00, + .list = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, +}; +EXPORT_SYMBOL(rtw89_fw_blacklist_default); + union rtw89_fw_element_arg { size_t offset; enum rtw89_rf_path rf_path; @@ -344,6 +354,46 @@ ignore: return 0; } +static int __check_secure_blacklist(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr_section_info *section_info, + const void *content) +{ + const struct rtw89_fw_blacklist *chip_blacklist = rtwdev->chip->fw_blacklist; + const union rtw89_fw_section_mssc_content *section_content = content; + struct rtw89_fw_secure *sec = &rtwdev->fw.sec; + u8 byte_idx; + u8 bit_mask; + + if (!sec->secure_boot) + return 0; + + if (!info->secure_section_exist || section_info->ignore) + return 0; + + if (!chip_blacklist) { + rtw89_err(rtwdev, "chip no blacklist for secure firmware\n"); + return -ENOENT; + } + + byte_idx = section_content->blacklist.bit_in_chip_list >> 3; + bit_mask = BIT(section_content->blacklist.bit_in_chip_list & 0x7); + + if (section_content->blacklist.ver > chip_blacklist->ver) { + rtw89_err(rtwdev, "chip blacklist out of date (%u, %u)\n", + section_content->blacklist.ver, chip_blacklist->ver); + return -EINVAL; + } + + if (chip_blacklist->list[byte_idx] & bit_mask) { + rtw89_err(rtwdev, "firmware %u in chip blacklist\n", + section_content->blacklist.ver); + return -EPERM; + } + + return 0; +} + static int __parse_security_section(struct rtw89_dev *rtwdev, struct rtw89_fw_bin_info *info, struct rtw89_fw_hdr_section_info *section_info, @@ -374,7 +424,7 @@ static int __parse_security_section(struct rtw89_dev *rtwdev, info->secure_section_exist = true; } - return 0; + return __check_secure_blacklist(rtwdev, info, section_info, content); } static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 len, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 53a4083ba128..1c53c1e22439 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -663,6 +663,11 @@ struct rtw89_fw_mss_pool_hdr { } __packed; union rtw89_fw_section_mssc_content { + struct { + u8 pad[0x20]; + u8 bit_in_chip_list; + u8 ver; + } __packed blacklist; struct { u8 pad[58]; __le32 v; @@ -673,6 +678,13 @@ union rtw89_fw_section_mssc_content { } __packed key_sign_len; } __packed; +struct rtw89_fw_blacklist { + u8 ver; + u8 list[32]; +}; + +extern const struct rtw89_fw_blacklist rtw89_fw_blacklist_default; + static inline void SET_CTRL_INFO_MACID(void *table, u32 val) { le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0)); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 3ef07d80caf1..3cd1d7a4d202 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2459,6 +2459,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = NULL, .fifo_size = 196608, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index bcd71d6fce29..514ef0c2b32b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2176,6 +2176,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = NULL, .fifo_size = 458752, .small_fifo_size = false, .dle_scc_rsvd_size = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 2ef534b4e951..5dbb33d5e403 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -812,6 +812,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 196608, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 8f2dccc07bb5..b4832c7897a8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -746,6 +746,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 458752, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 5876b1e8bb6b..f3965a65ae18 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2968,6 +2968,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 458752, .small_fifo_size = false, .dle_scc_rsvd_size = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index f3661eb894dc..defa6cf9f7f0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2729,6 +2729,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .try_ce_fw = false, .bbmcu_nr = 1, .needed_fw_elms = RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 589824, .small_fifo_size = false, .dle_scc_rsvd_size = 0, From ce6cb9568c7ddcc9d5c0b9a1f3c0f75e058dde8f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 207/465] wifi: rtw89: fw: get sb_sel_ver via get_unaligned_le32() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2f9da853f4d848d23bade4c22931ea0f5a011674 Author: Ping-Ke Shih Date: Mon Feb 17 14:43:05 2025 +0800 wifi: rtw89: fw: get sb_sel_ver via get_unaligned_le32() The sb_sel_ver is selection version for secure boot recorded in firmware binary data, and its size is 4 and offset is 58 (not natural alignment). Use get_unaligned_le32() to get this value safely. Find this by reviewing. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217064308.43559-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index e4231dd620e6..0a129476171a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -324,7 +324,7 @@ static int __parse_formatted_mssc(struct rtw89_dev *rtwdev, if (!sec->secure_boot) goto out; - sb_sel_ver = le32_to_cpu(section_content->sb_sel_ver.v); + sb_sel_ver = get_unaligned_le32(§ion_content->sb_sel_ver.v); if (sb_sel_ver && sb_sel_ver != sec->sb_sel_mgn) goto ignore; From ee6da79d2ee3456f061f407d216fe1231defe757 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 208/465] wifi: rtw89: fw: propagate error code from rtw89_h2c_tx() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 56e1acaa0f80620b8e2c3410db35b4b975782b0a Author: Ping-Ke Shih Date: Mon Feb 17 14:43:06 2025 +0800 wifi: rtw89: fw: propagate error code from rtw89_h2c_tx() The error code should be propagated to callers during downloading firmware header and body. Remove unnecessary assignment of -1. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217064308.43559-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0a129476171a..e05034fbb898 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1510,7 +1510,6 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { rtw89_err(rtwdev, "failed to send h2c\n"); - ret = -1; goto fail; } @@ -1597,7 +1596,6 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, ret = rtw89_h2c_tx(rtwdev, skb, true); if (ret) { rtw89_err(rtwdev, "failed to send h2c\n"); - ret = -1; goto fail; } From 6e9e82e8ce8b25c7041d2f97061c8142ae13fee3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 209/465] wifi: rtw89: fw: add debug message for unexpected secure firmware JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dc2fc1a3419e9218fde6e7897c8c238ebe75a69e Author: Ping-Ke Shih Date: Mon Feb 17 14:43:07 2025 +0800 wifi: rtw89: fw: add debug message for unexpected secure firmware If failed to load a non-secure firmware with a secure chip, it only throws a unclear message: rtw89_8922ae 0000:03:00.0: parse fw header fail To address this case simpler, add a message to point out this. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217064308.43559-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index e05034fbb898..0cb2fee916cd 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -414,8 +414,11 @@ static int __parse_security_section(struct rtw89_dev *rtwdev, *mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN; if (sec->secure_boot) { - if (sec->mss_idx >= section_info->mssc) + if (sec->mss_idx >= section_info->mssc) { + rtw89_err(rtwdev, "unexpected MSS %d >= %d\n", + sec->mss_idx, section_info->mssc); return -EFAULT; + } section_info->key_addr = content + section_info->len + sec->mss_idx * FWDL_SECURITY_SIGLEN; section_info->key_len = FWDL_SECURITY_SIGLEN; From 4ae0b507f158d76be5e05b1e2b0869150133b091 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:44 +0200 Subject: [PATCH 210/465] wifi: rtw89: fw: safely cast mfw_hdr pointer from firmware->data JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e0722103306fd5fd3cdde8d100a7588bfe7abdf0 Author: Ping-Ke Shih Date: Mon Feb 17 14:43:08 2025 +0800 wifi: rtw89: fw: safely cast mfw_hdr pointer from firmware->data Check size of struct mfw_hdr within firmware->size before type casting to ensure to validly dereference fields from mfm_hdr pointer. Then, check if signature field is equal to RTW89_MFW_SIG to assert current is multi-firmware. Addresses-Coverity-ID: 1494046 ("Untrusted loop bound") Addresses-Coverity-ID: 1544385 ("Untrusted array index read") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250217064308.43559-6-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0cb2fee916cd..b843d259fbfa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -542,6 +542,23 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, } } +static +const struct rtw89_mfw_hdr *rtw89_mfw_get_hdr_ptr(struct rtw89_dev *rtwdev, + const struct firmware *firmware) +{ + const struct rtw89_mfw_hdr *mfw_hdr; + + if (sizeof(*mfw_hdr) > firmware->size) + return NULL; + + mfw_hdr = (const struct rtw89_mfw_hdr *)firmware->data; + + if (mfw_hdr->sig != RTW89_MFW_SIG) + return NULL; + + return mfw_hdr; +} + static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev, const struct firmware *firmware, const struct rtw89_mfw_hdr *mfw_hdr) @@ -572,14 +589,15 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, { struct rtw89_fw_info *fw_info = &rtwdev->fw; const struct firmware *firmware = fw_info->req.firmware; + const struct rtw89_mfw_info *mfw_info = NULL, *tmp; + const struct rtw89_mfw_hdr *mfw_hdr; const u8 *mfw = firmware->data; u32 mfw_len = firmware->size; - const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw; - const struct rtw89_mfw_info *mfw_info = NULL, *tmp; int ret; int i; - if (mfw_hdr->sig != RTW89_MFW_SIG) { + mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); + if (!mfw_hdr) { rtw89_debug(rtwdev, RTW89_DBG_FW, "use legacy firmware\n"); /* legacy firmware support normal type only */ if (type != RTW89_FW_NORMAL) @@ -635,13 +653,13 @@ static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev) { struct rtw89_fw_info *fw_info = &rtwdev->fw; const struct firmware *firmware = fw_info->req.firmware; - const struct rtw89_mfw_hdr *mfw_hdr = - (const struct rtw89_mfw_hdr *)firmware->data; const struct rtw89_mfw_info *mfw_info; + const struct rtw89_mfw_hdr *mfw_hdr; u32 size; int ret; - if (mfw_hdr->sig != RTW89_MFW_SIG) { + mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); + if (!mfw_hdr) { rtw89_warn(rtwdev, "not mfw format\n"); return 0; } From 061e938b5339ca5e15ff2b1e695c62bda559ae0a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 211/465] wifi: rtw88: Fix rtw_mac_power_switch() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e66bca16638ee59e997f9d9a3711b3ae587d04d9 Author: Bitterblue Smith Date: Tue Feb 18 01:28:59 2025 +0200 wifi: rtw88: Fix rtw_mac_power_switch() for RTL8814AU rtw_mac_power_switch() checks bit 8 of REG_SYS_STATUS1 to see if the chip is powered on. This bit appears to be always on in the RTL8814AU, so ignore it. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/2f0fcffb-3067-4d95-a68c-f2f3a5a47921@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 63edf6461de8..0491f501c138 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -291,6 +291,7 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) if (rtw_read8(rtwdev, REG_CR) == 0xea) cur_pwr = false; else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + chip->id != RTW_CHIP_TYPE_8814A && (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0))) cur_pwr = false; else From 00fd767c7a6f84a122dba67a4bd41a2900e549af Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 212/465] wifi: rtw88: Fix rtw_desc_to_mcsrate() to handle MCS16-31 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 86d04f8f991a0509e318fe886d5a1cf795736c7d Author: Bitterblue Smith Date: Tue Feb 18 01:29:52 2025 +0200 wifi: rtw88: Fix rtw_desc_to_mcsrate() to handle MCS16-31 This function translates the rate number reported by the hardware into something mac80211 can understand. It was ignoring the 3SS and 4SS HT rates. Translate them too. Also set *nss to 0 for the HT rates, just to make sure it's initialised. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d0a5a86b-4869-47f6-a5a7-01c0f987cc7f@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c index e222d3c01a77..66819f694405 100644 --- a/drivers/net/wireless/realtek/rtw88/util.c +++ b/drivers/net/wireless/realtek/rtw88/util.c @@ -101,7 +101,8 @@ void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) *nss = 4; *mcs = rate - DESC_RATEVHT4SS_MCS0; } else if (rate >= DESC_RATEMCS0 && - rate <= DESC_RATEMCS15) { + rate <= DESC_RATEMCS31) { + *nss = 0; *mcs = rate - DESC_RATEMCS0; } } From 77a2516a5e54c679d0b56c369d09a8f11967f7e3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 213/465] wifi: rtw88: Fix rtw_init_ht_cap() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c7eea1ba05ca5b0dbf77a27cf2e1e6e2fb3c0043 Author: Bitterblue Smith Date: Tue Feb 18 01:30:22 2025 +0200 wifi: rtw88: Fix rtw_init_ht_cap() for RTL8814AU Set the RX mask and the highest RX rate according to the number of spatial streams the chip can receive. For RTL8814AU that is 3. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/4e786f50-ed1c-4387-8b28-e6ff00e35e81@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index e4f9b744f24d..3319bb5044c3 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1565,6 +1565,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev, { const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; + int i; ht_cap->ht_supported = true; ht_cap->cap = 0; @@ -1584,17 +1585,11 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev, ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_cap->ampdu_density = chip->ampdu_density; ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (efuse->hw_cap.nss > 1) { - ht_cap->mcs.rx_mask[0] = 0xFF; - ht_cap->mcs.rx_mask[1] = 0xFF; - ht_cap->mcs.rx_mask[4] = 0x01; - ht_cap->mcs.rx_highest = cpu_to_le16(300); - } else { - ht_cap->mcs.rx_mask[0] = 0xFF; - ht_cap->mcs.rx_mask[1] = 0x00; - ht_cap->mcs.rx_mask[4] = 0x01; - ht_cap->mcs.rx_highest = cpu_to_le16(150); - } + + for (i = 0; i < efuse->hw_cap.nss; i++) + ht_cap->mcs.rx_mask[i] = 0xFF; + ht_cap->mcs.rx_mask[4] = 0x01; + ht_cap->mcs.rx_highest = cpu_to_le16(150 * efuse->hw_cap.nss); } static void rtw_init_vht_cap(struct rtw_dev *rtwdev, From 3f00ac8621090fa1e78c8bbacfdc01f1b02dac46 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 214/465] wifi: rtw88: Fix rtw_init_vht_cap() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6be7544d19fcfcb729495e793bc6181f85bb8949 Author: Bitterblue Smith Date: Tue Feb 18 01:30:48 2025 +0200 wifi: rtw88: Fix rtw_init_vht_cap() for RTL8814AU Set the MCS maps and the highest rates according to the number of spatial streams the chip has. For RTL8814AU that is 3. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/e86aa009-b5bf-4b3a-8112-ea5e3cd49465@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 3319bb5044c3..959f56a3cc1a 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1596,8 +1596,9 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev, struct ieee80211_sta_vht_cap *vht_cap) { struct rtw_efuse *efuse = &rtwdev->efuse; - u16 mcs_map; + u16 mcs_map = 0; __le16 highest; + int i; if (efuse->hw_cap.ptcl != EFUSE_HW_CAP_IGNORE && efuse->hw_cap.ptcl != EFUSE_HW_CAP_PTCL_VHT) @@ -1620,21 +1621,15 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev, if (rtw_chip_has_rx_ldpc(rtwdev)) vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; - mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; - if (efuse->hw_cap.nss > 1) { - highest = cpu_to_le16(780); - mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2; - } else { - highest = cpu_to_le16(390); - mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << 2; + for (i = 0; i < 8; i++) { + if (i < efuse->hw_cap.nss) + mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); } + highest = cpu_to_le16(390 * efuse->hw_cap.nss); + vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.rx_highest = highest; From e14c9255a51f3233148675f202f98cde4b82ab17 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 215/465] wifi: rtw88: Fix rtw_rx_phy_stat() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 053a7aace0207593776c729f229d87f1be464b98 Author: Bitterblue Smith Date: Tue Feb 18 01:31:13 2025 +0200 wifi: rtw88: Fix rtw_rx_phy_stat() for RTL8814AU Record statistics for the 3SS rates too. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/39e3c7cf-37ed-4c0e-af00-dcd9eab351f0@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 7 +++++++ drivers/net/wireless/realtek/rtw88/rx.c | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 24ac749271cc..c15e0f55d09a 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -386,6 +386,9 @@ enum rtw_evm { RTW_EVM_1SS, RTW_EVM_2SS_A, RTW_EVM_2SS_B, + RTW_EVM_3SS_A, + RTW_EVM_3SS_B, + RTW_EVM_3SS_C, /* keep it last */ RTW_EVM_NUM }; @@ -403,6 +406,10 @@ enum rtw_snr { RTW_SNR_2SS_B, RTW_SNR_2SS_C, RTW_SNR_2SS_D, + RTW_SNR_3SS_A, + RTW_SNR_3SS_B, + RTW_SNR_3SS_C, + RTW_SNR_3SS_D, /* keep it last */ RTW_SNR_NUM }; diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index 90fc8a5fa89e..8b0afaaffaa0 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -73,6 +73,12 @@ static void rtw_rx_phy_stat(struct rtw_dev *rtwdev, rate_ss_evm = 2; evm_id = RTW_EVM_2SS_A; break; + case DESC_RATEMCS16...DESC_RATEMCS23: + case DESC_RATEVHT3SS_MCS0...DESC_RATEVHT3SS_MCS9: + rate_ss = 3; + rate_ss_evm = 3; + evm_id = RTW_EVM_3SS_A; + break; default: rtw_warn(rtwdev, "unknown pkt rate = %d\n", pkt_stat->rate); return; From a8e6da75e7fb2de059dd774ceec1954340d29afa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 216/465] wifi: rtw88: Extend rtw_phy_config_swing_table() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8b42c46cf6656ef3c2f6d1ec0f113753c6875712 Author: Bitterblue Smith Date: Tue Feb 18 01:31:51 2025 +0200 wifi: rtw88: Extend rtw_phy_config_swing_table() for RTL8814AU Select the TX power tracking tables for RF paths C and D as well. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/e1e532c9-8733-4ec8-84fe-ced4af6c08da@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/phy.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index b487457d2215..55be0d8e0c28 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -2458,32 +2458,56 @@ void rtw_phy_config_swing_table(struct rtw_dev *rtwdev, swing_table->n[RF_PATH_A] = tbl->pwrtrk_2g_ccka_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2g_cckb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2g_cckb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2g_cckc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2g_cckc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2g_cckd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2g_cckd_n; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n; } } else if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_1]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_1]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_1]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_1]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_1]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_1]; } else if (IS_CH_5G_BAND_3(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_2]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_2]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_2]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_2]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_2]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_2]; } else if (IS_CH_5G_BAND_4(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_3]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_3]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_3]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_3]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_3]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_3]; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n; } } EXPORT_SYMBOL(rtw_phy_config_swing_table); From d3af8bfcbd2afb4aa213d708b834c2fdb0ec6a18 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 217/465] wifi: rtw88: Extend rtw_debugfs_get_phy_info() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cfebabdd351e9cbafdc99cb198db482208ec5ad9 Author: Bitterblue Smith Date: Tue Feb 18 01:32:15 2025 +0200 wifi: rtw88: Extend rtw_debugfs_get_phy_info() for RTL8814AU Print information about the 3rd and 4th RF paths and about the 3rd spatial stream. Also, fix a small bug: don't show the average SNR and EVM for the OFDM and HT/VHT rates when the rate is actually CCK 11M. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/7c8e94e2-e034-40f3-bdaf-b000018b5573@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/debug.c | 37 +++++++++++++++------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 364ec0436d0f..1adb03d1210a 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -654,10 +654,10 @@ static void rtw_print_rate(struct seq_file *m, u8 rate) case DESC_RATE6M...DESC_RATE54M: rtw_print_ofdm_rate_txt(m, rate); break; - case DESC_RATEMCS0...DESC_RATEMCS15: + case DESC_RATEMCS0...DESC_RATEMCS31: rtw_print_ht_rate_txt(m, rate); break; - case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT2SS_MCS9: + case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT4SS_MCS9: rtw_print_vht_rate_txt(m, rate); break; default: @@ -849,20 +849,28 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) last_cnt->num_qry_pkt[rate_id + 9]); } - seq_printf(m, "[RSSI(dBm)] = {%d, %d}\n", + seq_printf(m, "[RSSI(dBm)] = {%d, %d, %d, %d}\n", dm_info->rssi[RF_PATH_A] - 100, - dm_info->rssi[RF_PATH_B] - 100); - seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d}\n", + dm_info->rssi[RF_PATH_B] - 100, + dm_info->rssi[RF_PATH_C] - 100, + dm_info->rssi[RF_PATH_D] - 100); + seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d, -%d, -%d}\n", dm_info->rx_evm_dbm[RF_PATH_A], - dm_info->rx_evm_dbm[RF_PATH_B]); - seq_printf(m, "[Rx SNR] = {%d, %d}\n", + dm_info->rx_evm_dbm[RF_PATH_B], + dm_info->rx_evm_dbm[RF_PATH_C], + dm_info->rx_evm_dbm[RF_PATH_D]); + seq_printf(m, "[Rx SNR] = {%d, %d, %d, %d}\n", dm_info->rx_snr[RF_PATH_A], - dm_info->rx_snr[RF_PATH_B]); - seq_printf(m, "[CFO_tail(KHz)] = {%d, %d}\n", + dm_info->rx_snr[RF_PATH_B], + dm_info->rx_snr[RF_PATH_C], + dm_info->rx_snr[RF_PATH_D]); + seq_printf(m, "[CFO_tail(KHz)] = {%d, %d, %d, %d}\n", dm_info->cfo_tail[RF_PATH_A], - dm_info->cfo_tail[RF_PATH_B]); + dm_info->cfo_tail[RF_PATH_B], + dm_info->cfo_tail[RF_PATH_C], + dm_info->cfo_tail[RF_PATH_D]); - if (dm_info->curr_rx_rate >= DESC_RATE11M) { + if (dm_info->curr_rx_rate >= DESC_RATE6M) { seq_puts(m, "[Rx Average Status]:\n"); seq_printf(m, " * OFDM, EVM: {-%d}, SNR: {%d}\n", (u8)ewma_evm_read(&ewma_evm[RTW_EVM_OFDM]), @@ -875,6 +883,13 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) (u8)ewma_evm_read(&ewma_evm[RTW_EVM_2SS_B]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_A]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_B])); + seq_printf(m, " * 3SS, EVM: {-%d, -%d, -%d}, SNR: {%d, %d, %d}\n", + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_A]), + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_B]), + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_C]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_A]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_B]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_C])); } seq_puts(m, "[Rx Counter]:\n"); From b64d82ddfdb3818ded8a7bf754ad2905c4c537a7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 218/465] wifi: rtw88: Extend rtw_debugfs_get_tx_pwr_tbl() for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c374281f828545b3698cf936b584249c2f9e40c5 Author: Bitterblue Smith Date: Tue Feb 18 01:32:49 2025 +0200 wifi: rtw88: Extend rtw_debugfs_get_tx_pwr_tbl() for RTL8814AU Make it print the TX power details for all RF paths, not just A and B, and for all the rates supported by the chip, not just 1SS and 2SS rates. Also skip the RF paths and rates not supported by the chip. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/ea65a978-a735-4c97-af82-d7fe26f95da1@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/debug.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 1adb03d1210a..b67d69b01f87 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -692,9 +692,11 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; - struct rtw_hal *hal = &rtwdev->hal; - u8 path, rate, bw, ch, regd; struct rtw_power_params pwr_param = {0}; + struct rtw_hal *hal = &rtwdev->hal; + u8 nss = rtwdev->efuse.hw_cap.nss; + u8 path, rate, bw, ch, regd; + u8 max_ht_rate, max_rate; mutex_lock(&rtwdev->mutex); bw = hal->current_band_width; @@ -707,19 +709,23 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v) seq_printf(m, "%-4s %-10s %-9s %-9s (%-4s %-4s %-4s) %-4s\n", "path", "rate", "pwr", "base", "byr", "lmt", "sar", "rem"); + max_ht_rate = DESC_RATEMCS0 + nss * 8 - 1; + + if (rtwdev->chip->vht_supported) + max_rate = DESC_RATEVHT1SS_MCS0 + nss * 10 - 1; + else + max_rate = max_ht_rate; + mutex_lock(&hal->tx_power_mutex); - for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + for (path = RF_PATH_A; path < hal->rf_path_num; path++) { /* there is no CCK rates used in 5G */ if (hal->current_band_type == RTW_BAND_5G) rate = DESC_RATE6M; else rate = DESC_RATE1M; - /* now, not support vht 3ss and vht 4ss*/ - for (; rate <= DESC_RATEVHT2SS_MCS9; rate++) { - /* now, not support ht 3ss and ht 4ss*/ - if (rate > DESC_RATEMCS15 && - rate < DESC_RATEVHT1SS_MCS0) + for (; rate <= max_rate; rate++) { + if (rate > max_ht_rate && rate <= DESC_RATEMCS31) continue; rtw_get_tx_power_params(rtwdev, path, rate, bw, From 3594550e69820e816fd1deecbbb3664f713580b2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:45 +0200 Subject: [PATCH 219/465] wifi: ath12k: use link specific bss_conf as well in ath12k_mac_vif_cache_flush() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 11d963d44c77261d6a948f3745bbd678eef4b83b Author: Baochen Qiang Date: Mon Dec 9 10:41:46 2024 +0800 wifi: ath12k: use link specific bss_conf as well in ath12k_mac_vif_cache_flush() Commit 3952657848c0 ("wifi: ath12k: Use mac80211 vif's link_conf instead of bss_conf") aims at, where applicable, replacing all usage of vif's bss_conf with link specific bss_conff, but missed one instance in ath12k_mac_vif_cache_flush(). This results in wrong configurations passed to ath12k_mac_bss_info_changed() when the link in question is not the default link. Change to use the link specific bss_conf to fix this issue. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Fixes: 3952657848c0 ("wifi: ath12k: Use mac80211 vif's link_conf instead of bss_conf") Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20241209024146.3282-1-quic_bqiang@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 27d2fad1b915..3e3afdc56fc9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8346,6 +8346,7 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id]; struct ath12k_base *ab = ar->ab; + struct ieee80211_bss_conf *link_conf; int ret; @@ -8364,7 +8365,13 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif } if (cache->bss_conf_changed) { - ath12k_mac_bss_info_changed(ar, arvif, &vif->bss_conf, + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in cache flush for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + ath12k_mac_bss_info_changed(ar, arvif, link_conf, cache->bss_conf_changed); } From 4a43852d233a0a30df202fed99acf48893af02dd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 220/465] wifi: ath12k: Add NULL check to validate tpc_stats JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e180a01bf2c4a67db13d70d2d91410a8c6f74be3 Author: Roopni Devanathan Date: Fri Feb 21 09:42:50 2025 +0530 wifi: ath12k: Add NULL check to validate tpc_stats While processing TPC stats received from firmware, there are chances that the tpc_stats might not be filled and the data is not available. This can happen under two scenarios. First, when firmware sends a non-zero event count before event count 0. When this happens, tpc_stats will be checked for data before memory allocation and the tpc_stats will be unavailable. Second, when memory allocation failed when event count received is 0 and the firmware still sends a non-zero event. When this happens, memory will not be allocated for tpc_stats though event count is 0, so when non-zero event count is received, tpc_stats will be empty. There are checks to validate if tpc_stats variable is filled that are used in two subsequent places, but these are placed after tpc_stats is dereference without checking if it is NULL or has valid data. Fix this by removing the mentioned checks and adding a NULL check after assigning tpc_stats to check if it is valid. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Closes: https://scan7.scan.coverity.com/#/project-view/52668/11354?selectedIssue=1637145 Fixes: f0c3bb78e42f ("wifi: ath12k: Add Support to Parse TPC Event from Firmware") Signed-off-by: Roopni Devanathan Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250221041250.769491-1-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/wmi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f934d49acee6..1866293f7159 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -8471,6 +8471,10 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, } tpc_stats = ar->debug.tpc_stats; + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + goto unlock; + } if (!(event_count == 0)) { if (event_count != tpc_stats->event_count + 1) { @@ -8489,13 +8493,12 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, ath12k_wmi_tpc_stats_event_parser, tpc_stats); if (ret) { - if (tpc_stats) - ath12k_wmi_free_tpc_stats_mem(ar); + ath12k_wmi_free_tpc_stats_mem(ar); ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret); goto unlock; } - if (tpc_stats && tpc_stats->end_of_event) + if (tpc_stats->end_of_event) complete(&ar->debug.tpc_complete); unlock: From 0ad7baab07c1d5943cf37c960b1dbf60c45bb438 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 221/465] wifi: mac80211: Add counter for all monitor interfaces JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 129860044c611008be37f49d04cf41874e3659e6 Author: Alexander Wetzel Date: Thu Feb 20 10:41:39 2025 +0100 wifi: mac80211: Add counter for all monitor interfaces Count open monitor interfaces regardless of the monitor interface type. The new counter virt_monitors takes over counting interfaces depending on the virtual monitor interface while monitors is used for all active monitor interfaces. This fixes monitor packet mirroring when using MONITOR_FLAG_ACTIVE or NO_VIRTUAL_MONITOR interfaces. Fixes: 286e69677065 ("wifi: mac80211: Drop cooked monitor support") Reported-by: Karthikeyan Periyasamy Closes: https://lore.kernel.org/r/cc715114-4e3b-619a-49dc-a4878075e1dc@quicinc.com Signed-off-by: Alexander Wetzel Tested-by: Karthikeyan Periyasamy Link: https://patch.msgid.link/20250220094139.61459-1-Alexander@wetzel-home.de Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/cfg.c | 5 ++--- net/mac80211/ethtool.c | 2 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/iface.c | 22 +++++++++++++--------- net/mac80211/util.c | 3 ++- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 86fab28f503a..9902baa2c48f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4370,9 +4370,8 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy, if (chanctx_conf) { *chandef = link->conf->chanreq.oper; ret = 0; - } else if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) && - local->open_count > 0 && - local->open_count == local->monitors && + } else if (local->open_count > 0 && + local->open_count == local->virt_monitors && sdata->vif.type == NL80211_IFTYPE_MONITOR) { *chandef = local->monitor_chanreq.oper; ret = 0; diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 42f7ee142ce3..0397755a3bd1 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -158,7 +158,7 @@ do_survey: if (chanctx_conf) channel = chanctx_conf->def.chan; else if (local->open_count > 0 && - local->open_count == local->monitors && + local->open_count == local->virt_monitors && sdata->vif.type == NL80211_IFTYPE_MONITOR) channel = local->monitor_chanreq.oper.chan; else diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f23be8b5d0d8..845888ac3d2c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1384,7 +1384,7 @@ struct ieee80211_local { spinlock_t queue_stop_reason_lock; int open_count; - int monitors, tx_mntrs; + int monitors, virt_monitors, tx_mntrs; /* number of interfaces with corresponding FIF_ flags */ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll, fif_probe_req; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 77d0078616fb..b0423046028c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -582,11 +582,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: + local->monitors--; + if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) && !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) { - local->monitors--; - if (local->monitors == 0) { + local->virt_monitors--; + if (local->virt_monitors == 0) { local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; } @@ -683,7 +685,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do case NL80211_IFTYPE_AP_VLAN: break; case NL80211_IFTYPE_MONITOR: - if (local->monitors == 0) + if (local->virt_monitors == 0) ieee80211_del_virtual_monitor(local); ieee80211_recalc_idle(local); @@ -720,7 +722,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do ieee80211_configure_filter(local); ieee80211_hw_config(local, hw_reconf_flags); - if (local->monitors == local->open_count) + if (local->virt_monitors == local->open_count) ieee80211_add_virtual_monitor(local); } @@ -979,7 +981,7 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat local->hw.wiphy->frag_threshold != (u32)-1) flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; - if (local->monitors) + if (local->virt_monitors) flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; } else { flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; @@ -989,7 +991,7 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat ieee80211_iftype_supports_hdr_offload(sdata->vif.type)) { flags |= IEEE80211_OFFLOAD_DECAP_ENABLED; - if (local->monitors && + if (local->virt_monitors && !ieee80211_hw_check(&local->hw, SUPPORTS_CONC_MON_RX_DECAP)) flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; } else { @@ -1333,20 +1335,22 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) if (res) goto err_stop; } else { - if (local->monitors == 0 && local->open_count == 0) { + if (local->virt_monitors == 0 && local->open_count == 0) { res = ieee80211_add_virtual_monitor(local); if (res) goto err_stop; } - local->monitors++; + local->virt_monitors++; /* must be before the call to ieee80211_configure_filter */ - if (local->monitors == 1) { + if (local->virt_monitors == 1) { local->hw.conf.flags |= IEEE80211_CONF_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; } } + local->monitors++; + ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); ieee80211_recalc_offload(local); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fdda14c08e2b..dec6e16b8c7d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2156,7 +2156,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) wake_up: - if (local->monitors == local->open_count && local->monitors > 0) + if (local->virt_monitors > 0 && + local->virt_monitors == local->open_count) ieee80211_add_virtual_monitor(local); /* From 8b5a6f90da85ad987f273ee0601e3633efcf29a8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 222/465] wifi: cfg80211: convert timeouts to secs_to_jiffies() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7d2497ff7e5ffd5a2e4fb1a7f2547db61bcbebdd Author: Easwar Hariharan Date: Wed Feb 19 20:32:40 2025 +0000 wifi: cfg80211: convert timeouts to secs_to_jiffies() Commit b35108a51cf7 ("jiffies: Define secs_to_jiffies()") introduced secs_to_jiffies(). As the value here is a multiple of 1000, use secs_to_jiffies() instead of msecs_to_jiffies to avoid the multiplication. This is converted using scripts/coccinelle/misc/secs_to_jiffies.cocci with the following Coccinelle rules: @depends on patch@ expression E; @@ -msecs_to_jiffies(E * 1000) +secs_to_jiffies(E) -msecs_to_jiffies(E * MSEC_PER_SEC) +secs_to_jiffies(E) Signed-off-by: Easwar Hariharan Reviewed-by: Jeff Johnson Link: https://patch.msgid.link/20250219203240.141272-1-eahariha@linux.microsoft.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index cd2124329521..1de25e9763cb 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1365,7 +1365,7 @@ void cfg80211_bss_age(struct cfg80211_registered_device *rdev, unsigned long age_secs) { struct cfg80211_internal_bss *bss; - unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); + unsigned long age_jiffies = secs_to_jiffies(age_secs); spin_lock_bh(&rdev->bss_lock); list_for_each_entry(bss, &rdev->bss_list, list) From bf2c3c9be9ff137d481a7768f581825899fae670 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 223/465] wifi: mac80211: Fix possible integer promotion issue JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ebf9944bed4ef69d29371cd3b7276f858c513954 Author: Ilan Peer Date: Fri Feb 14 09:47:21 2025 +0200 wifi: mac80211: Fix possible integer promotion issue Fix a possible integer promotion issue in mac80211 in ieee80211_ml_epcs() Fixes: de86c5f60839 ("wifi: mac80211: Add support for EPCS configuration") Reported-by: Dan Carpenter Signed-off-by: Ilan Peer Link: https://patch.msgid.link/20250214074721.1613549-1-ilan.peer@intel.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 856014afdde0..9015a979649a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10705,7 +10705,7 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, elems->ml_epcs_len, scratch, scratch_len, IEEE80211_MLE_SUBELEM_FRAGMENT); - if (len < sizeof(control)) + if (len < (ssize_t)sizeof(control)) continue; pos = scratch + sizeof(control); From d1c93fb708296ca4df2e755a614eb187c70d4f43 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 224/465] wifi: mac80211: fix integer overflow in hwmp_route_info_get() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d00c0c4105e5ab8a6a13ed23d701cceb285761fa Author: Gavrilov Ilia Date: Wed Feb 12 08:21:25 2025 +0000 wifi: mac80211: fix integer overflow in hwmp_route_info_get() Since the new_metric and last_hop_metric variables can reach the MAX_METRIC(0xffffffff) value, an integer overflow may occur when multiplying them by 10/9. It can lead to incorrect behavior. Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a8d418d9ac25 ("mac80211: mesh: only switch path when new metric is at least 10% better") Cc: stable@vger.kernel.org Signed-off-by: Ilia Gavrilov Link: https://patch.msgid.link/20250212082124.4078236-1-Ilia.Gavrilov@infotecs.ru Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mesh_hwmp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 0b13a6648e08..751bc55d604a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -367,6 +367,12 @@ u32 airtime_link_metric_get(struct ieee80211_local *local, return (u32)result; } +/* Check that the first metric is at least 10% better than the second one */ +static bool is_metric_better(u32 x, u32 y) +{ + return (x < y) && (x < (y - x / 10)); +} + /** * hwmp_route_info_get - Update routing info to originator and transmitter * @@ -458,8 +464,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, (mpath->sn == orig_sn && (rcu_access_pointer(mpath->next_hop) != sta ? - mult_frac(new_metric, 10, 9) : - new_metric) >= mpath->metric)) { + !is_metric_better(new_metric, mpath->metric) : + new_metric >= mpath->metric))) { process = false; fresh_info = false; } @@ -533,8 +539,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, if ((mpath->flags & MESH_PATH_FIXED) || ((mpath->flags & MESH_PATH_ACTIVE) && ((rcu_access_pointer(mpath->next_hop) != sta ? - mult_frac(last_hop_metric, 10, 9) : - last_hop_metric) > mpath->metric))) + !is_metric_better(last_hop_metric, mpath->metric) : + last_hop_metric > mpath->metric)))) fresh_info = false; } else { mpath = mesh_path_add(sdata, ta); From 48956caf6863900ee50a573ab900ce46f5a42fed Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 225/465] wifi: mac80211: add ieee80211_iter_chan_contexts_mtx JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ebba23e0779834e68a200b7968975274cbe260ef Author: Miri Korenblit Date: Wed Feb 12 08:22:57 2025 +0200 wifi: mac80211: add ieee80211_iter_chan_contexts_mtx Add a chanctx iterator that can be called from a wiphy-locked context. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212082137.d85eef3024de.Icda0616416c5fd4b2cbf892bdab2476f26e644ec@changeid [fix kernel-doc] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/mac80211.h | 25 +++++++++++++++++++++++++ net/mac80211/chan.c | 20 +++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e1131bcdac86..fc67f8fb8655 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -6671,6 +6671,31 @@ void ieee80211_iter_chan_contexts_atomic( void *data), void *iter_data); +/** + * ieee80211_iter_chan_contexts_mtx - iterate channel contexts + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @iter: iterator function + * @iter_data: data passed to iterator function + * + * Iterate all active channel contexts. This function can only be used while + * holding the wiphy mutex. + * + * The iterator will not find a context that's being added (during + * the driver callback to add it) but will find it while it's being + * removed. + * + * Note that during hardware restart, all contexts that existed + * before the restart are considered already present so will be + * found while iterating, whether they've been re-added already + * or not. + */ +void ieee80211_iter_chan_contexts_mtx( + struct ieee80211_hw *hw, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, + void *data), + void *iter_data); + /** * ieee80211_ap_probereq_get - retrieve a Probe Request template * @hw: pointer obtained from ieee80211_alloc_hw(). diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index dc28f2b0957a..c3bfac58151f 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * mac80211 - channel management - * Copyright 2020 - 2024 Intel Corporation + * Copyright 2020 - 2025 Intel Corporation */ #include @@ -2178,3 +2178,21 @@ void ieee80211_iter_chan_contexts_atomic( rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); + +void ieee80211_iter_chan_contexts_mtx( + struct ieee80211_hw *hw, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, + void *data), + void *iter_data) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_chanctx *ctx; + + lockdep_assert_wiphy(hw->wiphy); + + list_for_each_entry(ctx, &local->chanctx_list, list) + if (ctx->driver_present) + iter(hw, &ctx->conf, iter_data); +} +EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_mtx); From 975b42c4b655a45b444c62659567161ade4791c1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 226/465] wifi: cfg80211: expose update timestamp to drivers JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ceaad3c435964f601602531d0f6f7deaff329e21 Author: Benjamin Berg Date: Wed Feb 12 08:22:58 2025 +0200 wifi: cfg80211: expose update timestamp to drivers This information is exposed to userspace but not drivers. Make this field public so that drivers are also able to access it. The information is for example useful for link selection to determine whether the BSS corresponding to an MLO link has been seen in a recent scan. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212082137.b682ee7aebc8.I0f7cca9effa2b1cee79f4f2eb8b549c99b4e0571@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 3 +++ net/wireless/core.h | 1 - net/wireless/nl80211.c | 4 ++-- net/wireless/scan.c | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c433b6ebe1fa..1f449f00f0e7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2947,6 +2947,7 @@ struct cfg80211_bss_ies { * @nontrans_list: list of non-transmitted BSS, if this is a transmitted one * (multi-BSSID support) * @signal: signal strength value (type depends on the wiphy's signal_type) + * @ts_boottime: timestamp of the last BSS update in nanoseconds since boot * @chains: bitmask for filled values in @chain_signal. * @chain_signal: per-chain signal strength of last received BSS in dBm. * @bssid_index: index in the multiple BSS set @@ -2971,6 +2972,8 @@ struct cfg80211_bss { s32 signal; + u64 ts_boottime; + u16 beacon_interval; u16 capability; diff --git a/net/wireless/core.h b/net/wireless/core.h index 826299f3d781..b094b526b05d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -180,7 +180,6 @@ struct cfg80211_internal_bss { struct list_head list; struct list_head hidden_list; struct rb_node rbn; - u64 ts_boottime; unsigned long ts; unsigned long refcount; atomic_t hold; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e66c545be6d5..fbc86cbd5945 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10531,9 +10531,9 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, intbss->parent_bssid))) goto nla_put_failure; - if (intbss->ts_boottime && + if (res->ts_boottime && nla_put_u64_64bit(msg, NL80211_BSS_LAST_SEEN_BOOTTIME, - intbss->ts_boottime, NL80211_BSS_PAD)) + res->ts_boottime, NL80211_BSS_PAD)) goto nla_put_failure; if (!nl80211_put_signal(msg, intbss->pub.chains, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1de25e9763cb..9865f305275d 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,7 +5,7 @@ * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include @@ -1934,7 +1934,7 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, known->pub.signal = new->pub.signal; known->pub.capability = new->pub.capability; known->ts = new->ts; - known->ts_boottime = new->ts_boottime; + known->pub.ts_boottime = new->pub.ts_boottime; known->parent_tsf = new->parent_tsf; known->pub.chains = new->pub.chains; memcpy(known->pub.chain_signal, new->pub.chain_signal, @@ -2291,7 +2291,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy, tmp.pub.signal = 0; tmp.pub.beacon_interval = data->beacon_interval; tmp.pub.capability = data->capability; - tmp.ts_boottime = drv_data->boottime_ns; + tmp.pub.ts_boottime = drv_data->boottime_ns; tmp.parent_tsf = drv_data->parent_tsf; ether_addr_copy(tmp.parent_bssid, drv_data->parent_bssid); tmp.pub.chains = drv_data->chains; From 88128a6afef1f0074a151ddbedb16b9d3592807d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 227/465] wifi: iwlwifi: location api cleanup JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 180d52d224ccaf475a0279563381b5e7fd8d1f05 Author: Avraham Stern Date: Wed Feb 12 07:43:22 2025 +0200 wifi: iwlwifi: location api cleanup Remove the version suffix from the latest version of the range request command structs and the range response notification structs. In addition, don't use MVM in the API file as it is not MVM specific. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.294b7109e0be.I229ceef5933e825815d84c33855cadd62687ee04@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/api/location.h | 66 +++++++++---------- .../intel/iwlwifi/mvm/ftm-initiator.c | 18 ++--- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 +- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index b8dff139aa05..e1952fc6d149 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2015-2017 Intel Deutschland GmbH * Copyright (C) 2018-2022 Intel Corporation - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #ifndef __iwl_fw_api_location_h__ #define __iwl_fw_api_location_h__ @@ -1015,7 +1015,7 @@ struct iwl_tof_range_req_ap_entry_v9 { } __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_9 */ /** - * struct iwl_tof_range_req_ap_entry_v10 - AP configuration parameters + * struct iwl_tof_range_req_ap_entry - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @band: 0 for 5.2 GHz, 1 for 2.4 GHz, 2 for 6GHz * @channel_num: AP Channel number @@ -1063,7 +1063,7 @@ struct iwl_tof_range_req_ap_entry_v9 { * @min_time_between_msr: For non trigger based NDP ranging, the minimum time * between measurements in units of milliseconds */ -struct iwl_tof_range_req_ap_entry_v10 { +struct iwl_tof_range_req_ap_entry { __le32 initiator_ap_flags; u8 band; u8 channel_num; @@ -1134,7 +1134,7 @@ enum iwl_tof_initiator_flags { IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT = BIT(20), }; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */ -#define IWL_MVM_TOF_MAX_APS 5 +#define IWL_TOF_MAX_APS 5 #define IWL_MVM_TOF_MAX_TWO_SIDED_APS 5 /** @@ -1153,7 +1153,7 @@ enum iwl_tof_initiator_flags { * when the session is done (successfully / partially). * one of iwl_tof_response_mode. * @reserved0: reserved - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), * '1' Use MAC Address randomization according to the below * @range_req_bssid: ranging request BSSID @@ -1183,7 +1183,7 @@ struct iwl_tof_range_req_cmd_v5 { u8 ftm_tx_chains; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v2 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v2 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */ @@ -1192,7 +1192,7 @@ struct iwl_tof_range_req_cmd_v5 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1216,7 +1216,7 @@ struct iwl_tof_range_req_cmd_v7 { __le32 tsf_mac_id; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v3 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v3 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_7 */ /** @@ -1224,7 +1224,7 @@ struct iwl_tof_range_req_cmd_v7 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1248,7 +1248,7 @@ struct iwl_tof_range_req_cmd_v8 { __le32 tsf_mac_id; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v4 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v4 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_8 */ /** @@ -1256,7 +1256,7 @@ struct iwl_tof_range_req_cmd_v8 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1276,7 +1276,7 @@ struct iwl_tof_range_req_cmd_v9 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v6 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v6 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_9 */ /** @@ -1284,7 +1284,7 @@ struct iwl_tof_range_req_cmd_v9 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1304,7 +1304,7 @@ struct iwl_tof_range_req_cmd_v11 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v7 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v7 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_11 */ /** @@ -1312,7 +1312,7 @@ struct iwl_tof_range_req_cmd_v11 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1332,7 +1332,7 @@ struct iwl_tof_range_req_cmd_v12 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v8 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v8 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_12 */ /** @@ -1340,7 +1340,7 @@ struct iwl_tof_range_req_cmd_v12 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1360,15 +1360,15 @@ struct iwl_tof_range_req_cmd_v13 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v9 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v9 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */ /** - * struct iwl_tof_range_req_cmd_v14 - start measurement cmd + * struct iwl_tof_range_req_cmd - start measurement cmd * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1377,9 +1377,9 @@ struct iwl_tof_range_req_cmd_v13 { * This is the session time for completing the measurement. * @tsf_mac_id: report the measurement start time for each ap in terms of the * TSF of this mac id. 0xff to disable TSF reporting. - * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry_v10. + * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry. */ -struct iwl_tof_range_req_cmd_v14 { +struct iwl_tof_range_req_cmd { __le32 initiator_flags; u8 request_id; u8 num_of_ap; @@ -1388,8 +1388,8 @@ struct iwl_tof_range_req_cmd_v14 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v10 ap[IWL_MVM_TOF_MAX_APS]; -} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */ + struct iwl_tof_range_req_ap_entry ap[IWL_TOF_MAX_APS]; +} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_15 */ /* * enum iwl_tof_range_request_status - status of the sent request @@ -1609,7 +1609,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 { } __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_5 */ /** - * struct iwl_tof_range_rsp_ap_entry_ntfy_v6 - AP parameters (response) + * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) * @bssid: BSSID of the AP * @measure_status: current APs measurement status, one of * &enum iwl_tof_entry_status. @@ -1645,7 +1645,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 { * @tx_pn: the last PN used for this responder Tx in case PMF is configured in * LE byte order. */ -struct iwl_tof_range_rsp_ap_entry_ntfy_v6 { +struct iwl_tof_range_rsp_ap_entry_ntfy { u8 bssid[ETH_ALEN]; u8 measure_status; u8 measure_bw; @@ -1695,7 +1695,7 @@ enum iwl_tof_response_status { * @request_status: status of current measurement session, one of * &enum iwl_tof_response_status. * @last_in_batch: reprot policy (when not all responses are uploaded at once) - * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_aps: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @ap: per-AP data */ struct iwl_tof_range_rsp_ntfy_v5 { @@ -1703,7 +1703,7 @@ struct iwl_tof_range_rsp_ntfy_v5 { u8 request_status; u8 last_in_batch; u8 num_of_aps; - struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_5 */ /** @@ -1719,7 +1719,7 @@ struct iwl_tof_range_rsp_ntfy_v6 { u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_6 */ /** @@ -1735,23 +1735,23 @@ struct iwl_tof_range_rsp_ntfy_v7 { u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_7 */ /** - * struct iwl_tof_range_rsp_ntfy_v8 - ranging response notification + * struct iwl_tof_range_rsp_ntfy - ranging response notification * @request_id: A Token ID of the corresponding Range request * @num_of_aps: Number of APs results * @last_report: 1 if no more FTM sessions are scheduled, 0 otherwise. * @reserved: reserved * @ap: per-AP data */ -struct iwl_tof_range_rsp_ntfy_v8 { +struct iwl_tof_range_rsp_ntfy { u8 request_id; u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_8, LOCATION_RANGE_RSP_NTFY_API_S_VER_9 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index f7034fa40c26..dfb25b964f0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include @@ -848,7 +848,7 @@ static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm, static int iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_pmsr_request_peer *peer, - struct iwl_tof_range_req_ap_entry_v10 *target) + struct iwl_tof_range_req_ap_entry *target) { u32 i2r_max_sts, flags; int ret; @@ -920,7 +920,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_pmsr_request *req) { - struct iwl_tof_range_req_cmd_v14 cmd; + struct iwl_tof_range_req_cmd cmd; struct iwl_host_cmd hcmd = { .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, @@ -934,7 +934,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm, for (i = 0; i < cmd.num_of_ap; i++) { struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; - struct iwl_tof_range_req_ap_entry_v10 *target = &cmd.ap[i]; + struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i]; err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target); if (err) @@ -1200,7 +1200,7 @@ static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index, static void iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm, - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap) + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap) { struct iwl_mvm_ftm_pasn_entry *entry; @@ -1238,7 +1238,7 @@ static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len) switch (ver) { case 9: case 8: - return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8); + return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy); case 7: return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7); case 6: @@ -1258,7 +1258,7 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data; struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data; struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data; - struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data; + struct iwl_tof_range_rsp_ntfy *fw_resp_v8 = (void *)pkt->data; int i; bool new_api = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); @@ -1294,9 +1294,9 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n", mvm->ftm_initiator.req->cookie, num_of_aps); - for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) { + for (i = 0; i < num_of_aps && i < IWL_TOF_MAX_APS; i++) { struct cfg80211_pmsr_result result = {}; - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap; + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap; int peer_idx; if (new_api) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 014777c9cc5d..20aaef913e3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -70,7 +70,7 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { }; static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = { - .max_peers = IWL_MVM_TOF_MAX_APS, + .max_peers = IWL_TOF_MAX_APS, .report_ap_tsf = 1, .randomize_mac_addr = 1, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d17592d3f6c8..b6e0b63cbd3c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1310,7 +1310,7 @@ struct iwl_mvm { struct cfg80211_pmsr_request *req; struct wireless_dev *req_wdev; struct list_head loc_list; - int responses[IWL_MVM_TOF_MAX_APS]; + int responses[IWL_TOF_MAX_APS]; struct { struct list_head resp; } smooth; From 1c57c923624c3e85a07573d2bf43269be07c2169 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:46 +0200 Subject: [PATCH 228/465] wifi: iwlwifi: use 0xff instead of 0xffffffff for invalid JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 79c06299e719f21ecb9b657cd2baa980698b1f0b Author: Emmanuel Grumbach Date: Wed Feb 12 07:43:23 2025 +0200 wifi: iwlwifi: use 0xff instead of 0xffffffff for invalid The firmware is now able to understand 0xff and that is more widely used. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.c6719e6dc0a6.Ifd149101fa886730602dbbb02f980be8e554fe84@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/context.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h index a9fa5f054ce0..464eed9b5e71 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2022 Intel Corporation + * Copyright (C) 2012-2014, 2022, 2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -14,6 +14,9 @@ * @FW_CTXT_COLOR_POS: position of the color * @FW_CTXT_COLOR_MSK: mask of the color * @FW_CTXT_INVALID: value used to indicate unused/invalid + * @FW_CTXT_ID_INVALID: value used to indicate unused/invalid. This can be + * used with newer firmware which no longer use the color. Typically, + * firmware versions supported by iwlmld can use this value. */ enum iwl_ctxt_id_and_color { FW_CTXT_ID_POS = 0, @@ -21,6 +24,7 @@ enum iwl_ctxt_id_and_color { FW_CTXT_COLOR_POS = 8, FW_CTXT_COLOR_MSK = 0xff << FW_CTXT_COLOR_POS, FW_CTXT_INVALID = 0xffffffff, + FW_CTXT_ID_INVALID = 0xff, }; #define FW_CMD_ID_AND_COLOR(_id, _color) (((_id) << FW_CTXT_ID_POS) |\ From f01e93d2f832eddb7d02cbe8aeee0acc78a2b77c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 229/465] wifi: iwlwifi: remove mvm prefix from iwl_mvm_esr_mode_notif JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e1fc9288a1faf77947990327593f7fdeef4a62bc Author: Miri Korenblit Date: Wed Feb 12 07:43:24 2025 +0200 wifi: iwlwifi: remove mvm prefix from iwl_mvm_esr_mode_notif This is not specific to mvm. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.c536eeaae129.I848307be6df21913c0ce3eb6baef715cd401db1a@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 570a3f722510..34a562d6c208 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH @@ -92,7 +92,7 @@ enum iwl_data_path_subcmd_ids { /** * @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode, - * uses &struct iwl_mvm_esr_mode_notif + * uses &struct iwl_esr_mode_notif */ ESR_MODE_NOTIF = 0xF3, 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 511e86ea11d4..b511e3aa6bb2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -697,11 +697,11 @@ enum iwl_mvm_fw_esr_recommendation { }; /* ESR_MODE_RECOMMENDATION_CODE_API_E_VER_1 */ /** - * struct iwl_mvm_esr_mode_notif - FWs recommendation/force for esr mode + * struct iwl_esr_mode_notif - FWs recommendation/force for esr mode * * @action: the action to apply on esr state. See &iwl_mvm_fw_esr_recommendation */ -struct iwl_mvm_esr_mode_notif { +struct iwl_esr_mode_notif { __le32 action; } __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index adb7505fd173..710efdb139f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -145,7 +145,7 @@ static void iwl_mvm_rx_esr_mode_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_esr_mode_notif *notif = (void *)pkt->data; + struct iwl_esr_mode_notif *notif = (void *)pkt->data; struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm); /* FW recommendations is only for entering EMLSR */ @@ -495,7 +495,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(DATA_PATH_GROUP, ESR_MODE_NOTIF, iwl_mvm_rx_esr_mode_notif, RX_HANDLER_ASYNC_LOCKED_WIPHY, - struct iwl_mvm_esr_mode_notif), + struct iwl_esr_mode_notif), RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF, iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED, From 5307a81b77e1719145141ad733c1283bfc313762 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 230/465] wifi: iwlwifi: mld: add a debug level for PTP prints JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2bfbd823abde3f462d956b03e7f0d043eaba385a Author: Miri Korenblit Date: Wed Feb 12 07:43:25 2025 +0200 wifi: iwlwifi: mld: add a debug level for PTP prints This is required for PTP related debug prints. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.866f8f66b1d7.I764abcb845d992d058c753ce8fa9d45fed2ff4ec@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-debug.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index bf52c2edaad1..5377dda9ad53 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2021, 2024 Intel Corporation + * Copyright(c) 2018 - 2021, 2024-2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project. *****************************************************************************/ @@ -156,6 +156,7 @@ do { \ #define IWL_DL_FW 0x00010000 #define IWL_DL_RF_KILL 0x00020000 #define IWL_DL_TPT 0x00040000 +#define IWL_DL_PTP 0x00080000 /* 0x00F00000 - 0x00100000 */ #define IWL_DL_RATE 0x00100000 #define IWL_DL_CALIB 0x00200000 @@ -216,5 +217,6 @@ do { \ #define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a) #define IWL_DEBUG_FW_INFO(p, f, a...) \ IWL_DEBUG(p, IWL_DL_INFO | IWL_DL_FW, f, ## a) +#define IWL_DEBUG_PTP(p, f, a...) IWL_DEBUG(p, IWL_DL_PTP, f, ## a) #endif From f3f1864ba6867cacee5d4145ecbfcf4ddb412e2e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 231/465] wifi: iwlwifi: mld: add a debug level for EHT prints JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e51f035b5a0850061483537d14988b5677d2f748 Author: Miri Korenblit Date: Wed Feb 12 07:43:26 2025 +0200 wifi: iwlwifi: mld: add a debug level for EHT prints This is required for EHT related debug prints. As there are no more available debug levels, delete IWL_DL_EXTERNAL, which is not used, and replace it with IWL_DL_EHT. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.9b1e0d87e7e4.Iea6c1329f7b6312a73896f9a9d9bce72bd6548e2@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-debug.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index 5377dda9ad53..ea32dc88584f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -166,7 +166,7 @@ do { \ #define IWL_DL_RX 0x01000000 #define IWL_DL_ISR 0x02000000 #define IWL_DL_HT 0x04000000 -#define IWL_DL_EXTERNAL 0x08000000 +#define IWL_DL_EHT 0x08000000 /* 0xF0000000 - 0x10000000 */ #define IWL_DL_11H 0x10000000 #define IWL_DL_STATS 0x20000000 @@ -176,7 +176,6 @@ do { \ #define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) #define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a) #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) -#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a) #define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) @@ -218,5 +217,5 @@ do { \ #define IWL_DEBUG_FW_INFO(p, f, a...) \ IWL_DEBUG(p, IWL_DL_INFO | IWL_DL_FW, f, ## a) #define IWL_DEBUG_PTP(p, f, a...) IWL_DEBUG(p, IWL_DL_PTP, f, ## a) - +#define IWL_DEBUG_EHT(p, f, a...) IWL_DEBUG(p, IWL_DL_EHT, f, ## a) #endif From b120463ae8c703c6ede369c3839c7f88800b4fa2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 232/465] wifi: iwlwifi: add support for external 32 KHz clock JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f8e02ca6497cfa8946b4599d75bb7cab04be2404 Author: Emmanuel Grumbach Date: Wed Feb 12 07:43:27 2025 +0200 wifi: iwlwifi: add support for external 32 KHz clock In case the BIOS allows it, instruct the firmware to use the external 32 KHz clock. The op mode specific implementation (i.e. reading the BIOS table) will come in a later patch. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.9aae3f74fee0.I25ae45ef02b9ea387b512f974c1f3e5367a537e5@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-context-info-gen3.h | 8 +++++--- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 3 +++ drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h index cd25a1b9f2ff..20563a32a21a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018, 2020-2024 Intel Corporation + * Copyright (C) 2018, 2020-2025 Intel Corporation */ #ifndef __iwl_context_info_file_gen3_h__ #define __iwl_context_info_file_gen3_h__ @@ -80,10 +80,12 @@ enum iwl_prph_scratch_flags { * enum iwl_prph_scratch_ext_flags - PRPH scratch control ext flags * @IWL_PRPH_SCRATCH_EXT_URM_FW: switch to URM mode based on fw setting * @IWL_PRPH_SCRATCH_EXT_URM_PERM: switch to permanent URM mode + * @IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID: use external 32 KHz clock */ enum iwl_prph_scratch_ext_flags { - IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4), - IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5), + IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4), + IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5), + IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID = BIT(8), }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 021691513a57..25fb4c50e38b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -888,6 +888,7 @@ struct iwl_txq { * @trans_specific: data for the specific transport this is allocated for/with * @dsbr_urm_fw_dependent: switch to URM based on fw settings * @dsbr_urm_permanent: switch to URM permanently + * @ext_32khz_clock_valid: if true, the external 32 KHz clock can be used */ struct iwl_trans { bool csme_own; @@ -916,6 +917,8 @@ struct iwl_trans { u8 dsbr_urm_fw_dependent:1, dsbr_urm_permanent:1; + bool ext_32khz_clock_valid; + u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; bool pm_support; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index 838c426db7f0..8aa7c455bdee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include "iwl-trans.h" @@ -137,6 +137,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, if (trans->dsbr_urm_permanent) control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_PERM; + if (trans->ext_32khz_clock_valid) + control_flags_ext |= IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID; + /* Allocate prph scratch */ prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch), &trans_pcie->prph_scratch_dma_addr, From 414d4d9b36ca2da3c0d808983a9ca3c385394bf5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 233/465] wifi: iwlwifi: export iwl_get_lari_config_bitmap JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f073cc3a66cb72cd811bd18033185fce2764e083 Author: Emmanuel Grumbach Date: Wed Feb 12 07:43:28 2025 +0200 wifi: iwlwifi: export iwl_get_lari_config_bitmap This will now be called from another opmode we are writing. iwl_fill_lari_config will only be used for the older ones. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.9bb7fbc592a6.I8850691eac7c8471257f3031e8c05905afc72f70@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 7 ++++++- drivers/net/wireless/intel/iwlwifi/fw/regulatory.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 11f54339acc6..6adcfa6e214a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -474,7 +474,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) } IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); -static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { int ret; u32 val; @@ -521,6 +521,7 @@ static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) return config_bitmap; } +IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap); static size_t iwl_get_lari_config_cmd_size(u8 cmd_ver) { @@ -571,6 +572,10 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, WIDE_ID(REGULATORY_AND_NVM_GROUP, LARI_CONFIG_CHANGE), 1); + if (WARN_ONCE(cmd_ver > 12, + "Don't add newer versions to this function\n")) + return -EINVAL; + memset(cmd, 0, sizeof(*cmd)); *cmd_size = iwl_get_lari_config_cmd_size(cmd_ver); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index d978a4fadfae..53693314d505 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -215,6 +215,7 @@ int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); int iwl_bios_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value); +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, struct iwl_lari_config_change_cmd *cmd, size_t *cmd_size); From 9fd5df98bff9b34a9debb1e906917a4f2ad6471c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 234/465] wifi: iwlwifi: remember if the UATS table was read successfully JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d645fbb47dcf80d86ebe70e703277634824850ca Author: Emmanuel Grumbach Date: Wed Feb 12 07:43:29 2025 +0200 wifi: iwlwifi: remember if the UATS table was read successfully This will allow to read the table once, and not any time the command is sent. The actual use of this will be in a later patch. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.61801b78a2cb.I710a766888f370a75b47116fec29d41c106b13ed@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 2 ++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 048877fa7c71..a9e6bba2419e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -104,6 +104,7 @@ struct iwl_txf_iter_data { * 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 * @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. @@ -181,6 +182,7 @@ struct iwl_fw_runtime { struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_mcc_allowed_ap_type_cmd uats_table; + bool uats_valid; u8 uefi_tables_lock_status; }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 434eed4130b9..78bd0eb7aa92 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -402,6 +402,9 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data, memcpy(fwrt->uats_table.offset_map, uats_data->offset_map, sizeof(fwrt->uats_table.offset_map)); + + fwrt->uats_valid = true; + return 0; } From 701b1e6c5a9432fefa2264c5b62211daef8b1c5e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 235/465] wifi: iwlwifi: remove mvm prefix from iwl_mvm_d3_end_notif JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 048a3d94b0a80b6a4daeffc182ebb5bba4b5a71e Author: Miri Korenblit Date: Wed Feb 12 07:43:30 2025 +0200 wifi: iwlwifi: remove mvm prefix from iwl_mvm_d3_end_notif This is not op mode specific. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.c99748f63511.I5c8dcc46e992e76c82fdf7dbee65957cbdca1b43@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index c2362bc786b2..9c271ea67155 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -1006,7 +1006,7 @@ struct iwl_wowlan_wake_pkt_notif { * struct iwl_mvm_d3_end_notif - d3 end notification * @flags: See &enum iwl_d0i3_flags */ -struct iwl_mvm_d3_end_notif { +struct iwl_d3_end_notif { __le32 flags; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 82ca7f8b1bb2..3e8b7168af01 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -3388,7 +3388,7 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, break; } case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { - struct iwl_mvm_d3_end_notif *notif = (void *)pkt->data; + struct iwl_d3_end_notif *notif = (void *)pkt->data; d3_data->d3_end_flags = __le32_to_cpu(notif->flags); d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF; From 3b91477430f38de98218a51b7cdee456e02e1e08 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 236/465] wifi: iwlwifi: add OMI bandwidth reduction APIs JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 26fef6d386a0c6c0e1c848755687aa9c753bf22c Author: Johannes Berg Date: Wed Feb 12 07:43:31 2025 +0200 wifi: iwlwifi: add OMI bandwidth reduction APIs This adds the API definitions needed for OMI bandwidth reduction. Will be used in a later patches. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.4d34e8f5a3df.Idd6185cdb8d8a133f92032db9278c1510961cbdc@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/fw/api/datapath.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 34a562d6c208..c139b965980d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -90,6 +90,12 @@ enum iwl_data_path_subcmd_ids { */ SEC_KEY_CMD = 0x18, + /** + * @OMI_SEND_STATUS_NOTIF: notification after OMI was sent + * uses &struct iwl_omi_send_status_notif + */ + OMI_SEND_STATUS_NOTIF = 0xF2, + /** * @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode, * uses &struct iwl_esr_mode_notif @@ -688,4 +694,13 @@ struct iwl_sec_key_cmd { } __packed u; /* SEC_KEY_OPERATION_API_U_VER_1 */ } __packed; /* SEC_KEY_CMD_API_S_VER_1 */ +/** + * struct iwl_omi_send_status_notif - OMI status notification + * @success: indicates that the OMI was sent successfully + * (currently always set) + */ +struct iwl_omi_send_status_notif { + __le32 success; +} __packed; /* OMI_SEND_STATUS_NTFY_API_S_VER_1 */ + #endif /* __iwl_fw_api_datapath_h__ */ From e4df441d3260df1fc74a67a0daa6b2619fc506f7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 237/465] wifi: iwlwifi: add IWL_MAX_NUM_IGTKS macro JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5098c09a174c2e769bd016f989f6010c11fdef8c Author: Miri Korenblit Date: Wed Feb 12 07:43:32 2025 +0200 wifi: iwlwifi: add IWL_MAX_NUM_IGTKS macro This macro represents the number of IGTKS the FW can support. Will be used in a later patch. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.a8e3c7461f13.If63cbc73eaf328b2c1d7c8e57627eb93c35b0c70@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/sta.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index d7f8a276b683..ecbcd5084cd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -191,6 +191,7 @@ enum iwl_sta_sleep_flag { #define STA_KEY_IDX_INVALID (0xff) #define STA_KEY_MAX_DATA_KEY_NUM (4) #define IWL_MAX_GLOBAL_KEYS (4) +#define IWL_MAX_NUM_IGTKS 2 #define STA_KEY_LEN_WEP40 (5) #define STA_KEY_LEN_WEP104 (13) From 5af5ffa06db4ff9439eacd99505c80d6c45df448 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:47 +0200 Subject: [PATCH 238/465] wifi: iwlwifi: add Debug Host Command APIs JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 95da92e7c6ffd63aa0c30d20963793dae4ae94ef Author: Miri Korenblit Date: Wed Feb 12 07:43:33 2025 +0200 wifi: iwlwifi: add Debug Host Command APIs Add the defintition of those APIs, those will be used in a later patch. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250212073923.d842253ee55d.I2e8d65f22d5acde70ed6be16f913160a93d06852@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/fw/api/commands.h | 5 + .../net/wireless/intel/iwlwifi/fw/api/dhc.h | 129 ++++++++++++++++++ .../net/wireless/intel/iwlwifi/fw/api/rs.h | 52 +++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 4b450c722a9c..d43adb6f9220 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -506,6 +506,11 @@ enum iwl_legacy_cmds { */ DTS_MEASUREMENT_NOTIFICATION = 0xdd, + /** + * @DEBUG_HOST_COMMAND: &struct iwl_dhc_cmd + */ + DEBUG_HOST_COMMAND = 0xf1, + /** * @LDBG_CONFIG_CMD: configure continuous trace recording */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h new file mode 100644 index 000000000000..dbe06f3fc662 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_fw_api_dhc_h__ +#define __iwl_fw_api_dhc_h__ + +#define DHC_TABLE_MASK_POS (28) + +/** + * enum iwl_dhc_table_id - DHC table operations index + */ +enum iwl_dhc_table_id { + /** + * @DHC_TABLE_INTEGRATION: select the integration table + */ + DHC_TABLE_INTEGRATION = 2 << DHC_TABLE_MASK_POS, +}; + +/** + * enum iwl_dhc_umac_integration_table - integration operations + */ +enum iwl_dhc_umac_integration_table { + /** + * @DHC_INT_UMAC_TWT_OPERATION: trigger a TWT operation + */ + DHC_INT_UMAC_TWT_OPERATION = 4, + /** + * @DHC_INTEGRATION_TLC_DEBUG_CONFIG: TLC debug + */ + DHC_INTEGRATION_TLC_DEBUG_CONFIG = 1, + /** + * @DHC_INTEGRATION_MAX: Maximum UMAC integration table entries + */ + DHC_INTEGRATION_MAX +}; + +#define DHC_TARGET_UMAC BIT(27) + +/** + * struct iwl_dhc_cmd - debug host command + * @length: length in DWs of the data structure that is concatenated to the end + * of this struct + * @index_and_mask: bit 31 is 1 for data set operation else it's 0 + * bits 28-30 is the index of the table of the operation - + * &enum iwl_dhc_table_id * + * bit 27 is 0 if the cmd targeted to LMAC and 1 if targeted to UMAC, + * (LMAC is 0 for backward compatibility) + * bit 26 is 0 if the cmd targeted to LMAC0 and 1 if targeted to LMAC1, + * relevant only if bit 27 set to 0 + * bits 0-25 is a specific entry index in the table specified in bits 28-30 + * + * @data: the concatenated data. + */ +struct iwl_dhc_cmd { + __le32 length; + __le32 index_and_mask; + __le32 data[]; +} __packed; /* DHC_CMD_API_S */ + +/** + * enum iwl_dhc_twt_operation_type - describes the TWT operation type + * + * @DHC_TWT_REQUEST: Send a Request TWT command + * @DHC_TWT_SUGGEST: Send a Suggest TWT command + * @DHC_TWT_DEMAND: Send a Demand TWT command + * @DHC_TWT_GROUPING: Send a Grouping TWT command + * @DHC_TWT_ACCEPT: Send a Accept TWT command + * @DHC_TWT_ALTERNATE: Send a Alternate TWT command + * @DHC_TWT_DICTATE: Send a Dictate TWT command + * @DHC_TWT_REJECT: Send a Reject TWT command + * @DHC_TWT_TEARDOWN: Send a TearDown TWT command + */ +enum iwl_dhc_twt_operation_type { + DHC_TWT_REQUEST, + DHC_TWT_SUGGEST, + DHC_TWT_DEMAND, + DHC_TWT_GROUPING, + DHC_TWT_ACCEPT, + DHC_TWT_ALTERNATE, + DHC_TWT_DICTATE, + DHC_TWT_REJECT, + DHC_TWT_TEARDOWN, +}; /* DHC_TWT_OPERATION_TYPE_E */ + +/** + * struct iwl_dhc_twt_operation - trigger a TWT operation + * + * @mac_id: the mac Id on which to trigger TWT operation + * @twt_operation: see &enum iwl_dhc_twt_operation_type + * @target_wake_time: when should we be on channel + * @interval_exp: the exponent for the interval + * @interval_mantissa: the mantissa for the interval + * @min_wake_duration: the minimum duration for the wake period + * @trigger: is the TWT triggered or not + * @flow_type: is the TWT announced or not + * @flow_id: the TWT flow identifier from 0 to 7 + * @protection: is the TWT protected + * @ndo_paging_indicator: is ndo_paging_indicator set + * @responder_pm_mode: is responder_pm_mode set + * @negotiation_type: if the responder wants to doze outside the TWT SP + * @twt_request: 1 for TWT request, 0 otherwise + * @implicit: is TWT implicit + * @twt_group_assignment: the TWT group assignment + * @twt_channel: the TWT channel + * @reserved: reserved + */ +struct iwl_dhc_twt_operation { + __le32 mac_id; + __le32 twt_operation; + __le64 target_wake_time; + __le32 interval_exp; + __le32 interval_mantissa; + __le32 min_wake_duration; + u8 trigger; + u8 flow_type; + u8 flow_id; + u8 protection; + u8 ndo_paging_indicator; + u8 responder_pm_mode; + u8 negotiation_type; + u8 twt_request; + u8 implicit; + u8 twt_group_assignment; + u8 twt_channel; + u8 reserved; +}; /* DHC_TWT_OPERATION_API_S */ + +#endif /* __iwl_fw_api_dhc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 1a60f0cdf972..c2f806cbab59 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -226,6 +226,58 @@ struct iwl_tlc_update_notif { __le32 amsdu_enabled; } __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */ +/** + * enum iwl_tlc_debug_types - debug options + */ +enum iwl_tlc_debug_types { + /** + * @IWL_TLC_DEBUG_FIXED_RATE: set fixed rate for rate scaling + */ + IWL_TLC_DEBUG_FIXED_RATE, + /** + * @IWL_TLC_DEBUG_AGG_DURATION_LIM: time limit for a BA + * session, in usec + */ + IWL_TLC_DEBUG_AGG_DURATION_LIM, + /** + * @IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM: set max number of frames + * in an aggregation + */ + IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM, + /** + * @IWL_TLC_DEBUG_TPC_ENABLED: enable or disable tpc + */ + IWL_TLC_DEBUG_TPC_ENABLED, + /** + * @IWL_TLC_DEBUG_TPC_STATS: get number of frames Tx'ed in each + * tpc step + */ + IWL_TLC_DEBUG_TPC_STATS, + /** + * @IWL_TLC_DEBUG_RTS_DISABLE: disable RTS (bool true/false). + */ + IWL_TLC_DEBUG_RTS_DISABLE, + /** + * @IWL_TLC_DEBUG_PARTIAL_FIXED_RATE: set partial fixed rate to fw + */ + IWL_TLC_DEBUG_PARTIAL_FIXED_RATE, +}; /* TLC_MNG_DEBUG_TYPES_API_E */ + +#define MAX_DATA_IN_DHC_TLC_CMD 10 + +/** + * struct iwl_dhc_tlc_cmd - fixed debug config + * @sta_id: bit 0 - enable/disable, bits 1 - 7 hold station id + * @reserved1: reserved + * @type: type id of %enum iwl_tlc_debug_types + * @data: data to send + */ +struct iwl_dhc_tlc_cmd { + u8 sta_id; + u8 reserved1[3]; + __le32 type; + __le32 data[MAX_DATA_IN_DHC_TLC_CMD]; +} __packed; /* TLC_MNG_DEBUG_CMD_S */ #define IWL_MAX_MCS_DISPLAY_SIZE 12 From 2e228165fe35ff8fae21c002075627545982e7eb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 239/465] wifi: ath12k: Improve BSS discovery with hidden SSID in 6 GHz band JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 27d38bdfd416f4db70e09c3bef3b030c86fd235a Author: Ramasamy Kaliappan Date: Fri Feb 7 11:30:05 2025 +0530 wifi: ath12k: Improve BSS discovery with hidden SSID in 6 GHz band Currently, sometimes, the station is unable to identify the configured AP SSID in its scan results when the AP is not broadcasting its name publicly and has a hidden SSID. Currently, channel dwell time for an ath12k station is 30 ms. Sometimes, station can send broadcast probe request to AP close to the end of dwell time. In some of these cases, before AP sends a response to the received probe request, the dwell time on the station side would come to an end. So, the station will move to scan next channel and will not be able to acknowledge the unicast probe response. Resolve this issue by increasing station's channel dwell time to 70 ms, so that the it remains on the same channel for a longer period. This would increase the station's chance of receiving probe response from the AP. The station will then send a response acknowledgment back to the AP, thus leading to successful scan and BSS discovery. With an increased dwell time, scan would take longer than it takes now. But, this fix is an improvement for hidden SSID scan issue. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Ramasamy Kaliappan Signed-off-by: Roopni Devanathan Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250207060005.153835-1-quic_rdevanat@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 1866293f7159..6d1ea5f3a791 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -2433,8 +2433,8 @@ void ath12k_wmi_start_scan_init(struct ath12k *ar, arg->dwell_time_active = 50; arg->dwell_time_active_2g = 0; arg->dwell_time_passive = 150; - arg->dwell_time_active_6g = 40; - arg->dwell_time_passive_6g = 30; + arg->dwell_time_active_6g = 70; + arg->dwell_time_passive_6g = 70; arg->min_rest_time = 50; arg->max_rest_time = 500; arg->repeat_probe_time = 0; From 223c5aaa1cac437f7133e409e027c7b7ceb81354 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 240/465] wifi: rtw89: add support for HW TKIP crypto JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9e9877bd82a8073fe73e110ce4868e51adba701f Author: Kuan-Chung Chen Date: Thu Feb 20 14:23:44 2025 +0800 wifi: rtw89: add support for HW TKIP crypto For 8852BTE, 8852CE, and 8922AE, TKIP encryption and decryption will be handled by hardware. All other chips will retain their existing software-based encryption and decryption. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250220062344.15836-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/cam.c | 6 ++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac_be.c | 4 ++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 9 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index 8fa1e6c1ce13..eca3d767ff60 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -476,6 +476,12 @@ int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev, case WLAN_CIPHER_SUITE_WEP104: hw_key_type = RTW89_SEC_KEY_TYPE_WEP104; break; + case WLAN_CIPHER_SUITE_TKIP: + if (!chip->hw_tkip_crypto) + return -EOPNOTSUPP; + hw_key_type = RTW89_SEC_KEY_TYPE_TKIP; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + break; case WLAN_CIPHER_SUITE_CCMP: hw_key_type = RTW89_SEC_KEY_TYPE_CCMP128; if (!chip->hw_mgmt_tx_encrypt) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 865cdcfa59e1..3d458dbbdd00 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4285,6 +4285,7 @@ struct rtw89_chip_info { bool ul_tb_pwr_diff; bool hw_sec_hdr; bool hw_mgmt_tx_encrypt; + bool hw_tkip_crypto; u8 rf_path_num; u8 tx_nss; u8 rx_nss; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index dce0a4b01440..99b82dc85ea3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -708,8 +708,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP | B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC | B_BE_MC_DEC | B_BE_BC_DEC | - B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC; - val32 &= ~B_BE_SEC_PRE_ENQUE_TX; + B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC | + B_BE_SEC_PRE_ENQUE_TX; rtw89_write32(rtwdev, R_BE_SEC_ENG_CTRL, val32); rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 3cd1d7a4d202..34f2278ddf20 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2502,6 +2502,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .ul_tb_pwr_diff = false, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, .rf_path_num = 1, .tx_nss = 1, .rx_nss = 1, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 514ef0c2b32b..66d85c04da26 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2220,6 +2220,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .ul_tb_pwr_diff = false, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5dbb33d5e403..7650576f0577 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -856,6 +856,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .ul_tb_pwr_diff = false, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index b4832c7897a8..c96ef0dfbae2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -789,6 +789,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .ul_tb_pwr_diff = false, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = true, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index f3965a65ae18..7ba92bf898e3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3015,6 +3015,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .ul_tb_pwr_diff = true, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, + .hw_tkip_crypto = true, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index defa6cf9f7f0..157685c1b686 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2774,6 +2774,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .ul_tb_pwr_diff = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, + .hw_tkip_crypto = true, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, From 9fc7978e88ecece80ab69b5c9614c57e42a4acfd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 241/465] wifi: rtw89: Parse channel from IE to correct invalid hardware reports during scanning JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e16acf907a3c66b9996a5df43e177a5edec8e0a5 Author: Chih-Kang Chang Date: Thu Feb 20 14:43:57 2025 +0800 wifi: rtw89: Parse channel from IE to correct invalid hardware reports during scanning For some packets, we could not get channel information from PPDU status. And this causes wrong frequencies being reported. Parse the channel information from IE if provided by AP to fix this. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250220064357.17962-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.c | 44 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 8 files changed, 51 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 8d5dc22fb508..4d286182e21d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2382,6 +2382,49 @@ static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; } +static void rtw89_core_update_rx_freq_from_ie(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + size_t hdr_len, ielen; + u8 *variable; + int chan; + + if (!rtwdev->chip->rx_freq_frome_ie) + return; + + if (!rtwdev->scanning) + return; + + if (ieee80211_is_beacon(mgmt->frame_control)) { + variable = mgmt->u.beacon.variable; + hdr_len = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + } else if (ieee80211_is_probe_resp(mgmt->frame_control)) { + variable = mgmt->u.probe_resp.variable; + hdr_len = offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + } else { + return; + } + + if (skb->len > hdr_len) + ielen = skb->len - hdr_len; + else + return; + + /* The parsing code for both 2GHz and 5GHz bands is the same in this + * function. + */ + chan = cfg80211_get_ies_channel_number(variable, ielen, NL80211_BAND_2GHZ); + if (chan == -1) + return; + + rx_status->band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; + rx_status->freq = ieee80211_channel_to_frequency(chan, rx_status->band); +} + static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct rtw89_rx_desc_info *desc_info, @@ -2399,6 +2442,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); rtw89_core_validate_rx_signal(rx_status); + rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); /* In low power mode, it does RX in thread context. */ local_bh_disable(); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 3d458dbbdd00..3f62df657e1f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4283,6 +4283,7 @@ struct rtw89_chip_info { bool support_ant_gain; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; + bool rx_freq_frome_ie; bool hw_sec_hdr; bool hw_mgmt_tx_encrypt; bool hw_tkip_crypto; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 34f2278ddf20..b709dd71b06a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2500,6 +2500,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_ant_gain = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 66d85c04da26..638236bffebb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2218,6 +2218,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_ant_gain = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 7650576f0577..8bec74716f84 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -854,6 +854,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_ant_gain = true, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index c96ef0dfbae2..e62810660005 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -787,6 +787,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_ant_gain = true, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 7ba92bf898e3..5d3e105dc87f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3013,6 +3013,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_ant_gain = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, + .rx_freq_frome_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 157685c1b686..210fa818ffe8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2772,6 +2772,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_ant_gain = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, From 90c0f16ef8e31119f27970e5fb3fcc36131d1fd8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 242/465] wifi: ath11k: Clear affinity hint before calling ath11k_pcic_free_irq() in error path JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-23129 commit 68410c5bd381a81bcc92b808e7dc4e6b9ed25d11 Author: Manivannan Sadhasivam Date: Tue Feb 25 11:04:45 2025 +0530 wifi: ath11k: Clear affinity hint before calling ath11k_pcic_free_irq() in error path If a shared IRQ is used by the driver due to platform limitation, then the IRQ affinity hint is set right after the allocation of IRQ vectors in ath11k_pci_alloc_msi(). This does no harm unless one of the functions requesting the IRQ fails and attempt to free the IRQ. This results in the below warning: WARNING: CPU: 7 PID: 349 at kernel/irq/manage.c:1929 free_irq+0x278/0x29c Call trace: free_irq+0x278/0x29c ath11k_pcic_free_irq+0x70/0x10c [ath11k] ath11k_pci_probe+0x800/0x820 [ath11k_pci] local_pci_probe+0x40/0xbc The warning is due to not clearing the affinity hint before freeing the IRQs. So to fix this issue, clear the IRQ affinity hint before calling ath11k_pcic_free_irq() in the error path. The affinity will be cleared once again further down the error path due to code organization, but that does no harm. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 Cc: Baochen Qiang Fixes: 39564b475ac5 ("wifi: ath11k: fix boot failure with one MSI vector") Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20250225053447.16824-2-manivannan.sadhasivam@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 6565ac1917b7..450ac5313de1 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1125,6 +1125,8 @@ unsupported_wcn6855_soc: return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ath11k_pcic_free_irq(ab); err_ce_free: From 5a449716cd5614504e94f5fbbb91c3d219052e01 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 243/465] wifi: ath12k: Clear affinity hint before calling ath12k_pci_free_irq() in error path JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-22128 commit b43b1e2c52db77c872bd60d30cdcc72c47df70c7 Author: Manivannan Sadhasivam Date: Tue Feb 25 11:04:46 2025 +0530 wifi: ath12k: Clear affinity hint before calling ath12k_pci_free_irq() in error path If a shared IRQ is used by the driver due to platform limitation, then the IRQ affinity hint is set right after the allocation of IRQ vectors in ath12k_pci_msi_alloc(). This does no harm unless one of the functions requesting the IRQ fails and attempt to free the IRQ. This may end up with a warning from the IRQ core that is expecting the affinity hint to be cleared before freeing the IRQ: kernel/irq/manage.c: /* make sure affinity_hint is cleaned up */ if (WARN_ON_ONCE(desc->affinity_hint)) desc->affinity_hint = NULL; So to fix this issue, clear the IRQ affinity hint before calling ath12k_pci_free_irq() in the error path. The affinity will be cleared once again further down the error path due to code organization, but that does no harm. Fixes: a3012f206d07 ("wifi: ath12k: set IRQ affinity to CPU0 in case of one MSI vector") Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20250225053447.16824-3-manivannan.sadhasivam@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 7126cc361def..213a0f21d588 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1697,6 +1697,8 @@ static int ath12k_pci_probe(struct pci_dev *pdev, return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); ath12k_pci_free_irq(ab); err_ce_free: From c371a6d1d49f5f9e64539844dead25c55ff489fd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 244/465] wifi: ath11k/ath12k: Replace irq_set_affinity_hint() with irq_set_affinity_and_hint() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6f2d839d11b36c630dbcad2c68613f15409de392 Author: Manivannan Sadhasivam Date: Tue Feb 25 11:04:47 2025 +0530 wifi: ath11k/ath12k: Replace irq_set_affinity_hint() with irq_set_affinity_and_hint() irq_set_affinity_hint() API is deprecated now, so let's use the recommended equivalent irq_set_affinity_and_hint(). Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20250225053447.16824-4-manivannan.sadhasivam@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/pci.c | 2 +- drivers/net/wireless/ath/ath12k/pci.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 450ac5313de1..412f4a134e4a 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -921,7 +921,7 @@ static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab_pci->ab->dev_flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath11k_pci_probe(struct pci_dev *pdev, diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 213a0f21d588..b474696ac6d8 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -649,7 +649,7 @@ static int ath12k_pci_set_irq_affinity_hint(struct ath12k_pci *ab_pci, if (test_bit(ATH12K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath12k_pci_config_irq(struct ath12k_base *ab) From 2c16f3e1bde780c044e821e471dd269a30faf333 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 245/465] wifi: iwlwifi: Fix spelling mistake "Increate" -> "Increase" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7ed3f3c40d7b5cd8d8e4ce8e4c27d604a27c1861 Author: Colin Ian King Date: Thu Feb 27 22:19:17 2025 +0000 wifi: iwlwifi: Fix spelling mistake "Increate" -> "Increase" There is a spelling mistake in a IWL_DEBUG_RATE message. Fix it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250227221917.658401-1-colin.i.king@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index a8c4e354e2ce..068c58e9c1eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1783,7 +1783,7 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, if ((high_tpt != IWL_INVALID_VALUE) && (high_tpt > current_tpt)) { IWL_DEBUG_RATE(mvm, - "Higher rate is better. Increate rate\n"); + "Higher rate is better. Increase rate\n"); return RS_ACTION_UPSCALE; } From 79b08b0264b9f5934b19112583d4268219841516 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 246/465] wifi: cfg80211: reorg sinfo structure elements for mesh JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 23ff5f6f23f1419d87351243fd4258c2eec0fbf7 Author: Sarika Sharma Date: Thu Feb 13 22:46:21 2025 +0530 wifi: cfg80211: reorg sinfo structure elements for mesh Currently, as multi-link operation(MLO) is not supported for mesh, reorganize the sinfo structure for mesh-specific fields and embed mesh related NL attributes together in organized view. This will allow for the simplified reorganization of sinfo structure for link level in a subsequent patch to add support for MLO station statistics. No functionality changes added. Pahole summary before the reorg of sinfo structure: - size: 256, cachelines: 4, members: 50 - sum members: 239, holes: 4, sum holes: 17 - paddings: 2, sum paddings: 2 - forced alignments: 1, forced holes: 1, sum forced holes: 1 Pahole summary after the reorg of sinfo structure: - size: 248, cachelines: 4, members: 50 - sum members: 239, holes: 4, sum holes: 9 - paddings: 2, sum paddings: 2 - forced alignments: 1, last cacheline: 56 bytes Signed-off-by: Sarika Sharma Link: https://patch.msgid.link/20250213171632.1646538-2-quic_sarishar@quicinc.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 43 +++++++++++++++++++++--------------------- net/wireless/nl80211.c | 11 ++++++----- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1f449f00f0e7..3264c228a1d8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2045,9 +2045,6 @@ struct cfg80211_tid_stats { * @assoc_at: bootime (ns) of the last association * @rx_bytes: bytes (size of MPDUs) received from this station * @tx_bytes: bytes (size of MPDUs) transmitted to this station - * @llid: mesh local link id - * @plid: mesh peer link id - * @plink_state: mesh peer link state * @signal: The signal strength, type depends on the wiphy's signal_type. * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @signal_avg: Average signal strength, type depends on the wiphy's signal_type. @@ -2067,14 +2064,20 @@ struct cfg80211_tid_stats { * This number should increase every time the list of stations * changes, i.e. when a station is added or removed, so that * userspace can tell whether it got a consistent snapshot. + * @beacon_loss_count: Number of times beacon loss event has triggered. * @assoc_req_ies: IEs from (Re)Association Request. * This is used only when in AP mode with drivers that do not use * user space MLME/SME implementation. The information is provided for * the cfg80211_new_sta() calls to notify user space of the IEs. * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. * @sta_flags: station flags mask & values - * @beacon_loss_count: Number of times beacon loss event has triggered. * @t_offset: Time offset of the station relative to this host. + * @llid: mesh local link id + * @plid: mesh peer link id + * @plink_state: mesh peer link state + * @connected_to_gate: true if mesh STA has a path to mesh gate + * @connected_to_as: true if mesh STA has a path to authentication server + * @airtime_link_metric: mesh airtime link metric. * @local_pm: local mesh STA power save mode * @peer_pm: peer mesh STA power save mode * @nonpeer_pm: non-peer mesh STA power save mode @@ -2083,7 +2086,6 @@ struct cfg80211_tid_stats { * @rx_beacon: number of beacons received from this peer * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received * from this peer - * @connected_to_gate: true if mesh STA has a path to mesh gate * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer * @tx_duration: aggregate PPDU duration(usecs) for all the frames to a peer * @airtime_weight: current airtime scheduling weight @@ -2097,8 +2099,6 @@ struct cfg80211_tid_stats { * @fcs_err_count: number of packets (MPDUs) received from this station with * an FCS error. This counter should be incremented only when TA of the * received packet with an FCS error matches the peer MAC address. - * @airtime_link_metric: mesh airtime link metric. - * @connected_to_as: true if mesh STA has a path to authentication server * @mlo_params_valid: Indicates @assoc_link_id and @mld_addr fields are filled * by driver. Drivers use this only in cfg80211_new_sta() calls when AP * MLD's MLME/SME is offload to driver. Drivers won't fill this @@ -2125,9 +2125,6 @@ struct station_info { u64 assoc_at; u64 rx_bytes; u64 tx_bytes; - u16 llid; - u16 plid; - u8 plink_state; s8 signal; s8 signal_avg; @@ -2147,36 +2144,38 @@ struct station_info { int generation; + u32 beacon_loss_count; + const u8 *assoc_req_ies; size_t assoc_req_ies_len; - u32 beacon_loss_count; s64 t_offset; + u16 llid; + u16 plid; + u8 plink_state; + u8 connected_to_gate; + u8 connected_to_as; + u32 airtime_link_metric; enum nl80211_mesh_power_mode local_pm; enum nl80211_mesh_power_mode peer_pm; enum nl80211_mesh_power_mode nonpeer_pm; u32 expected_throughput; + u16 airtime_weight; + + s8 ack_signal; + s8 avg_ack_signal; + struct cfg80211_tid_stats *pertid; + u64 tx_duration; u64 rx_duration; u64 rx_beacon; u8 rx_beacon_signal_avg; - u8 connected_to_gate; - - struct cfg80211_tid_stats *pertid; - s8 ack_signal; - s8 avg_ack_signal; - - u16 airtime_weight; u32 rx_mpdu_count; u32 fcs_err_count; - u32 airtime_link_metric; - - u8 connected_to_as; - bool mlo_params_valid; u8 assoc_link_id; u8 mld_addr[ETH_ALEN] __aligned(2); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fbc86cbd5945..dd1b64d501f2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6760,9 +6760,6 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO_U64(RX_BYTES64, rx_bytes); PUT_SINFO_U64(TX_BYTES64, tx_bytes); - PUT_SINFO(LLID, llid, u16); - PUT_SINFO(PLID, plid, u16); - PUT_SINFO(PLINK_STATE, plink_state, u8); PUT_SINFO_U64(RX_DURATION, rx_duration); PUT_SINFO_U64(TX_DURATION, tx_duration); @@ -6806,13 +6803,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(TX_RETRIES, tx_retries, u32); PUT_SINFO(TX_FAILED, tx_failed, u32); PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); - PUT_SINFO(AIRTIME_LINK_METRIC, airtime_link_metric, u32); PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); + + PUT_SINFO(LLID, llid, u16); + PUT_SINFO(PLID, plid, u16); + PUT_SINFO(PLINK_STATE, plink_state, u8); + PUT_SINFO(AIRTIME_LINK_METRIC, airtime_link_metric, u32); PUT_SINFO(LOCAL_PM, local_pm, u32); PUT_SINFO(PEER_PM, peer_pm, u32); PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8); PUT_SINFO(CONNECTED_TO_AS, connected_to_as, u8); + PUT_SINFO_U64(T_OFFSET, t_offset); if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { bss_param = nla_nest_start_noflag(msg, @@ -6840,7 +6842,6 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, &sinfo->sta_flags)) goto nla_put_failure; - PUT_SINFO_U64(T_OFFSET, t_offset); PUT_SINFO_U64(RX_DROP_MISC, rx_dropped_misc); PUT_SINFO_U64(BEACON_RX, rx_beacon); PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); From 553809cc60aa017adc15c090a48facb9272d911f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:48 +0200 Subject: [PATCH 247/465] wifi: mac80211: refactor populating mesh related fields in sinfo JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e5328c14590d034dc586e56aaab88e966d06efa7 Author: Sarika Sharma Date: Thu Feb 13 22:46:22 2025 +0530 wifi: mac80211: refactor populating mesh related fields in sinfo Introduce the sta_set_mesh_sinfo() to populate mesh related fields in sinfo structure for station statistics. This will allow for the simplified population of other fields in the sinfo structure for link level in a subsequent patch to add support for MLO station statistics. No functionality changes added. Signed-off-by: Sarika Sharma Link: https://patch.msgid.link/20250213171632.1646538-3-quic_sarishar@quicinc.com [reword since it's just an internal thing] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/sta_info.c | 64 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index caa3d0236b5e..30cdc783999d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2598,6 +2598,39 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) return value; } +#ifdef CONFIG_MAC80211_MESH +static void sta_set_mesh_sinfo(struct sta_info *sta, + struct station_info *sinfo) +{ + struct ieee80211_local *local = sta->sdata->local; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_LLID) | + BIT_ULL(NL80211_STA_INFO_PLID) | + BIT_ULL(NL80211_STA_INFO_PLINK_STATE) | + BIT_ULL(NL80211_STA_INFO_LOCAL_PM) | + BIT_ULL(NL80211_STA_INFO_PEER_PM) | + BIT_ULL(NL80211_STA_INFO_NONPEER_PM) | + BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_GATE) | + BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_AS); + + sinfo->llid = sta->mesh->llid; + sinfo->plid = sta->mesh->plid; + sinfo->plink_state = sta->mesh->plink_state; + if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_T_OFFSET); + sinfo->t_offset = sta->mesh->t_offset; + } + sinfo->local_pm = sta->mesh->local_pm; + sinfo->peer_pm = sta->mesh->peer_pm; + sinfo->nonpeer_pm = sta->mesh->nonpeer_pm; + sinfo->connected_to_gate = sta->mesh->connected_to_gate; + sinfo->connected_to_as = sta->mesh->connected_to_as; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_LINK_METRIC); + sinfo->airtime_link_metric = airtime_link_metric_get(local, sta); +} +#endif + void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, bool tidstats) { @@ -2782,31 +2815,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sta_set_tidstats(sta, &sinfo->pertid[i], i); } - if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_LLID) | - BIT_ULL(NL80211_STA_INFO_PLID) | - BIT_ULL(NL80211_STA_INFO_PLINK_STATE) | - BIT_ULL(NL80211_STA_INFO_LOCAL_PM) | - BIT_ULL(NL80211_STA_INFO_PEER_PM) | - BIT_ULL(NL80211_STA_INFO_NONPEER_PM) | - BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_GATE) | - BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_AS); - - sinfo->llid = sta->mesh->llid; - sinfo->plid = sta->mesh->plid; - sinfo->plink_state = sta->mesh->plink_state; - if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_T_OFFSET); - sinfo->t_offset = sta->mesh->t_offset; - } - sinfo->local_pm = sta->mesh->local_pm; - sinfo->peer_pm = sta->mesh->peer_pm; - sinfo->nonpeer_pm = sta->mesh->nonpeer_pm; - sinfo->connected_to_gate = sta->mesh->connected_to_gate; - sinfo->connected_to_as = sta->mesh->connected_to_as; + if (ieee80211_vif_is_mesh(&sdata->vif)) + sta_set_mesh_sinfo(sta, sinfo); #endif - } sinfo->bss_param.flags = 0; if (sdata->vif.bss_conf.use_cts_prot) @@ -2862,12 +2874,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } - - if (ieee80211_vif_is_mesh(&sdata->vif)) { - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_LINK_METRIC); - sinfo->airtime_link_metric = - airtime_link_metric_get(local, sta); - } } u32 sta_get_expected_throughput(struct sta_info *sta) From d5ffc4d89a40a83951c59c1d0808d2ad8e157559 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 248/465] wifi: ath11k: refactor transmitted arvif retrieval JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ab8a17bc63e2d187ea4602e1d1b9baf10c7b252d Author: Aloka Dixit Date: Mon Feb 10 10:27:14 2025 -0800 wifi: ath11k: refactor transmitted arvif retrieval Create a new function ath11k_mac_get_tx_arvif() to retrieve 'arvif' for the transmitted interface of the MBSSID set. This will help modifying the same code path to reflect mac80211 data structure changes to support MLO. This also fixes an issue in ath11k_mac_update_vif_chan() where tx_arvif is not reset to NULL inside for loop during each iteration. Compile tested only. Signed-off-by: Aloka Dixit Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250210182718.408891-2-aloka.dixit@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/mac.c | 53 +++++++++++++-------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 14ed53836589..466a314b938b 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1529,6 +1529,14 @@ static int ath11k_mac_set_vif_params(struct ath11k_vif *arvif, return ret; } +static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif) +{ + if (arvif->vif->mbssid_tx_vif) + return ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); + + return NULL; +} + static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) { struct ath11k_vif *tx_arvif; @@ -1538,7 +1546,7 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) u32 params = 0; u8 i = 0; - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); + tx_arvif = ath11k_mac_get_tx_arvif(arvif); beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw, tx_arvif->vif, 0); @@ -1589,21 +1597,21 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) { struct ath11k *ar = arvif->ar; struct ath11k_base *ab = ar->ab; - struct ath11k_vif *tx_arvif = arvif; + struct ath11k_vif *tx_arvif; struct ieee80211_hw *hw = ar->hw; struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn; int ret; - if (vif->mbssid_tx_vif) { - tx_arvif = ath11k_vif_to_arvif(vif->mbssid_tx_vif); - if (tx_arvif != arvif) { - ar = tx_arvif->ar; - ab = ar->ab; - hw = ar->hw; - vif = tx_arvif->vif; - } + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (tx_arvif && tx_arvif != arvif) { + ar = tx_arvif->ar; + ab = ar->ab; + hw = ar->hw; + vif = tx_arvif->vif; + } else { + tx_arvif = arvif; } bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); @@ -1674,7 +1682,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, struct ieee80211_bss_conf *info) { struct ath11k *ar = arvif->ar; - struct ath11k_vif *tx_arvif = NULL; + struct ath11k_vif *tx_arvif; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -1701,9 +1709,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, ether_addr_copy(arvif->bssid, info->bssid); - if (arvif->vif->mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, @@ -6385,23 +6391,20 @@ static int ath11k_mac_setup_vdev_params_mbssid(struct ath11k_vif *arvif, { struct ath11k *ar = arvif->ar; struct ath11k_vif *tx_arvif; - struct ieee80211_vif *tx_vif; *tx_vdev_id = 0; - tx_vif = arvif->vif->mbssid_tx_vif; - if (!tx_vif) { + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (!tx_arvif) { *flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP; return 0; } - tx_arvif = ath11k_vif_to_arvif(tx_vif); - if (arvif->vif->bss_conf.nontransmitted) { - if (ar->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy) + if (ar->hw->wiphy != tx_arvif->ar->hw->wiphy) return -EINVAL; *flags = WMI_HOST_VDEV_FLAGS_NON_TRANSMIT_AP; - *tx_vdev_id = ath11k_vif_to_arvif(tx_vif)->vdev_id; + *tx_vdev_id = tx_arvif->vdev_id; } else if (tx_arvif == arvif) { *flags = WMI_HOST_VDEV_FLAGS_TRANSMIT_AP; } else { @@ -7361,8 +7364,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, int n_vifs) { struct ath11k_base *ab = ar->ab; - struct ath11k_vif *arvif, *tx_arvif = NULL; - struct ieee80211_vif *mbssid_tx_vif; + struct ath11k_vif *arvif, *tx_arvif; int ret; int i; bool monitor_vif = false; @@ -7416,10 +7418,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n", ret); - mbssid_tx_vif = arvif->vif->mbssid_tx_vif; - if (mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, From e4e576bf4c06716c4aba270e2df5cef8726c4d9d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 249/465] wifi: ath11k: pass tx arvif for MBSSID and EMA beacon generation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8e30bfad4dc96c27f52b84b3fc5928252dafee5d Author: Aloka Dixit Date: Mon Feb 10 10:27:15 2025 -0800 wifi: ath11k: pass tx arvif for MBSSID and EMA beacon generation Function ath11k_mac_setup_bcn_tmpl() retrieves tx_arvif only for a sanity check and then calls ath11k_mac_setup_bcn_tmpl_mbssid() or ath11k_mac_setup_bcn_tmpl_ema() both of which again retrieve the same pointer. Instead store the pointer and pass it to the latter two functions. Compile tested only. Signed-off-by: Aloka Dixit Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250210182718.408891-3-aloka.dixit@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/mac.c | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 466a314b938b..97816916abac 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1537,17 +1537,15 @@ static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif) return NULL; } -static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) +static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { - struct ath11k_vif *tx_arvif; struct ieee80211_ema_beacons *beacons; int ret = 0; bool nontx_vif_params_set = false; u32 params = 0; u8 i = 0; - tx_arvif = ath11k_mac_get_tx_arvif(arvif); - beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw, tx_arvif->vif, 0); if (!beacons || !beacons->cnt) { @@ -1593,25 +1591,22 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) return ret; } -static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) +static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { struct ath11k *ar = arvif->ar; struct ath11k_base *ab = ar->ab; - struct ath11k_vif *tx_arvif; struct ieee80211_hw *hw = ar->hw; struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn; int ret; - tx_arvif = ath11k_mac_get_tx_arvif(arvif); - if (tx_arvif && tx_arvif != arvif) { + if (tx_arvif != arvif) { ar = tx_arvif->ar; ab = ar->ab; hw = ar->hw; vif = tx_arvif->vif; - } else { - tx_arvif = arvif; } bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); @@ -1640,6 +1635,7 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) { struct ieee80211_vif *vif = arvif->vif; + struct ath11k_vif *tx_arvif; if (arvif->vdev_type != WMI_VDEV_TYPE_AP) return 0; @@ -1647,14 +1643,18 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) /* Target does not expect beacon templates for the already up * non-transmitting interfaces, and results in a crash if sent. */ - if (vif->mbssid_tx_vif && - arvif != ath11k_vif_to_arvif(vif->mbssid_tx_vif) && arvif->is_up) - return 0; + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (tx_arvif) { + if (arvif != tx_arvif && arvif->is_up) + return 0; - if (vif->bss_conf.ema_ap && vif->mbssid_tx_vif) - return ath11k_mac_setup_bcn_tmpl_ema(arvif); + if (vif->bss_conf.ema_ap) + return ath11k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif); + } else { + tx_arvif = arvif; + } - return ath11k_mac_setup_bcn_tmpl_mbssid(arvif); + return ath11k_mac_setup_bcn_tmpl_mbssid(arvif, tx_arvif); } void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) From de610871c8959175cfb8ddcb5b2e64dc88d95940 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 250/465] wifi: ath12k: refactor transmitted arvif retrieval JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 20fe6390b503f0c8fa15a73720fdceb2c3745c87 Author: Aloka Dixit Date: Mon Feb 10 10:27:16 2025 -0800 wifi: ath12k: refactor transmitted arvif retrieval Create a new function ath12k_mac_get_tx_arvif() to retrieve 'arvif' for the transmitted interface of the MBSSID set. This clean up will help modifying the same code path for MLO changes. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aloka Dixit Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250210182718.408891-4-aloka.dixit@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 60 +++++++++++++++------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 3e3afdc56fc9..2de9a9b003a6 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -580,6 +580,19 @@ static int ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id, return 0; } +static struct ath12k_link_vif *ath12k_mac_get_tx_arvif(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *tx_ahvif; + + if (arvif->ahvif->vif->mbssid_tx_vif) { + tx_ahvif = ath12k_vif_to_ahvif(arvif->ahvif->vif->mbssid_tx_vif); + if (tx_ahvif) + return &tx_ahvif->deflink; + } + + return NULL; +} + struct ieee80211_bss_conf * ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif) { @@ -1638,7 +1651,6 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) struct ieee80211_ema_beacons *beacons; struct ath12k_link_vif *tx_arvif; bool nontx_profile_found = false; - struct ath12k_vif *tx_ahvif; int ret = 0; u8 i; @@ -1650,10 +1662,9 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) return -ENOLINK; } - tx_ahvif = ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif); - tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif); beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), - tx_ahvif->vif, + tx_arvif->ahvif->vif, tx_arvif->link_id); if (!beacons || !beacons->cnt) { ath12k_warn(arvif->ar->ab, @@ -1696,11 +1707,10 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ieee80211_bss_conf *link_conf; - struct ath12k_link_vif *tx_arvif = arvif; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; struct ieee80211_mutable_offsets offs = {}; - struct ath12k_vif *tx_ahvif = ahvif; bool nontx_profile_found = false; struct sk_buff *bcn; int ret; @@ -1715,17 +1725,19 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) return -ENOLINK; } - if (vif->mbssid_tx_vif) { - tx_ahvif = ath12k_vif_to_ahvif(vif->mbssid_tx_vif); - tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif); + if (tx_arvif) { if (tx_arvif != arvif && arvif->is_up) return 0; if (link_conf->ema_ap) return ath12k_mac_setup_bcn_tmpl_ema(arvif); + } else { + tx_arvif = arvif; } - bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), tx_ahvif->vif, + bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), + tx_arvif->ahvif->vif, &offs, tx_arvif->link_id); if (!bcn) { ath12k_warn(ab, "failed to get beacon template from mac80211\n"); @@ -1782,6 +1794,7 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, { struct ath12k_wmi_vdev_up_params params = {}; struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; int ret; @@ -1812,11 +1825,9 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, params.vdev_id = arvif->vdev_id; params.aid = ahvif->aid; params.bssid = arvif->bssid; - if (ahvif->vif->mbssid_tx_vif) { - struct ath12k_vif *tx_ahvif = - ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif); - struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif); + if (tx_arvif) { params.tx_bssid = tx_arvif->bssid; params.nontx_profile_idx = info->bssid_index; params.nontx_profile_cnt = 1 << info->bssid_indicator; @@ -7841,14 +7852,9 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif, u32 *flags, u32 *tx_vdev_id) { struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *tx_vif = ahvif->vif->mbssid_tx_vif; struct ieee80211_bss_conf *link_conf; struct ath12k *ar = arvif->ar; struct ath12k_link_vif *tx_arvif; - struct ath12k_vif *tx_ahvif; - - if (!tx_vif) - return 0; link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) { @@ -7857,11 +7863,13 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif, return -ENOLINK; } - tx_ahvif = ath12k_vif_to_ahvif(tx_vif); - tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif); + if (!tx_arvif) + return 0; if (link_conf->nontransmitted) { - if (ar->ah->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy) + if (ath12k_ar_to_hw(ar)->wiphy != + ath12k_ar_to_hw(tx_arvif->ar)->wiphy) return -EINVAL; *flags = WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP; @@ -9238,9 +9246,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, int n_vifs) { struct ath12k_wmi_vdev_up_params params = {}; + struct ath12k_link_vif *arvif, *tx_arvif; struct ieee80211_bss_conf *link_conf; struct ath12k_base *ab = ar->ab; - struct ath12k_link_vif *arvif; struct ieee80211_vif *vif; struct ath12k_vif *ahvif; u8 link_id; @@ -9308,11 +9316,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, params.vdev_id = arvif->vdev_id; params.aid = ahvif->aid; params.bssid = arvif->bssid; - if (vif->mbssid_tx_vif) { - struct ath12k_vif *tx_ahvif = - ath12k_vif_to_ahvif(vif->mbssid_tx_vif); - struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif); + if (tx_arvif) { params.tx_bssid = tx_arvif->bssid; params.nontx_profile_idx = link_conf->bssid_index; params.nontx_profile_cnt = 1 << link_conf->bssid_indicator; From e930abd2ce5e439463e541c7314fc08f5f097989 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 251/465] wifi: ath12k: pass tx arvif for MBSSID and EMA beacon generation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5f1e9f2cbc2b8aff6a2bef9b67c09c8ac8923285 Author: Aloka Dixit Date: Mon Feb 10 10:27:17 2025 -0800 wifi: ath12k: pass tx arvif for MBSSID and EMA beacon generation Add new input parameter to ath12k_mac_setup_bcn_tmpl_ema() for 'tx_arvif' as the caller ath12k_mac_setup_bcn_tmpl() already stores it locally. Avoid duplicate retrieval. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aloka Dixit Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250210182718.408891-5-aloka.dixit@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2de9a9b003a6..cdf40d68e43d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1643,13 +1643,13 @@ static void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif, struct sk_bu } } -static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) +static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, + struct ath12k_link_vif *tx_arvif) { struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_bss_conf *bss_conf; struct ath12k_wmi_bcn_tmpl_ema_arg ema_args; struct ieee80211_ema_beacons *beacons; - struct ath12k_link_vif *tx_arvif; bool nontx_profile_found = false; int ret = 0; u8 i; @@ -1662,7 +1662,6 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) return -ENOLINK; } - tx_arvif = ath12k_mac_get_tx_arvif(arvif); beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), tx_arvif->ahvif->vif, tx_arvif->link_id); @@ -1731,7 +1730,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) return 0; if (link_conf->ema_ap) - return ath12k_mac_setup_bcn_tmpl_ema(arvif); + return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif); } else { tx_arvif = arvif; } From f4e408fdfddd943979a9fc3ff25331f41cc40e84 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 252/465] wifi: ath12k: pass BSSID index as input for EMA JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f4f5ee5e3a21830c3c4f1925108dbc7c3c070574 Author: Aloka Dixit Date: Mon Feb 10 10:27:18 2025 -0800 wifi: ath12k: pass BSSID index as input for EMA Function ath12k_mac_setup_bcn_tmpl_ema() retrieves 'bss_conf' only to get BSSID index which is an overhead because the caller ath12k_mac_setup_bcn_tmpl() has already stored this locally. Pass the index as an input instead. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aloka Dixit Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250210182718.408891-6-aloka.dixit@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index cdf40d68e43d..dfa05f0ee6c9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1644,24 +1644,15 @@ static void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif, struct sk_bu } static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, - struct ath12k_link_vif *tx_arvif) + struct ath12k_link_vif *tx_arvif, + u8 bssid_index) { - struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_bss_conf *bss_conf; struct ath12k_wmi_bcn_tmpl_ema_arg ema_args; struct ieee80211_ema_beacons *beacons; bool nontx_profile_found = false; int ret = 0; u8 i; - bss_conf = ath12k_mac_get_link_bss_conf(arvif); - if (!bss_conf) { - ath12k_warn(arvif->ar->ab, - "failed to get link bss conf to update bcn tmpl for vif %pM link %u\n", - ahvif->vif->addr, arvif->link_id); - return -ENOLINK; - } - beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), tx_arvif->ahvif->vif, tx_arvif->link_id); @@ -1677,7 +1668,7 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, for (i = 0; i < beacons->cnt; i++) { if (tx_arvif != arvif && !nontx_profile_found) ath12k_mac_set_arvif_ies(arvif, beacons->bcn[i].skb, - bss_conf->bssid_index, + bssid_index, &nontx_profile_found); ema_args.bcn_cnt = beacons->cnt; @@ -1695,7 +1686,7 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, if (tx_arvif != arvif && !nontx_profile_found) ath12k_warn(arvif->ar->ab, "nontransmitted bssid index %u not found in beacon template\n", - bss_conf->bssid_index); + bssid_index); ieee80211_beacon_free_ema_list(beacons); return ret; @@ -1730,7 +1721,8 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) return 0; if (link_conf->ema_ap) - return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif); + return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif, + link_conf->bssid_index); } else { tx_arvif = arvif; } From 87cbd68cf64a7e68b33b5b3f6cf4a5b301567559 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 253/465] wifi: ath10k: Deprecate qcom,ath10k-calibration-variant properties JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a97ed4ecb797a69f52f66445ebca538c93462ba4 Author: Krzysztof Kozlowski Date: Tue Feb 25 10:05:35 2025 +0100 wifi: ath10k: Deprecate qcom,ath10k-calibration-variant properties Add support for calibration-like properties without 'ath10k' prefix, while still keeping everything backwards compatible. Signed-off-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-4-3b2aa3f89c53@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath10k/core.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b3294287bce1..6d336e39d673 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1163,8 +1163,11 @@ int ath10k_core_check_dt(struct ath10k *ar) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath10k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); + if (!variant) + of_property_read_string(node, "qcom,ath10k-calibration-variant", + &variant); if (!variant) return -ENODATA; @@ -2259,7 +2262,9 @@ static int ath10k_core_pre_cal_download(struct ath10k *ar) "boot did not find a pre calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); if (ret) { ath10k_dbg(ar, ATH10K_DBG_BOOT, "unable to load pre cal data from DT: %d\n", ret); @@ -2337,7 +2342,9 @@ static int ath10k_download_cal_data(struct ath10k *ar) "boot did not find a calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); if (ret == 0) { ar->cal_mode = ATH10K_CAL_MODE_DT; goto done; From b29d4df3b5ecd9c73579ee7d605a9757725ba8c3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 254/465] dt-bindings: wireless: ath10k: Strip ath10k prefix from calibration properties JIRA: https://issues.redhat.com/browse/RHEL-89168 commit fcd37e2a33167cf6b507256365e45a43009d74eb Author: Krzysztof Kozlowski Date: Tue Feb 25 10:05:32 2025 +0100 dt-bindings: wireless: ath10k: Strip ath10k prefix from calibration properties Devicetree properties describing exactly the same thing should be reusable between device bindings. All Qualcomm Atheros WiFi chips needs certain calibration data, so properties should not be prefixed with device family (ath10k). Deprecate qcom,ath10k-calibration-variant and alike, so we gradually switch to a common property. This will also allow moving these properties to common schema, if desired. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-1-3b2aa3f89c53@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../bindings/net/wireless/qcom,ath10k.yaml | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml index 070c4c9b8643..a8a992b0b54e 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml @@ -93,20 +93,41 @@ properties: ieee80211-freq-limit: true - qcom,ath10k-calibration-data: + qcom,calibration-data: $ref: /schemas/types.yaml#/definitions/uint8-array description: Calibration data + board-specific data as a byte array. The length can vary between hardware versions. - qcom,ath10k-calibration-variant: + qcom,ath10k-calibration-data: + $ref: /schemas/types.yaml#/definitions/uint8-array + deprecated: true + description: + Calibration data + board-specific data as a byte array. The length + can vary between hardware versions. + + qcom,calibration-variant: $ref: /schemas/types.yaml#/definitions/string description: Unique variant identifier of the calibration data in board-2.bin for designs with colliding bus and device specific ids + qcom,ath10k-calibration-variant: + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + description: + Unique variant identifier of the calibration data in board-2.bin + for designs with colliding bus and device specific ids + + qcom,pre-calibration-data: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: + Pre-calibration data as a byte array. The length can vary between + hardware versions. + qcom,ath10k-pre-calibration-data: $ref: /schemas/types.yaml#/definitions/uint8-array + deprecated: true description: Pre-calibration data as a byte array. The length can vary between hardware versions. From 4c6d0a6cacb0ab528d357a305ca3e58c374cf961 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 255/465] wifi: ath11k: Deprecate qcom,ath11k-calibration-variant properties JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1bd9ffec4cdb9a1f09bbba16bba538f6c58a397b Author: Krzysztof Kozlowski Date: Tue Feb 25 10:05:36 2025 +0100 wifi: ath11k: Deprecate qcom,ath11k-calibration-variant properties Add support for calibration-like properties without 'ath11k' prefix, while still keeping everything backwards compatible. Signed-off-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-5-3b2aa3f89c53@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath11k/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 65bba0ae69a9..3d39ff85ba94 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1175,8 +1175,11 @@ int ath11k_core_check_dt(struct ath11k_base *ab) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath11k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); + if (!variant) + of_property_read_string(node, "qcom,ath11k-calibration-variant", + &variant); if (!variant) return -ENODATA; From e7407d3ff46b5ef721dc399ddf38ef4e104bd6a4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:49 +0200 Subject: [PATCH 256/465] dt-bindings: net: ath11k: document the inputs of the ath11k on WCN6855 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 02d697272cc62665a66930f3a3f9fd12f820eba7 Author: Bartosz Golaszewski Date: Wed Aug 14 10:23:01 2024 +0200 dt-bindings: net: ath11k: document the inputs of the ath11k on WCN6855 Describe the inputs from the PMU of the ath11k module on WCN6855. Signed-off-by: Bartosz Golaszewski Acked-by: Conor Dooley Reviewed-by: Krzysztof Kozlowski Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240814082301.8091-1-brgl@bgdev.pl Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/qcom,ath11k-pci.yaml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml index 8675d7d0215c..a71fdf05bc1e 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml @@ -50,6 +50,9 @@ properties: vddrfa1p7-supply: description: VDD_RFA_1P7 supply regulator handle + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + vddpcie0p9-supply: description: VDD_PCIE_0P9 supply regulator handle @@ -77,6 +80,22 @@ allOf: - vddrfa1p7-supply - vddpcie0p9-supply - vddpcie1p8-supply + - if: + properties: + compatible: + contains: + const: pci17cb,1103 + then: + required: + - vddrfacmn-supply + - vddaon-supply + - vddwlcx-supply + - vddwlmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply + - vddpcie0p9-supply + - vddpcie1p8-supply additionalProperties: false @@ -99,6 +118,16 @@ examples: compatible = "pci17cb,1103"; reg = <0x10000 0x0 0x0 0x0 0x0>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; + vddaon-supply = <&vreg_pmu_aon_0p8>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + qcom,ath11k-calibration-variant = "LE_X13S"; }; }; From 525b888bde2740220f2ba34d79c85468d7247439 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 257/465] dt-bindings: wireless: ath11k: Strip ath11k prefix from calibration property JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 352e8c4379fa540747cbb6c94c4b149c7487feac Author: Krzysztof Kozlowski Date: Tue Feb 25 10:05:33 2025 +0100 dt-bindings: wireless: ath11k: Strip ath11k prefix from calibration property Devicetree properties describing exactly the same thing should be reusable between device bindings. All Qualcomm Atheros WiFi chips needs certain calibration data, so properties should not be prefixed with device family (ath11k). Deprecate qcom,ath11k-calibration-variant and alike, so we gradually switch to a common property. This will also allow moving these properties to common schema, if desired. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-2-3b2aa3f89c53@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../bindings/net/wireless/qcom,ath11k-pci.yaml | 9 ++++++++- .../devicetree/bindings/net/wireless/qcom,ath11k.yaml | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml index a71fdf05bc1e..25b0afd1b615 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml @@ -23,8 +23,15 @@ properties: reg: maxItems: 1 + qcom,calibration-variant: + $ref: /schemas/types.yaml#/definitions/string + description: | + string to uniquely identify variant of the calibration data for designs + with colliding bus and device ids + qcom,ath11k-calibration-variant: $ref: /schemas/types.yaml#/definitions/string + deprecated: true description: | string to uniquely identify variant of the calibration data for designs with colliding bus and device ids @@ -128,7 +135,7 @@ examples: vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; - qcom,ath11k-calibration-variant = "LE_X13S"; + qcom,calibration-variant = "LE_X13S"; }; }; }; diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml index ff5763dc66a8..e70b9b0a2e29 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml @@ -42,8 +42,15 @@ properties: * reg * reg-names + qcom,calibration-variant: + $ref: /schemas/types.yaml#/definitions/string + description: + string to uniquely identify variant of the calibration data in the + board-2.bin for designs with colliding bus and device specific ids + qcom,ath11k-calibration-variant: $ref: /schemas/types.yaml#/definitions/string + deprecated: true description: string to uniquely identify variant of the calibration data in the board-2.bin for designs with colliding bus and device specific ids From 99b599bc6434376e4c2bcea9e449108a62094482 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 258/465] dt-bindings: wireless: ath12k: Strip ath12k prefix from calibration property JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 64e37c19383f840da534449b88d7adea4c69f52d Author: Krzysztof Kozlowski Date: Tue Feb 25 10:05:34 2025 +0100 dt-bindings: wireless: ath12k: Strip ath12k prefix from calibration property Devicetree properties describing exactly the same thing should be reusable between device bindings. All Qualcomm Atheros WiFi chips needs certain calibration data, so properties should not be prefixed with device family (ath12k). Deprecate qcom,ath12k-calibration-variant and alike, so we gradually switch to a common property. This will also allow moving these properties to common schema, if desired. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-3-3b2aa3f89c53@linaro.org Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- .../bindings/net/wireless/qcom,ath12k-wsi.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml index cbfb559f6b69..7f83871bd107 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml @@ -53,8 +53,15 @@ properties: reg: maxItems: 1 + qcom,calibration-variant: + $ref: /schemas/types.yaml#/definitions/string + description: + String to uniquely identify variant of the calibration data for designs + with colliding bus and device ids + qcom,ath12k-calibration-variant: $ref: /schemas/types.yaml#/definitions/string + deprecated: true description: String to uniquely identify variant of the calibration data for designs with colliding bus and device ids @@ -104,7 +111,7 @@ examples: compatible = "pci17cb,1109"; reg = <0x0 0x0 0x0 0x0 0x0>; - qcom,ath12k-calibration-variant = "RDP433_1"; + qcom,calibration-variant = "RDP433_1"; ports { #address-cells = <1>; @@ -140,7 +147,7 @@ examples: compatible = "pci17cb,1109"; reg = <0x0 0x0 0x0 0x0 0x0>; - qcom,ath12k-calibration-variant = "RDP433_2"; + qcom,calibration-variant = "RDP433_2"; qcom,wsi-controller; ports { @@ -177,7 +184,7 @@ examples: compatible = "pci17cb,1109"; reg = <0x0 0x0 0x0 0x0 0x0>; - qcom,ath12k-calibration-variant = "RDP433_3"; + qcom,calibration-variant = "RDP433_3"; ports { #address-cells = <1>; From 7f05dfe8efe915e729467145e98b3c64ccbad78e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 259/465] wifi: ath12k: Report proper tx completion status to mac80211 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d2d9c9b8de725e1006d3aa3d18678a732f5d3584 Author: Vinith Kumar R Date: Fri Nov 22 23:04:32 2024 +0530 wifi: ath12k: Report proper tx completion status to mac80211 Currently Tx completion for few exception packets are received from firmware and the tx status updated to mac80211. The tx status values of HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP and HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL are considered as tx failure and reported as tx failure to mac80211. But these failure status is due to internal firmware tx drop and these packets were not tried to transmit in the air. In case of mesh this invalid tx status report might trigger mpath broken issue due to increase in mpath fail average. So do not report these tx status as tx failure instead free the skb by calling ieee80211_free_txskb(), and that will be accounted as dropped frame. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 Signed-off-by: Vinith Kumar R Signed-off-by: Tamizh Chelvam Raja Acked-by: Jeff Johnson Link: https://patch.msgid.link/20241122173432.2064858-1-quic_tamizhr@quicinc.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/dp_tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 46a55554c19c..ced232bf4aed 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -576,13 +576,13 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, switch (wbm_status) { case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); ts.ack_rssi = le32_get_bits(status_desc->info2, HTT_TX_WBM_COMP_INFO2_ACK_RSSI); ath12k_dp_tx_htt_tx_complete_buf(ab, msdu, tx_ring, &ts); break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: ath12k_dp_tx_free_txbuf(ab, msdu, mac_id, tx_ring); From 9313d317718723ff197cf82d4af432b1d66aef08 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 260/465] wifi: iwlwifi: remove mld/roc.c JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 799b7f93c01060d3d24c09d971bb6ce3c9601c71 Author: Johannes Berg Date: Tue Mar 4 13:47:59 2025 +0100 wifi: iwlwifi: remove mld/roc.c This file should never have been part of the commit, remove it. Fixes: af3be9088404 ("wifi: iwlwifi: support ROC version 6") Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/roc.c | 224 ------------------- 1 file changed, 224 deletions(-) delete mode 100644 drivers/net/wireless/intel/iwlwifi/mld/roc.c diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c deleted file mode 100644 index b87faca23ceb..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mld/roc.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* - * Copyright (C) 2024 - 2025 Intel Corporation - */ -#include -#include - -#include "mld.h" -#include "roc.h" -#include "hcmd.h" -#include "iface.h" -#include "sta.h" -#include "mlo.h" - -#include "fw/api/context.h" -#include "fw/api/time-event.h" - -#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200) - -static void -iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - int *result = data; - int ret; - - ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, - IWL_MLD_EMLSR_BLOCKED_ROC, - iwl_mld_get_primary_link(vif)); - if (ret) - *result = ret; -} - -int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_channel *channel, int duration, - enum ieee80211_roc_type type) -{ - struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_int_sta *aux_sta; - struct iwl_roc_req cmd = { - .action = cpu_to_le32(FW_CTXT_ACTION_ADD), - }; - u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, - WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); - u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); - enum iwl_roc_activity activity; - int ret = 0; - - lockdep_assert_wiphy(mld->wiphy); - - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_iter_emlsr_block_roc, - &ret); - if (ret) - return ret; - - /* TODO: task=Hotspot 2.0 */ - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { - IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", - vif->type); - - return -EOPNOTSUPP; - } - - switch (type) { - case IEEE80211_ROC_TYPE_NORMAL: - activity = ROC_ACTIVITY_P2P_DISC; - break; - case IEEE80211_ROC_TYPE_MGMT_TX: - activity = ROC_ACTIVITY_P2P_NEG; - break; - default: - WARN_ONCE(1, "Got an invalid P2P ROC type\n"); - return -EINVAL; - } - - if (WARN_ON(mld_vif->roc_activity != ROC_NUM_ACTIVITIES)) - return -EBUSY; - - /* No MLO on P2P device */ - aux_sta = &mld_vif->deflink.aux_sta; - - ret = iwl_mld_add_aux_sta(mld, aux_sta); - if (ret) - return ret; - - cmd.activity = cpu_to_le32(activity); - cmd.sta_id = cpu_to_le32(aux_sta->sta_id); - cmd.channel_info.channel = cpu_to_le32(channel->hw_value); - cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); - cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; - /* TODO: task=Hotspot 2.0, revisit those parameters when we add an ROC - * on the BSS vif - */ - cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); - cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); - - memcpy(cmd.node_addr, vif->addr, ETH_ALEN); - - ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), - &cmd, cmd_len); - if (ret) { - IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); - return ret; - } - mld_vif->roc_activity = activity; - - return 0; -} - -static void -iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - - iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC); -} - -static void iwl_mld_destroy_roc(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct iwl_mld_vif *mld_vif) -{ - mld_vif->roc_activity = ROC_NUM_ACTIVITIES; - - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_iter_emlsr_unblock_roc, - NULL); - - /* wait until every tx has seen that roc_activity has been reset */ - synchronize_net(); - /* from here, no new tx will be added - * we can flush the Tx on the queues - */ - - iwl_mld_flush_link_sta_txqs(mld, mld_vif->deflink.aux_sta.sta_id); - - iwl_mld_remove_aux_sta(mld, vif, &vif->bss_conf); -} - -int iwl_mld_cancel_roc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_roc_req cmd = { - .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), - }; - u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, - WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); - u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); - int ret; - - lockdep_assert_wiphy(mld->wiphy); - - /* TODO: task=Hotspot 2.0 */ - if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) - return -EOPNOTSUPP; - - /* No roc activity running it's probably already done */ - if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) - return 0; - - cmd.activity = cpu_to_le32(mld_vif->roc_activity); - - ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), - &cmd, cmd_len); - if (ret) - IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n"); - - /* We may have raced with the firmware expiring the ROC instance at - * this very moment. In that case, we can have a notification in the - * async processing queue. However, none can arrive _after_ this as - * ROC_CMD was sent synchronously, i.e. we waited for a response and - * the firmware cannot refer to this ROC after the response. Thus, - * if we just cancel the notification (if there's one) we'll be at a - * clean state for any possible next ROC. - */ - iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC, - mld_vif->roc_activity); - - iwl_mld_destroy_roc(mld, vif, mld_vif); - - return 0; -} - -void iwl_mld_handle_roc_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt) -{ - const struct iwl_roc_notif *notif = (void *)pkt->data; - u32 activity = le32_to_cpu(notif->activity); - /* TODO: task=Hotspot 2.0 - roc can run on BSS */ - struct ieee80211_vif *vif = mld->p2p_device_vif; - struct iwl_mld_vif *mld_vif; - - if (WARN_ON(!vif)) - return; - - mld_vif = iwl_mld_vif_from_mac80211(vif); - /* It is possible that the ROC was canceled - * but the notification was already fired. - */ - if (mld_vif->roc_activity != activity) - return; - - if (le32_to_cpu(notif->success) && - le32_to_cpu(notif->started)) { - /* We had a successful start */ - ieee80211_ready_on_channel(mld->hw); - } else { - /* ROC was not successful, tell the firmware to remove it */ - if (le32_to_cpu(notif->started)) - iwl_mld_cancel_roc(mld->hw, vif); - else - iwl_mld_destroy_roc(mld, vif, mld_vif); - /* we need to let know mac80211 about end OR - * an unsuccessful start - */ - ieee80211_remain_on_channel_expired(mld->hw); - } -} From e1ab6b57fc1cc2c3ab409644587f5965b9cab971 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 261/465] wifi: iwlwifi: add iwlmld sub-driver JIRA: https://issues.redhat.com/browse/RHEL-89168 Conflicts: - drivers/net/wireless/intel/iwlwifi/mld/mld.c drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c drivers/net/wireless/intel/iwlwifi/mld/tests/module.c Keep the behavior of MODULE_IMPORT_NS macro because cdd30ebb1b9f ("module: Convert symbol namespace to string literal") and others were not applied. commit d1e879ec600f9b3bdd253167533959facfefb17b Author: Miri Korenblit Date: Sun Feb 16 11:42:40 2025 +0200 wifi: iwlwifi: add iwlmld sub-driver iwlwifi is the driver of all Intel wifi devices since 2008. Since then, the hardware has changed a lot, but the firmware API has changed even more. The need to keep one driver that supports all those different APIs led us to introduce a new architecture circa 2012 which allowed us to keep the same interface to the hardware (DMAs, Tx queues, etc...) with a new layer to implement the mid-layer between mac80211 and the firmware. The first component is called the 'transport' and the latter is called 'operation_mode' a.k.a op_mode. In 2013 we took advantage of the new architecture to introduce iwlmvm which allowed us to implement the, then, new firmware API. This op_mode supports 7260 and up, those devices supports support at least VHT. Since then, wifi evolved and so did the firmware. It became much bigger and took a lot of functionality from the driver. It became increasingly hard to keep the same op_mode for the newest devices and we experienced frequent regressions on older devices. In order to avoid those regressions and keep the code maintainable, we decided it was about time to start a new op_mode. iwlmld is a new op_mode that supports BE200 or newer if the firmware being used is 97.ucode or newer. If the user has an older devices or BE200 with .96.ucode, iwlmvm will be loaded. Of course, this op_mode selection is seamless. All the features supported in iwlmvm are supported in iwlmld besides a few seldom used use cases: injection and Hotspot 2.0. Those are under work. A few points about the implementation: * iwlmld doesn't have any mutexes, it relies on the wiphy_lock * iwlmld is more "resource oriented": stations, links and interfaces are allocated and freed only after all the relevant flows are completed. * Firmware notifications' sizes are validated in a more structured way. We would love to see this new op_mode merged in 6.15. The firmware for this new driver (.97.ucode) is not yet publicly available but it'll be sent very soon. People eager to get an early version of this firmware can contact Emmanuel at: emmanuel.grumbach@intel.com I've listed the people who directly contributed code, but many others from various teams have contributed in other ways. Co-developed-by: Johannes Berg Signed-off-by: Johannes Berg Co-developed-by: Avraham Stern Signed-off-by: Avraham Stern Co-developed-by: Daniel Gabay Signed-off-by: Daniel Gabay Co-developed-by: Emmanuel Grumbach Signed-off-by: Emmanuel Grumbach Co-developed-by: Anjaneyulu Signed-off-by: Anjaneyulu Co-developed-by: Yedidya Benshimol Signed-off-by: Yedidya Benshimol Co-developed-by: Benjamin Berg Signed-off-by: Benjamin Berg Co-developed-by: Shaul Triebitz Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://lore.kernel.org/linux-wireless/20250216094321.537988-1-miriam.rachel.korenblit@intel.com/ [fix Kconfig, fix api/phy.h includes, SPDX tag and coding style issues, duplicated includes per 0-day robot] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/Kconfig | 15 +- drivers/net/wireless/intel/iwlwifi/Makefile | 5 +- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 5 + .../net/wireless/intel/iwlwifi/fw/api/phy.h | 4 +- .../net/wireless/intel/iwlwifi/iwl-config.h | 6 +- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 28 +- .../net/wireless/intel/iwlwifi/mld/Makefile | 16 + drivers/net/wireless/intel/iwlwifi/mld/agg.c | 670 +++++ drivers/net/wireless/intel/iwlwifi/mld/agg.h | 127 + drivers/net/wireless/intel/iwlwifi/mld/ap.c | 344 +++ drivers/net/wireless/intel/iwlwifi/mld/ap.h | 45 + drivers/net/wireless/intel/iwlwifi/mld/coex.c | 40 + drivers/net/wireless/intel/iwlwifi/mld/coex.h | 15 + .../wireless/intel/iwlwifi/mld/constants.h | 88 + drivers/net/wireless/intel/iwlwifi/mld/d3.c | 1998 ++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/d3.h | 51 + .../net/wireless/intel/iwlwifi/mld/debugfs.c | 823 +++++ .../net/wireless/intel/iwlwifi/mld/debugfs.h | 200 ++ .../intel/iwlwifi/mld/ftm-initiator.c | 451 +++ .../intel/iwlwifi/mld/ftm-initiator.h | 15 + drivers/net/wireless/intel/iwlwifi/mld/fw.c | 536 ++++ drivers/net/wireless/intel/iwlwifi/mld/hcmd.h | 54 + .../net/wireless/intel/iwlwifi/mld/iface.c | 671 +++++ .../net/wireless/intel/iwlwifi/mld/iface.h | 234 ++ drivers/net/wireless/intel/iwlwifi/mld/key.c | 358 +++ drivers/net/wireless/intel/iwlwifi/mld/key.h | 39 + drivers/net/wireless/intel/iwlwifi/mld/led.c | 100 + drivers/net/wireless/intel/iwlwifi/mld/led.h | 29 + drivers/net/wireless/intel/iwlwifi/mld/link.c | 1216 ++++++++ drivers/net/wireless/intel/iwlwifi/mld/link.h | 153 + .../wireless/intel/iwlwifi/mld/low_latency.c | 335 +++ .../wireless/intel/iwlwifi/mld/low_latency.h | 68 + .../net/wireless/intel/iwlwifi/mld/mac80211.c | 2671 +++++++++++++++++ .../net/wireless/intel/iwlwifi/mld/mac80211.h | 13 + drivers/net/wireless/intel/iwlwifi/mld/mcc.c | 329 ++ drivers/net/wireless/intel/iwlwifi/mld/mcc.h | 17 + drivers/net/wireless/intel/iwlwifi/mld/mld.c | 707 +++++ drivers/net/wireless/intel/iwlwifi/mld/mld.h | 584 ++++ drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 994 ++++++ drivers/net/wireless/intel/iwlwifi/mld/mlo.h | 157 + .../net/wireless/intel/iwlwifi/mld/notif.c | 758 +++++ .../net/wireless/intel/iwlwifi/mld/notif.h | 35 + drivers/net/wireless/intel/iwlwifi/mld/phy.c | 116 + drivers/net/wireless/intel/iwlwifi/mld/phy.h | 49 + .../net/wireless/intel/iwlwifi/mld/power.c | 395 +++ .../net/wireless/intel/iwlwifi/mld/power.h | 33 + drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 321 ++ drivers/net/wireless/intel/iwlwifi/mld/ptp.h | 45 + .../wireless/intel/iwlwifi/mld/regulatory.c | 393 +++ .../wireless/intel/iwlwifi/mld/regulatory.h | 23 + drivers/net/wireless/intel/iwlwifi/mld/roc.c | 224 ++ drivers/net/wireless/intel/iwlwifi/mld/roc.h | 20 + drivers/net/wireless/intel/iwlwifi/mld/rx.c | 2060 +++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/rx.h | 72 + drivers/net/wireless/intel/iwlwifi/mld/scan.c | 2006 +++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/scan.h | 135 + .../intel/iwlwifi/mld/session-protect.c | 222 ++ .../intel/iwlwifi/mld/session-protect.h | 102 + drivers/net/wireless/intel/iwlwifi/mld/sta.c | 1265 ++++++++ drivers/net/wireless/intel/iwlwifi/mld/sta.h | 266 ++ .../net/wireless/intel/iwlwifi/mld/stats.c | 509 ++++ .../net/wireless/intel/iwlwifi/mld/stats.h | 22 + .../wireless/intel/iwlwifi/mld/tests/Makefile | 5 + .../wireless/intel/iwlwifi/mld/tests/agg.c | 663 ++++ .../wireless/intel/iwlwifi/mld/tests/hcmd.c | 62 + .../intel/iwlwifi/mld/tests/link-selection.c | 171 ++ .../wireless/intel/iwlwifi/mld/tests/link.c | 103 + .../wireless/intel/iwlwifi/mld/tests/module.c | 11 + .../net/wireless/intel/iwlwifi/mld/tests/rx.c | 353 +++ .../wireless/intel/iwlwifi/mld/tests/utils.c | 475 +++ .../wireless/intel/iwlwifi/mld/tests/utils.h | 124 + .../net/wireless/intel/iwlwifi/mld/thermal.c | 438 +++ .../net/wireless/intel/iwlwifi/mld/thermal.h | 36 + .../wireless/intel/iwlwifi/mld/time_sync.c | 240 ++ .../wireless/intel/iwlwifi/mld/time_sync.h | 26 + drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 717 +++++ drivers/net/wireless/intel/iwlwifi/mld/tlc.h | 23 + drivers/net/wireless/intel/iwlwifi/mld/tx.c | 1374 +++++++++ drivers/net/wireless/intel/iwlwifi/mld/tx.h | 77 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 9 +- 80 files changed, 28178 insertions(+), 11 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/agg.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/agg.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ap.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ap.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/coex.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/coex.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/constants.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/d3.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/d3.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/debugfs.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/debugfs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/fw.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/hcmd.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/iface.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/iface.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/key.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/key.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/led.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/led.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/link.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/link.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/low_latency.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/low_latency.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mac80211.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mac80211.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mcc.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mcc.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mld.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mld.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mlo.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/mlo.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/notif.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/notif.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/phy.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/phy.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/power.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/power.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ptp.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/ptp.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/regulatory.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/regulatory.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/roc.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/roc.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/rx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/rx.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/scan.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/scan.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/session-protect.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/session-protect.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/sta.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/sta.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/stats.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/stats.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/link.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/module.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/thermal.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/thermal.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/time_sync.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/time_sync.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tlc.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tlc.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tx.h diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 4b04865fc2c9..82f577da1a8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -81,14 +81,25 @@ config IWLMVM of the devices that use this firmware is available here: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware +config IWLMLD + tristate "Intel Wireless WiFi MLD Firmware support" + select WANT_DEV_COREDUMP + depends on MAC80211 + depends on PTP_1588_CLOCK_OPTIONAL + help + This is the driver that supports firmwares of MLD capable devices. + The list of the devices that use this firmware is available here: + https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware + # don't call it _MODULE -- will confuse Kconfig/fixdep/... config IWLWIFI_OPMODE_MODULAR bool default y if IWLDVM=m default y if IWLMVM=m + default y if IWLMLD=m -comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" - depends on IWLDVM=n && IWLMVM=n +comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD" + depends on IWLDVM=n && IWLMVM=n && IWLMLD=n menu "Debugging Options" diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 19c4ce6f2465..9546ceeaf5e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -12,7 +12,8 @@ iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-gen3.o iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o -iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o cfg/bz.o cfg/sc.o cfg/dr.o +iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o +iwlwifi-$(CONFIG_IWLMLD) += cfg/bz.o cfg/sc.o cfg/dr.o iwlwifi-objs += iwl-dbg-tlv.o iwlwifi-objs += iwl-trans.o @@ -20,6 +21,7 @@ iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o iwlwifi-objs += fw/regulatory.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o +iwlwifi-$(CONFIG_IWLMLD) += fw/smem.o fw/init.o iwlwifi-$(CONFIG_ACPI) += fw/acpi.o iwlwifi-$(CONFIG_EFI) += fw/uefi.o iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += fw/debugfs.o @@ -33,6 +35,7 @@ ccflags-y += -I$(src) obj-$(CONFIG_IWLDVM) += dvm/ obj-$(CONFIG_IWLMVM) += mvm/ obj-$(CONFIG_IWLMEI) += mei/ +obj-$(CONFIG_IWLMLD) += mld/ obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/ diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 64a5bbb45c83..36e8e3a991dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -38,6 +38,11 @@ #define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \ IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" +#if !IS_ENABLED(CONFIG_IWLMVM) +const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; +const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz"; +#endif + static const struct iwl_base_params iwl_bz_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, .num_of_queues = 512, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h index eb8961b51cb0..f63b25b03b7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2019-2022, 2024-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_phy_h__ #define __iwl_fw_api_phy_h__ +#include +#include /** * enum iwl_phy_ops_subcmd_ids - PHY group commands diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 5fc9ce2b350e..7e4864c00780 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -653,6 +653,10 @@ extern const struct iwl_cfg iwl_cfg_ma; extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0; extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; +#endif /* CONFIG_IWLMVM */ + +#if IS_ENABLED(CONFIG_IWLMLD) +extern const struct iwl_ht_params iwl_bz_ht_params; extern const struct iwl_ht_params iwl_bz_ht_params; @@ -664,6 +668,6 @@ extern const struct iwl_cfg iwl_cfg_sc2; extern const struct iwl_cfg iwl_cfg_sc2f; extern const struct iwl_cfg iwl_cfg_dr; extern const struct iwl_cfg iwl_cfg_br; -#endif /* CONFIG_IWLMVM */ +#endif /* CONFIG_IWLMLD */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 352b6e73e08f..dcf07ac042f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -75,6 +75,9 @@ struct iwl_drv { enum { DVM_OP_MODE, MVM_OP_MODE, +#if IS_ENABLED(CONFIG_IWLMLD) + MLD_OP_MODE, +#endif }; /* Protects the table contents, i.e. the ops pointer & drv list */ @@ -86,6 +89,9 @@ static struct iwlwifi_opmode_table { } iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL }, [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, +#if IS_ENABLED(CONFIG_IWLMLD) + [MLD_OP_MODE] = { .name = "iwlmld", .ops = NULL }, +#endif }; #define IWL_DEFAULT_SCAN_CHANNELS 40 @@ -316,6 +322,7 @@ struct iwl_firmware_pieces { size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv; size_t n_mem_tlv; + u32 major; }; static void alloc_sec_data(struct iwl_firmware_pieces *pieces, @@ -957,19 +964,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; case IWL_UCODE_TLV_FW_VERSION: { const __le32 *ptr = (const void *)tlv_data; - u32 major, minor; + u32 minor; u8 local_comp; if (tlv_len != sizeof(u32) * 3) goto invalid_tlv_len; - major = le32_to_cpup(ptr++); + pieces->major = le32_to_cpup(ptr++); minor = le32_to_cpup(ptr++); local_comp = le32_to_cpup(ptr); snprintf(drv->fw.fw_version, sizeof(drv->fw.fw_version), - "%u.%08x.%u %s", major, minor, + "%u.%08x.%u %s", pieces->major, minor, local_comp, iwl_reduced_fw_name(drv)); break; } @@ -1468,6 +1475,8 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv) } } +#define IWL_MLD_SUPPORTED_FW_VERSION 97 + /* * iwl_req_fw_callback - callback when firmware was loaded * @@ -1720,6 +1729,19 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) break; } +#if IS_ENABLED(CONFIG_IWLMLD) + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) + op = &iwlwifi_opmode_table[MLD_OP_MODE]; + else +#else + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) { + IWL_ERR(drv, + "IWLMLD needs to be compiled to support this firmware\n"); + mutex_unlock(&iwlwifi_opmode_table_mtx); + goto out_unbind; + } +#endif + IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", drv->fw.fw_version, op->name); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/Makefile new file mode 100644 index 000000000000..ece66e7a9be4 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +obj-$(CONFIG_IWLMLD) += iwlmld.o +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/ + +iwlmld-y += mld.o notif.o mac80211.o fw.o power.o iface.o link.o rx.o mcc.o session-protect.o phy.o +iwlmld-y += scan.o sta.o tx.o coex.o tlc.o agg.o key.o regulatory.o ap.o thermal.o roc.o stats.o +iwlmld-y += low_latency.o mlo.o ptp.o time_sync.o ftm-initiator.o +iwlmld-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o +iwlmld-$(CONFIG_IWLWIFI_LEDS) += led.o +iwlmld-$(CONFIG_PM_SLEEP) += d3.o + +# non-upstream things +iwlmld-$(CONFIG_IWL_VENDOR_CMDS) += vendor-cmd.o +iwlmld-$(CONFIG_IWLMVM_AX_SOFTAP_TESTMODE) += ax-softap-testmode.o + +subdir-ccflags-y += -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c new file mode 100644 index 000000000000..db9e0f04f4b7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "agg.h" +#include "sta.h" +#include "hcmd.h" + +static void +iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct napi_struct *napi, + struct iwl_mld_baid_data *baid_data, + struct iwl_mld_reorder_buffer *reorder_buf, + u16 nssn) +{ + struct iwl_mld_reorder_buf_entry *entries = + &baid_data->entries[reorder_buf->queue * + baid_data->entries_per_queue]; + u16 ssn = reorder_buf->head_sn; + + while (ieee80211_sn_less(ssn, nssn)) { + int index = ssn % baid_data->buf_size; + struct sk_buff_head *skb_list = &entries[index].frames; + struct sk_buff *skb; + + ssn = ieee80211_sn_inc(ssn); + + /* Empty the list. Will have more than one frame for A-MSDU. + * Empty list is valid as well since nssn indicates frames were + * received. + */ + while ((skb = __skb_dequeue(skb_list))) { + iwl_mld_pass_packet_to_mac80211(mld, napi, skb, + reorder_buf->queue, + sta); + reorder_buf->num_stored--; + } + } + reorder_buf->head_sn = nssn; +} + +static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, + struct napi_struct *napi, + u8 baid, u16 nssn, int queue) +{ + struct iwl_mld_reorder_buffer *reorder_buf; + struct iwl_mld_baid_data *ba_data; + struct ieee80211_link_sta *link_sta; + u32 sta_id; + + IWL_DEBUG_HT(mld, "Frame release notification for BAID %u, NSSN %d\n", + baid, nssn); + + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || + baid >= ARRAY_SIZE(mld->fw_id_to_ba))) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!ba_data) { + IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); + goto out_unlock; + } + + /* pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) + goto out_unlock; + + reorder_buf = &ba_data->reorder_buf[queue]; + + iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data, + reorder_buf, nssn); +out_unlock: + rcu_read_unlock(); +} + +void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_frame_release *release = (void *)pkt->data; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release), + "Unexpected frame release notif size %u (expected %zu)\n", + pkt_len, sizeof(*release))) + return; + + iwl_mld_release_frames_from_notif(mld, napi, release->baid, + le16_to_cpu(release->nssn), + queue); +} + +void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, + int queue) +{ + struct iwl_bar_frame_release *release = (void *)pkt->data; + struct iwl_mld_baid_data *baid_data; + unsigned int baid, nssn, sta_id, tid; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release), + "Unexpected frame release notif size %u (expected %zu)\n", + pkt_len, sizeof(*release))) + return; + + baid = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_BAID_MASK); + nssn = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_NSSN_MASK); + sta_id = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_STA_MASK); + tid = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_TID_MASK); + + if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "BAR release: invalid BAID (%x)\n", baid)) + return; + + rcu_read_lock(); + baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!IWL_FW_CHECK(mld, !baid_data, + "Got valid BAID %d but not allocated, invalid BAR release!\n", + baid)) + goto out_unlock; + + if (IWL_FW_CHECK(mld, tid != baid_data->tid || + sta_id > mld->fw->ucode_capa.num_stations || + !(baid_data->sta_mask & BIT(sta_id)), + "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n", + baid, baid_data->sta_mask, baid_data->tid, sta_id, + tid)) + goto out_unlock; + + IWL_DEBUG_DROP(mld, "Received a BAR, expect packet loss: nssn %d\n", + nssn); + + iwl_mld_release_frames_from_notif(mld, napi, baid, nssn, queue); +out_unlock: + rcu_read_unlock(); +} + +void iwl_mld_del_ba(struct iwl_mld *mld, int queue, + struct iwl_mld_delba_data *data) +{ + struct iwl_mld_baid_data *ba_data; + struct iwl_mld_reorder_buffer *reorder_buf; + struct ieee80211_link_sta *link_sta; + u8 baid = data->baid; + u32 sta_id; + + if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid)) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (WARN_ON_ONCE(!ba_data)) + goto out_unlock; + + /* pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) + goto out_unlock; + + reorder_buf = &ba_data->reorder_buf[queue]; + + /* release all frames that are in the reorder buffer to the stack */ + iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL, + ba_data, reorder_buf, + ieee80211_sn_add(reorder_buf->head_sn, + ba_data->buf_size)); +out_unlock: + rcu_read_unlock(); +} + +/* Returns true if the MPDU was buffered\dropped, false if it should be passed + * to upper layer. + */ +enum iwl_mld_reorder_result +iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, + int queue, struct ieee80211_sta *sta, + struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) +{ + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + struct iwl_mld_baid_data *baid_data; + struct iwl_mld_reorder_buffer *buffer; + struct iwl_mld_reorder_buf_entry *entries; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u32 reorder = le32_to_cpu(desc->reorder_data); + bool amsdu, last_subframe, is_old_sn, is_dup; + u8 tid = ieee80211_get_tid(hdr); + u8 baid; + u16 nssn, sn; + u32 sta_mask = 0; + int index; + u8 link_id; + + baid = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_BAID_MASK); + + /* This also covers the case of receiving a Block Ack Request + * outside a BA session; we'll pass it to mac80211 and that + * then sends a delBA action frame. + * This also covers pure monitor mode, in which case we won't + * have any BA sessions. + */ + if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) + return IWL_MLD_PASS_SKB; + + /* no sta yet */ + if (WARN_ONCE(!sta, + "Got valid BAID without a valid station assigned\n")) + return IWL_MLD_PASS_SKB; + + /* not a data packet */ + if (!ieee80211_is_data_qos(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return IWL_MLD_PASS_SKB; + + if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) + return IWL_MLD_PASS_SKB; + + baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!baid_data) { + IWL_DEBUG_HT(mld, + "Got valid BAID but no baid allocated, bypass re-ordering (BAID=%d reorder=0x%x)\n", + baid, reorder); + return IWL_MLD_PASS_SKB; + } + + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + sta_mask |= BIT(mld_link_sta->fw_id); + + /* verify the BAID is correctly mapped to the sta and tid */ + if (IWL_FW_CHECK(mld, + tid != baid_data->tid || + !(sta_mask & baid_data->sta_mask), + "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n", + baid, baid_data->sta_mask, baid_data->tid, + sta_mask, tid)) + return IWL_MLD_PASS_SKB; + + buffer = &baid_data->reorder_buf[queue]; + entries = &baid_data->entries[queue * baid_data->entries_per_queue]; + + is_old_sn = !!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN); + + if (!buffer->valid && is_old_sn) + return IWL_MLD_PASS_SKB; + + buffer->valid = true; + + is_dup = !!(desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE)); + + /* drop any duplicated or outdated packets */ + if (is_dup || is_old_sn) + return IWL_MLD_DROP_SKB; + + sn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_SN_MASK); + nssn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_NSSN_MASK); + amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; + last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; + + /* release immediately if allowed by nssn and no stored frames */ + if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { + if (!amsdu || last_subframe) + buffer->head_sn = nssn; + return IWL_MLD_PASS_SKB; + } + + /* release immediately if there are no stored frames, and the sn is + * equal to the head. + * This can happen due to reorder timer, where NSSN is behind head_sn. + * When we released everything, and we got the next frame in the + * sequence, according to the NSSN we can't release immediately, + * while technically there is no hole and we can move forward. + */ + if (!buffer->num_stored && sn == buffer->head_sn) { + if (!amsdu || last_subframe) + buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); + return IWL_MLD_PASS_SKB; + } + + /* put in reorder buffer */ + index = sn % baid_data->buf_size; + __skb_queue_tail(&entries[index].frames, skb); + buffer->num_stored++; + + /* We cannot trust NSSN for AMSDU sub-frames that are not the last. The + * reason is that NSSN advances on the first sub-frame, and may cause + * the reorder buffer to advance before all the sub-frames arrive. + * + * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with + * SN 1. NSSN for first sub frame will be 3 with the result of driver + * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is + * already ahead and it will be dropped. + * If the last sub-frame is not on this queue - we will get frame + * release notification with up to date NSSN. + */ + if (!amsdu || last_subframe) + iwl_mld_reorder_release_frames(mld, sta, napi, baid_data, + buffer, nssn); + + return IWL_MLD_BUFFERED_SKB; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_reorder); + +static void iwl_mld_rx_agg_session_expired(struct timer_list *t) +{ + struct iwl_mld_baid_data *data = + from_timer(data, t, session_timer); + struct iwl_mld_baid_data __rcu **rcu_ptr = data->rcu_ptr; + struct iwl_mld_baid_data *ba_data; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + unsigned long timeout; + unsigned int sta_id; + + rcu_read_lock(); + + ba_data = rcu_dereference(*rcu_ptr); + if (WARN_ON(!ba_data)) + goto unlock; + + if (WARN_ON(!ba_data->timeout)) + goto unlock; + + timeout = ba_data->last_rx_timestamp + + TU_TO_JIFFIES(ba_data->timeout * 2); + if (time_is_after_jiffies(timeout)) { + mod_timer(&ba_data->session_timer, timeout); + goto unlock; + } + + /* timer expired, pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]); + + /* sta should be valid unless the following happens: + * The firmware asserts which triggers a reconfig flow, but + * the reconfig fails before we set the pointer to sta into + * the fw_id_to_link_sta pointer table. mac80211 can't stop + * A-MPDU and hence the timer continues to run. Then, the + * timer expires and sta is NULL. + */ + if (IS_ERR_OR_NULL(link_sta) || WARN_ON(!link_sta->sta)) + goto unlock; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + ieee80211_rx_ba_timer_expired(mld_sta->vif, link_sta->sta->addr, + ba_data->tid); +unlock: + rcu_read_unlock(); +} + +static int +iwl_mld_stop_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, int tid) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE), + .remove.sta_id_mask = + cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)), + .remove.tid = cpu_to_le32(tid), + + }; + int ret; + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + RX_BAID_ALLOCATION_CONFIG_CMD), + &cmd); + if (ret) + return ret; + + IWL_DEBUG_HT(mld, "RX BA Session stopped in fw\n"); + + return ret; +} + +static int +iwl_mld_start_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = cpu_to_le32(IWL_RX_BAID_ACTION_ADD), + .alloc.sta_id_mask = + cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)), + .alloc.tid = tid, + .alloc.ssn = cpu_to_le16(ssn), + .alloc.win_size = cpu_to_le16(buf_size), + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD), + .flags = CMD_WANT_SKB, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; + struct iwl_rx_baid_cfg_resp *resp; + struct iwl_rx_packet *pkt; + u32 resp_len; + int ret, baid; + + BUILD_BUG_ON(sizeof(*resp) != sizeof(baid)); + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + return ret; + + pkt = hcmd.resp_pkt; + + resp_len = iwl_rx_packet_payload_len(pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp), + "BAID_ALLOC_CMD: unexpected response length %d\n", + resp_len)) { + ret = -EIO; + goto out; + } + + IWL_DEBUG_HT(mld, "RX BA Session started in fw\n"); + + resp = (void *)pkt->data; + baid = le32_to_cpu(resp->baid); + + if (IWL_FW_CHECK(mld, baid < 0 || baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "BAID_ALLOC_CMD: invalid BAID response %d\n", baid)) { + ret = -EINVAL; + goto out; + } + + ret = baid; +out: + iwl_free_resp(&hcmd); + return ret; +} + +static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld, + struct iwl_mld_baid_data *data, + u16 ssn) +{ + for (int i = 0; i < mld->trans->num_rx_queues; i++) { + struct iwl_mld_reorder_buffer *reorder_buf = + &data->reorder_buf[i]; + struct iwl_mld_reorder_buf_entry *entries = + &data->entries[i * data->entries_per_queue]; + + reorder_buf->head_sn = ssn; + reorder_buf->queue = i; + + for (int j = 0; j < data->buf_size; j++) + __skb_queue_head_init(&entries[j].frames); + } +} + +static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld, + struct iwl_mld_baid_data *data) +{ + struct iwl_mld_delba_data delba_data = { + .baid = data->baid, + }; + + iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA, + &delba_data, sizeof(delba_data)); + + for (int i = 0; i < mld->trans->num_rx_queues; i++) { + struct iwl_mld_reorder_buffer *reorder_buf = + &data->reorder_buf[i]; + struct iwl_mld_reorder_buf_entry *entries = + &data->entries[i * data->entries_per_queue]; + + if (likely(!reorder_buf->num_stored)) + continue; + + /* This shouldn't happen in regular DELBA since the RX queues + * sync internal DELBA notification should trigger a release + * of all frames in the reorder buffer. + */ + WARN_ON(1); + + for (int j = 0; j < data->buf_size; j++) + __skb_queue_purge(&entries[j].frames); + } +} + +int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size, u16 timeout) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_baid_data *baid_data = NULL; + u32 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]); + int ret, baid; + u32 sta_mask; + + lockdep_assert_wiphy(mld->wiphy); + + if (mld->num_rx_ba_sessions >= IWL_MAX_BAID) { + IWL_DEBUG_HT(mld, + "Max num of RX BA sessions reached; blocking new session\n"); + return -ENOSPC; + } + + sta_mask = iwl_mld_fw_sta_id_mask(mld, sta); + if (WARN_ON(!sta_mask)) + return -EINVAL; + + /* sparse doesn't like the __align() so don't check */ +#ifndef __CHECKER__ + /* The division below will be OK if either the cache line size + * can be divided by the entry size (ALIGN will round up) or if + * the entry size can be divided by the cache line size, in which + * case the ALIGN() will do nothing. + */ + BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) && + sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES); +#endif + + /* Upward align the reorder buffer size to fill an entire cache + * line for each queue, to avoid sharing cache lines between + * different queues. + */ + reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES); + + /* Allocate here so if allocation fails we can bail out early + * before starting the BA session in the firmware + */ + baid_data = kzalloc(sizeof(*baid_data) + + mld->trans->num_rx_queues * reorder_buf_size, + GFP_KERNEL); + if (!baid_data) + return -ENOMEM; + + /* This division is why we need the above BUILD_BUG_ON(), + * if that doesn't hold then this will not be right. + */ + baid_data->entries_per_queue = + reorder_buf_size / sizeof(baid_data->entries[0]); + + baid = iwl_mld_start_ba_in_fw(mld, sta, tid, ssn, buf_size); + if (baid < 0) { + ret = baid; + goto out_free; + } + + mld->num_rx_ba_sessions++; + mld_sta->tid_to_baid[tid] = baid; + + baid_data->baid = baid; + baid_data->mld = mld; + baid_data->tid = tid; + baid_data->buf_size = buf_size; + baid_data->sta_mask = sta_mask; + baid_data->timeout = timeout; + baid_data->last_rx_timestamp = jiffies; + baid_data->rcu_ptr = &mld->fw_id_to_ba[baid]; + + iwl_mld_init_reorder_buffer(mld, baid_data, ssn); + + timer_setup(&baid_data->session_timer, iwl_mld_rx_agg_session_expired, + 0); + if (timeout) + mod_timer(&baid_data->session_timer, + TU_TO_EXP_TIME(timeout * 2)); + + IWL_DEBUG_HT(mld, "STA mask=0x%x (tid=%d) is assigned to BAID %d\n", + baid_data->sta_mask, tid, baid); + + /* protect the BA data with RCU to cover a case where our + * internal RX sync mechanism will timeout (not that it's + * supposed to happen) and we will free the session data while + * RX is being processed in parallel + */ + WARN_ON(rcu_access_pointer(mld->fw_id_to_ba[baid])); + rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data); + + return 0; + +out_free: + kfree(baid_data); + return ret; +} + +int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + int baid = mld_sta->tid_to_baid[tid]; + struct iwl_mld_baid_data *baid_data; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* during firmware restart, do not send the command as the firmware no + * longer recognizes the session. instead, only clear the driver BA + * session data. + */ + if (!mld->fw_status.in_hw_restart) { + ret = iwl_mld_stop_ba_in_fw(mld, sta, tid); + if (ret) + return ret; + } + + if (!WARN_ON(mld->num_rx_ba_sessions == 0)) + mld->num_rx_ba_sessions--; + + baid_data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]); + if (WARN_ON(!baid_data)) + return -EINVAL; + + if (timer_pending(&baid_data->session_timer)) + timer_shutdown_sync(&baid_data->session_timer); + + iwl_mld_free_reorder_buffer(mld, baid_data); + + RCU_INIT_POINTER(mld->fw_id_to_ba[baid], NULL); + kfree_rcu(baid_data, rcu_head); + + IWL_DEBUG_HT(mld, "BAID %d is free\n", baid); + + return 0; +} + +int iwl_mld_update_sta_baids(struct iwl_mld *mld, + 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 (mld->fw_status.in_hw_restart) + return 0; + + BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); + + for (baid = 0; baid < ARRAY_SIZE(mld->fw_id_to_ba); baid++) { + struct iwl_mld_baid_data *data; + int ret; + + data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]); + 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_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + return ret; + data->sta_mask = new_sta_mask; + } + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h new file mode 100644 index 000000000000..651c80d1c7cd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_agg_h__ +#define __iwl_agg_h__ + +#include "mld.h" +#include "fw/api/rx.h" + +/** + * struct iwl_mld_reorder_buffer - per ra/tid/queue reorder buffer + * @head_sn: reorder window head sequence number + * @num_stored: number of MPDUs stored in the buffer + * @queue: queue of this reorder buffer + * @valid: true if reordering is valid for this queue + */ +struct iwl_mld_reorder_buffer { + u16 head_sn; + u16 num_stored; + int queue; + bool valid; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_reorder_buf_entry - reorder buffer entry per-queue/per-seqno + * @frames: list of skbs stored. a list is necessary because in an A-MSDU, + * all sub-frames share the same sequence number, so they are stored + * together in the same list. + */ +struct iwl_mld_reorder_buf_entry { + struct sk_buff_head frames; +} +#ifndef __CHECKER__ +/* sparse doesn't like this construct: "bad integer constant expression" */ +__aligned(roundup_pow_of_two(sizeof(struct sk_buff_head))) +#endif +; + +/** + * struct iwl_mld_baid_data - Block Ack session data + * @rcu_head: RCU head for freeing this data + * @sta_mask: station mask for the BAID + * @tid: tid of the session + * @baid: baid of the session + * @buf_size: the reorder buffer size as set by the last ADDBA request + * @entries_per_queue: number of buffers per queue, this actually gets + * aligned up to avoid cache line sharing between queues + * @timeout: the timeout value specified in the ADDBA request. + * @last_rx_timestamp: timestamp of the last received packet (in jiffies). This + * value is updated only when the configured @timeout has passed since + * the last update to minimize cache bouncing between RX queues. + * @session_timer: timer is set to expire after 2 * @timeout (since we want + * to minimize the cache bouncing by updating @last_rx_timestamp only once + * after @timeout has passed). If no packets are received within this + * period, it informs mac80211 to initiate delBA flow, terminating the + * BA session. + * @rcu_ptr: BA data RCU protected access + * @mld: mld pointer, needed for timer context + * @reorder_buf: reorder buffer, allocated per queue + * @entries: data + */ +struct iwl_mld_baid_data { + struct rcu_head rcu_head; + u32 sta_mask; + u8 tid; + u8 baid; + u16 buf_size; + u16 entries_per_queue; + u16 timeout; + struct timer_list session_timer; + unsigned long last_rx_timestamp; + struct iwl_mld_baid_data __rcu **rcu_ptr; + struct iwl_mld *mld; + struct iwl_mld_reorder_buffer reorder_buf[IWL_MAX_RX_HW_QUEUES]; + struct iwl_mld_reorder_buf_entry entries[] ____cacheline_aligned_in_smp; +}; + +/** + * struct iwl_mld_delba_data - RX queue sync data for %IWL_MLD_RXQ_NOTIF_DEL_BA + * + * @baid: Block Ack id, used to identify the BA session to be removed + */ +struct iwl_mld_delba_data { + u32 baid; +} __packed; + +/** + * enum iwl_mld_reorder_result - Possible return values for iwl_mld_reorder() + * indicating how the caller should handle the skb based on the result. + * + * @IWL_MLD_PASS_SKB: skb should be passed to upper layer. + * @IWL_MLD_BUFFERED_SKB: skb has been buffered, don't pass it to upper layer. + * @IWL_MLD_DROP_SKB: skb should be dropped and freed by the caller. + */ +enum iwl_mld_reorder_result { + IWL_MLD_PASS_SKB, + IWL_MLD_BUFFERED_SKB, + IWL_MLD_DROP_SKB +}; + +int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size, u16 timeout); +int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid); + +enum iwl_mld_reorder_result +iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, + int queue, struct ieee80211_sta *sta, + struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc); + +void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); +void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, + int queue); + +void iwl_mld_del_ba(struct iwl_mld *mld, int queue, + struct iwl_mld_delba_data *data); + +int iwl_mld_update_sta_baids(struct iwl_mld *mld, + u32 old_sta_mask, + u32 new_sta_mask); + +#endif /* __iwl_agg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c new file mode 100644 index 000000000000..571eabd0b511 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include + +#include + +#include "ap.h" +#include "hcmd.h" +#include "tx.h" +#include "power.h" +#include "key.h" +#include "iwl-utils.h" + +#include "fw/api/sta.h" + +void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index, + u8 *beacon, u32 frame_size) +{ + u32 tim_idx; + struct ieee80211_mgmt *mgmt = (void *)beacon; + + /* The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx + 1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && beacon[tim_idx] == WLAN_EID_TIM) + *tim_index = cpu_to_le32(tim_idx); + else + IWL_WARN(mld, "Unable to find TIM Element in beacon\n"); +} + +u8 iwl_mld_get_rate_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + enum nl80211_band band) +{ + u32 legacy = link->beacon_tx_rate.control[band].legacy; + u32 rate_idx, rate_flags = 0, fw_rate; + + /* if beacon rate was configured try using it */ + if (hweight32(legacy) == 1) { + u32 rate = ffs(legacy) - 1; + struct ieee80211_supported_band *sband = + mld->hw->wiphy->bands[band]; + + rate_idx = sband->bitrates[rate].hw_value; + } else { + rate_idx = iwl_mld_get_lowest_rate(mld, info, vif); + } + + if (rate_idx <= IWL_LAST_CCK_RATE) + rate_flags = IWL_MAC_BEACON_CCK; + + /* Legacy rates are indexed as follows: + * 0 - 3 for CCK and 0 - 7 for OFDM. + */ + fw_rate = (rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : rate_idx); + + return fw_rate | rate_flags; +} + +int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd) +{ + struct iwl_host_cmd hcmd = { + .id = BEACON_TEMPLATE_CMD, + }; + + hcmd.len[0] = sizeof(*cmd); + hcmd.data[0] = cmd; + + hcmd.len[1] = beacon->len; + hcmd.data[1] = beacon->data; + hcmd.dataflags[1] = IWL_HCMD_DFL_DUP; + + return iwl_mld_send_cmd(mld, &hcmd); +} + +static int iwl_mld_fill_beacon_template_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); + struct ieee80211_chanctx_conf *ctx; + bool enable_fils; + u16 flags = 0; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + cmd->link_id = cpu_to_le32(mld_link->fw_id); + + ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + if (WARN_ON(!ctx || !ctx->def.chan)) + return -EINVAL; + + enable_fils = cfg80211_channel_is_psc(ctx->def.chan) || + (ctx->def.chan->band == NL80211_BAND_6GHZ && + ctx->def.width >= NL80211_CHAN_WIDTH_80); + + if (enable_fils) { + flags |= IWL_MAC_BEACON_FILS; + cmd->short_ssid = cpu_to_le32(~crc32_le(~0, vif->cfg.ssid, + vif->cfg.ssid_len)); + } + + cmd->byte_cnt = cpu_to_le16((u16)beacon->len); + + flags |= iwl_mld_get_rate_flags(mld, info, vif, link, + ctx->def.chan->band); + + cmd->flags = cpu_to_le16(flags); + + if (vif->type == NL80211_IFTYPE_AP) { + iwl_mld_set_tim_idx(mld, &cmd->tim_idx, + beacon->data, beacon->len); + + cmd->btwt_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len)); + } + + cmd->csa_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); + cmd->ecsa_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); + + return 0; +} + +/* The beacon template for the AP/GO/IBSS has changed and needs update */ +int iwl_mld_update_beacon_template(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mac_beacon_cmd cmd = {}; + struct sk_buff *beacon; + int ret; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); +#endif + + WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC); + + if (IWL_MLD_NON_TRANSMITTING_AP) + return 0; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mld_vif->beacon_inject_active) { + IWL_DEBUG_INFO(mld, + "Can't update template, beacon injection's active\n"); + return -EBUSY; + } + +#endif + beacon = ieee80211_beacon_get_template(mld->hw, vif, NULL, + link_conf->link_id); + if (!beacon) + return -ENOMEM; + + ret = iwl_mld_fill_beacon_template_cmd(mld, vif, beacon, &cmd, + link_conf); + + if (!ret) + ret = iwl_mld_send_beacon_template_cmd(mld, beacon, &cmd); + + dev_kfree_skb(beacon); + + return ret; +} + +void iwl_mld_free_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif) +{ + struct iwl_mld_link *link; + + if (WARN_ON(key->link_id < 0)) + return; + + link = iwl_mld_link_dereference_check(mld_vif, key->link_id); + if (WARN_ON(!link)) + return; + + for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { + if (link->ap_early_keys[i] != key) + continue; + /* Those weren't sent to FW, so should be marked as INVALID */ + if (WARN_ON(key->hw_key_idx != STA_KEY_IDX_INVALID)) + key->hw_key_idx = STA_KEY_IDX_INVALID; + link->ap_early_keys[i] = NULL; + } +} + +int iwl_mld_store_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif) +{ + struct iwl_mld_link *link; + + if (WARN_ON(key->link_id < 0)) + return -EINVAL; + + link = iwl_mld_link_dereference_check(mld_vif, key->link_id); + if (WARN_ON(!link)) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { + if (!link->ap_early_keys[i]) { + link->ap_early_keys[i] = key; + return 0; + } + } + + return -ENOSPC; +} + +static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret = 0; + + if (WARN_ON(!link)) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(mld_link->ap_early_keys); i++) { + struct ieee80211_key_conf *key = mld_link->ap_early_keys[i]; + + if (!key) + continue; + + mld_link->ap_early_keys[i] = NULL; + + ret = iwl_mld_add_key(mld, vif, NULL, key); + if (ret) + break; + } + return ret; +} + +int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + + ret = iwl_mld_update_beacon_template(mld, vif, link); + if (ret) + return ret; + + /* the link should be already activated when assigning chan context, + * and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated + */ + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_ALL & + ~(LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_EHT_PARAMS)); + if (ret) + return ret; + + ret = iwl_mld_add_mcast_sta(mld, vif, link); + if (ret) + return ret; + + ret = iwl_mld_add_bcast_sta(mld, vif, link); + if (ret) + goto rm_mcast; + + /* Those keys were configured by the upper layers before starting the + * AP. Now that it is started and the bcast and mcast sta were added to + * the FW, we can add the keys too. + */ + ret = iwl_mld_send_ap_early_keys(mld, vif, link); + if (ret) + goto rm_bcast; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + iwl_mld_vif_update_low_latency(mld, vif, true, + LOW_LATENCY_VIF_TYPE); + + mld_vif->ap_ibss_active = true; + + if (vif->p2p && mld->p2p_device_vif) + return iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); + + return 0; +rm_bcast: + iwl_mld_remove_bcast_sta(mld, vif, link); +rm_mcast: + iwl_mld_remove_mcast_sta(mld, vif, link); + return ret; +} + +void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + mld_vif->ap_ibss_active = false; + + if (vif->p2p && mld->p2p_device_vif) + iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + iwl_mld_vif_update_low_latency(mld, vif, false, + LOW_LATENCY_VIF_TYPE); + + iwl_mld_remove_bcast_sta(mld, vif, link); + + iwl_mld_remove_mcast_sta(mld, vif, link); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.h b/drivers/net/wireless/intel/iwlwifi/mld/ap.h new file mode 100644 index 000000000000..4a6f52b9552d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_ap_h__ +#define __iwl_ap_h__ + +#include "mld.h" +#include "iface.h" + +#include "fw/api/tx.h" + +int iwl_mld_update_beacon_template(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); + +int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_store_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif); + +void iwl_mld_free_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif); + +u8 iwl_mld_get_rate_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + enum nl80211_band band); + +void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index, + u8 *beacon, u32 frame_size); + +int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd); + +#endif /* __iwl_ap_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c new file mode 100644 index 000000000000..5f262bd43f21 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "fw/api/coex.h" + +#include "coex.h" +#include "mld.h" +#include "hcmd.h" +#include "mlo.h" + +int iwl_mld_send_bt_init_conf(struct iwl_mld *mld) +{ + struct iwl_bt_coex_cmd cmd = { + .mode = cpu_to_le32(BT_COEX_NW), + .enabled_modules = cpu_to_le32(BT_COEX_MPLUT_ENABLED | + BT_COEX_HIGH_BAND_RET), + }; + + return iwl_mld_send_cmd_pdu(mld, BT_CONFIG, &cmd); +} + +void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + const struct iwl_bt_coex_profile_notif zero_notif = {}; + /* zeroed structure means that BT is OFF */ + bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif)); + + if (bt_is_active == mld->bt_is_active) + return; + + IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF"); + + mld->bt_is_active = bt_is_active; + + iwl_mld_emlsr_check_bt(mld); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.h b/drivers/net/wireless/intel/iwlwifi/mld/coex.h new file mode 100644 index 000000000000..a77c5dc9613c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_coex_h__ +#define __iwl_mld_coex_h__ + +#include "mld.h" + +int iwl_mld_send_bt_init_conf(struct iwl_mld *mld); + +void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h new file mode 100644 index 000000000000..9a24996014b8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_constants_h__ +#define __iwl_mld_constants_h__ + +#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 4 +#define IWL_MLD_MISSED_BEACONS_THRESHOLD 8 +#define IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG 19 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH 15 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 11 +#define IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH -72 + +#define IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ +#define IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ + +#define IWL_MLD_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MLD_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) + +#define IWL_MLD_PS_SNOOZE_INTERVAL 25 +#define IWL_MLD_PS_SNOOZE_INTERVAL 25 +#define IWL_MLD_PS_SNOOZE_WINDOW 50 + +#define IWL_MLD_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 +#define IWL_MLD_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 + +#define IWL_MLD_PS_HEAVY_TX_THLD_PERCENT 50 +#define IWL_MLD_PS_HEAVY_RX_THLD_PERCENT 50 +#define IWL_MLD_PS_HEAVY_TX_THLD_PACKETS 20 +#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) + +/* OMI reduced BW thresholds (channel load percentage) */ +#define IWL_MLD_OMI_ENTER_CHAN_LOAD 10 +#define IWL_MLD_OMI_EXIT_CHAN_LOAD_160 20 +#define IWL_MLD_OMI_EXIT_CHAN_LOAD_320 30 +/* time (in milliseconds) to let AP "settle" the OMI */ +#define IWL_MLD_OMI_AP_SETTLE_DELAY 27 +/* time (in milliseconds) to not enter OMI reduced BW after leaving */ +#define IWL_MLD_OMI_EXIT_PROTECTION 5000 + +#define IWL_MLD_DIS_RANDOM_FW_ID false +#define IWL_MLD_D3_DEBUG false +#define IWL_MLD_NON_TRANSMITTING_AP false +#define IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT 3000 /* in seconds */ +#define IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT 60 /* in seconds */ +#define IWL_MLD_CONN_LISTEN_INTERVAL 10 +#define IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE 0 +#define IWL_MLD_AUTO_EML_ENABLE true + +#define IWL_MLD_HIGH_RSSI_THRESH_20MHZ -67 +#define IWL_MLD_LOW_RSSI_THRESH_20MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_40MHZ -64 +#define IWL_MLD_LOW_RSSI_THRESH_40MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_80MHZ -61 +#define IWL_MLD_LOW_RSSI_THRESH_80MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_160MHZ -58 +#define IWL_MLD_LOW_RSSI_THRESH_160MHZ -72 + +#define IWL_MLD_ENTER_EMLSR_TPT_THRESH 400 +#define IWL_MLD_CHAN_LOAD_THRESH 2 /* in percentage */ + +#define IWL_MLD_FTM_INITIATOR_ALGO IWL_TOF_ALGO_TYPE_MAX_LIKE +#define IWL_MLD_FTM_INITIATOR_DYNACK true +#define IWL_MLD_FTM_LMR_FEEDBACK_TERMINATE false +#define IWL_MLD_FTM_TEST_INCORRECT_SAC false +#define IWL_MLD_FTM_R2I_MAX_REP 7 +#define IWL_MLD_FTM_I2R_MAX_REP 7 +#define IWL_MLD_FTM_R2I_MAX_STS 1 +#define IWL_MLD_FTM_I2R_MAX_STS 1 +#define IWL_MLD_FTM_R2I_MAX_TOTAL_LTF 3 +#define IWL_MLD_FTM_I2R_MAX_TOTAL_LTF 3 +#define IWL_MLD_FTM_RESP_NDP_SUPPORT true +#define IWL_MLD_FTM_RESP_LMR_FEEDBACK_SUPPORT true +#define IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 7 +#define IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR 1000 + +#endif /* __iwl_mld_constants_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c new file mode 100644 index 000000000000..5a7207accd86 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -0,0 +1,1998 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mld.h" + +#include "d3.h" +#include "power.h" +#include "hcmd.h" +#include "iface.h" +#include "mcc.h" +#include "sta.h" +#include "mlo.h" + +#include "fw/api/d3.h" +#include "fw/api/offload.h" +#include "fw/api/sta.h" +#include "fw/dbg.h" + +#include +#include +#include + +/** + * enum iwl_mld_d3_notif - d3 notifications + * @IWL_D3_NOTIF_WOWLAN_INFO: WOWLAN_INFO_NOTIF is expected/was received + * @IWL_D3_NOTIF_WOWLAN_WAKE_PKT: WOWLAN_WAKE_PKT_NOTIF is expected/was received + * @IWL_D3_NOTIF_PROT_OFFLOAD: PROT_OFFLOAD_NOTIF is expected/was received + * @IWL_D3_ND_MATCH_INFO: OFFLOAD_MATCH_INFO_NOTIF is expected/was received + * @IWL_D3_NOTIF_D3_END_NOTIF: D3_END_NOTIF is expected/was received + */ +enum iwl_mld_d3_notif { + IWL_D3_NOTIF_WOWLAN_INFO = BIT(0), + IWL_D3_NOTIF_WOWLAN_WAKE_PKT = BIT(1), + IWL_D3_NOTIF_PROT_OFFLOAD = BIT(2), + IWL_D3_ND_MATCH_INFO = BIT(3), + IWL_D3_NOTIF_D3_END_NOTIF = BIT(4) +}; + +struct iwl_mld_resume_key_iter_data { + struct iwl_mld *mld; + struct iwl_mld_wowlan_status *wowlan_status; + u32 num_keys, gtk_cipher, igtk_cipher, bigtk_cipher; + bool unhandled_cipher; +}; + +struct iwl_mld_suspend_key_iter_data { + struct iwl_wowlan_rsc_tsc_params_cmd *rsc; + bool have_rsc; + int gtks; + int found_gtk_idx[4]; + __le32 gtk_cipher; + __le32 igtk_cipher; + __le32 bigtk_cipher; +}; + +struct iwl_mld_mcast_key_data { + u8 key[WOWLAN_KEY_MAX_SIZE]; + u8 len; + u8 flags; + u8 id; + union { + struct { + struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; + struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; + } gtk; + struct { + struct ieee80211_key_seq cmac_gmac_seq; + } igtk_bigtk; + }; + +}; + +/** + * struct iwl_mld_wowlan_status - contains wowlan status data from + * all wowlan notifications + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched patterns on packets + * @last_qos_seq: QoS sequence counter of offloaded tid + * @num_of_gtk_rekeys: number of GTK rekeys during D3 + * @tid_offloaded_tx: tid used by the firmware to transmit data packets + * while in wowlan + * @wake_packet: wakeup packet received + * @wake_packet_length: wake packet length + * @wake_packet_bufsize: wake packet bufsize + * @gtk: data of the last two used gtk's by the FW upon resume + * @igtk: data of the last used igtk by the FW upon resume + * @bigtk: data of the last two used gtk's by the FW upon resume + * @ptk: last seq numbers per tid passed by the FW, + * holds both in tkip and aes formats + */ +struct iwl_mld_wowlan_status { + u32 wakeup_reasons; + u64 replay_ctr; + u16 pattern_number; + u16 last_qos_seq; + u32 num_of_gtk_rekeys; + u8 tid_offloaded_tx; + u8 *wake_packet; + u32 wake_packet_length; + u32 wake_packet_bufsize; + struct iwl_mld_mcast_key_data gtk[WOWLAN_GTK_KEYS_NUM]; + struct iwl_mld_mcast_key_data igtk; + struct iwl_mld_mcast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM]; + struct { + struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; + struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; + + } ptk; +}; + +#define NETDETECT_QUERY_BUF_LEN \ + (sizeof(struct iwl_scan_offload_profile_match) * \ + IWL_SCAN_MAX_PROFILES_V2) + +/** + * struct iwl_mld_netdetect_res - contains netdetect results from + * match_info_notif + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @matches: array of match information, one for each match + */ +struct iwl_mld_netdetect_res { + u32 matched_profiles; + u8 matches[NETDETECT_QUERY_BUF_LEN]; +}; + +/** + * struct iwl_mld_resume_data - d3 resume flow data + * @notifs_expected: bitmap of expected notifications from fw, + * see &enum iwl_mld_d3_notif + * @notifs_received: bitmap of received notifications from fw, + * see &enum iwl_mld_d3_notif + * @d3_end_flags: bitmap of flags from d3_end_notif + * @notif_handling_err: error handling one of the resume notifications + * @wowlan_status: wowlan status data from all wowlan notifications + * @netdetect_res: contains netdetect results from match_info_notif + */ +struct iwl_mld_resume_data { + u32 notifs_expected; + u32 notifs_received; + u32 d3_end_flags; + bool notif_handling_err; + struct iwl_mld_wowlan_status *wowlan_status; + struct iwl_mld_netdetect_res *netdetect_res; +}; + +#define IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT \ + (IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET | \ + IWL_WOWLAN_WAKEUP_BY_PATTERN | \ + IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN |\ + IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN_WILDCARD |\ + IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN |\ + IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN_WILDCARD) + +#define IWL_WOWLAN_OFFLOAD_TID 0 + +void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + + lockdep_assert_wiphy(mld->wiphy); + + wowlan_data->rekey_data.kek_len = data->kek_len; + wowlan_data->rekey_data.kck_len = data->kck_len; + memcpy(wowlan_data->rekey_data.kek, data->kek, data->kek_len); + memcpy(wowlan_data->rekey_data.kck, data->kck, data->kck_len); + wowlan_data->rekey_data.akm = data->akm & 0xFF; + wowlan_data->rekey_data.replay_ctr = + cpu_to_le64(be64_to_cpup((const __be64 *)data->replay_ctr)); + wowlan_data->rekey_data.valid = true; +} + +#if IS_ENABLED(CONFIG_IPV6) +void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + struct inet6_ifaddr *ifa; + int idx = 0; + + memset(wowlan_data->tentative_addrs, 0, + sizeof(wowlan_data->tentative_addrs)); + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + wowlan_data->target_ipv6_addrs[idx] = ifa->addr; + if (ifa->flags & IFA_F_TENTATIVE) + __set_bit(idx, wowlan_data->tentative_addrs); + idx++; + if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) + break; + } + read_unlock_bh(&idev->lock); + + wowlan_data->num_target_ipv6_addrs = idx; +} +#endif + +enum rt_status { + FW_ALIVE, + FW_NEEDS_RESET, + FW_ERROR, +}; + +static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + u32 err_id; + + /* check for lmac1 error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.lmac_error_event_table[0], + &err_id)) { + if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + + return FW_NEEDS_RESET; + } + return FW_ERROR; + } + + /* check if we have lmac2 set and check for error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.lmac_error_event_table[1], + NULL)) + return FW_ERROR; + + /* check for umac error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.umac_error_event_table, + NULL)) + return FW_ERROR; + + return FW_ALIVE; +} + +static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif); + + if (rt_status == FW_ALIVE) + return false; + + if (rt_status == FW_ERROR) { + IWL_ERR(mld, "FW Error occurred during suspend\n"); + iwl_fwrt_dump_error_logs(&mld->fwrt); + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); + } + + return true; +} + +static int +iwl_mld_netdetect_config(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct cfg80211_wowlan *wowlan) +{ + int ret; + struct cfg80211_sched_scan_request *netdetect_cfg = + wowlan->nd_config; + struct ieee80211_scan_ies ies = {}; + + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, true); + if (ret) + return ret; + + ret = iwl_mld_sched_scan_start(mld, vif, netdetect_cfg, &ies, + IWL_MLD_SCAN_NETDETECT); + return ret; +} + +static void +iwl_mld_le64_to_tkip_seq(__le64 le_pn, struct ieee80211_key_seq *seq) +{ + u64 pn = le64_to_cpu(le_pn); + + seq->tkip.iv16 = (u16)pn; + seq->tkip.iv32 = (u32)(pn >> 16); +} + +static void +iwl_mld_le64_to_aes_seq(__le64 le_pn, struct ieee80211_key_seq *seq) +{ + u64 pn = le64_to_cpu(le_pn); + + seq->ccmp.pn[0] = pn >> 40; + seq->ccmp.pn[1] = pn >> 32; + seq->ccmp.pn[2] = pn >> 24; + seq->ccmp.pn[3] = pn >> 16; + seq->ccmp.pn[4] = pn >> 8; + seq->ccmp.pn[5] = pn; +} + +static void +iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data *gtk_data, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc, + int rsc_idx) +{ + struct ieee80211_key_seq *aes_seq = gtk_data->gtk.aes_seq; + struct ieee80211_key_seq *tkip_seq = gtk_data->gtk.tkip_seq; + + if (rsc_idx >= ARRAY_SIZE(sc->mcast_rsc)) + return; + + /* We store both the TKIP and AES representations coming from the + * FW because we decode the data from there before we iterate + * the keys and know which type is used. + */ + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_tkip_seq(sc->mcast_rsc[rsc_idx][tid], + &tkip_seq[tid]); + iwl_mld_le64_to_aes_seq(sc->mcast_rsc[rsc_idx][tid], + &aes_seq[tid]); + } +} + +static void +iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_gtk_status_v3 *gtk_data, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc) +{ + int status_idx = 0; + + BUILD_BUG_ON(sizeof(wowlan_status->gtk[0].key) < + sizeof(gtk_data[0].key)); + BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->gtk) < WOWLAN_GTK_KEYS_NUM); + + for (int notif_idx = 0; notif_idx < ARRAY_SIZE(wowlan_status->gtk); + notif_idx++) { + int rsc_idx; + + if (!(gtk_data[notif_idx].key_len)) + continue; + + wowlan_status->gtk[status_idx].len = + gtk_data[notif_idx].key_len; + wowlan_status->gtk[status_idx].flags = + gtk_data[notif_idx].key_flags; + wowlan_status->gtk[status_idx].id = + wowlan_status->gtk[status_idx].flags & + IWL_WOWLAN_GTK_IDX_MASK; + memcpy(wowlan_status->gtk[status_idx].key, + gtk_data[notif_idx].key, + sizeof(gtk_data[notif_idx].key)); + + /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc + * The gtk ids can be any two numbers between 0 and 3, + * the id_map maps between the key id and the index in sc->mcast + */ + rsc_idx = + sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id]; + iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx], + sc, rsc_idx); + + /* if it's as long as the TKIP encryption key, copy MIC key */ + if (wowlan_status->gtk[status_idx].len == + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) + memcpy(wowlan_status->gtk[status_idx].key + + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + gtk_data[notif_idx].tkip_mic_key, + sizeof(gtk_data[notif_idx].tkip_mic_key)); + status_idx++; + } +} + +static void +iwl_mld_convert_ptk_resume_seq(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc) +{ + struct ieee80211_key_seq *aes_seq = wowlan_status->ptk.aes_seq; + struct ieee80211_key_seq *tkip_seq = wowlan_status->ptk.tkip_seq; + + BUILD_BUG_ON(ARRAY_SIZE(sc->ucast_rsc) != IWL_MAX_TID_COUNT); + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_aes_seq(sc->ucast_rsc[tid], &aes_seq[tid]); + iwl_mld_le64_to_tkip_seq(sc->ucast_rsc[tid], &tkip_seq[tid]); + } +} + +static void +iwl_mld_convert_mcast_ipn(struct iwl_mld_mcast_key_data *key_status, + const struct iwl_wowlan_igtk_status *key) +{ + struct ieee80211_key_seq *seq = + &key_status->igtk_bigtk.cmac_gmac_seq; + u8 ipn_len = ARRAY_SIZE(key->ipn); + + BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_gmac.pn)); + BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_cmac.pn)); + BUILD_BUG_ON(offsetof(struct ieee80211_key_seq, aes_gmac) != + offsetof(struct ieee80211_key_seq, aes_cmac)); + + /* mac80211 expects big endian for memcmp() to work, convert. + * We don't have the key cipher yet so copy to both to cmac and gmac + */ + for (int i = 0; i < ipn_len; i++) { + seq->aes_gmac.pn[i] = key->ipn[ipn_len - i - 1]; + seq->aes_cmac.pn[i] = key->ipn[ipn_len - i - 1]; + } +} + +static void +iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_igtk_status *igtk) +{ + BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key)); + + if (!igtk->key_len) + return; + + wowlan_status->igtk.len = igtk->key_len; + wowlan_status->igtk.flags = igtk->key_flags; + wowlan_status->igtk.id = + u32_get_bits(igtk->key_flags, + IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + + WOWLAN_IGTK_MIN_INDEX; + + memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key)); + iwl_mld_convert_mcast_ipn(&wowlan_status->igtk, igtk); +} + +static void +iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_igtk_status *bigtk) +{ + int status_idx = 0; + + BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->bigtk) < WOWLAN_BIGTK_KEYS_NUM); + + for (int notif_idx = 0; notif_idx < WOWLAN_BIGTK_KEYS_NUM; + notif_idx++) { + if (!bigtk[notif_idx].key_len) + continue; + + wowlan_status->bigtk[status_idx].len = bigtk[notif_idx].key_len; + wowlan_status->bigtk[status_idx].flags = + bigtk[notif_idx].key_flags; + wowlan_status->bigtk[status_idx].id = + u32_get_bits(bigtk[notif_idx].key_flags, + IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + + WOWLAN_BIGTK_MIN_INDEX; + + BUILD_BUG_ON(sizeof(wowlan_status->bigtk[status_idx].key) < + sizeof(bigtk[notif_idx].key)); + memcpy(wowlan_status->bigtk[status_idx].key, + bigtk[notif_idx].key, sizeof(bigtk[notif_idx].key)); + iwl_mld_convert_mcast_ipn(&wowlan_status->bigtk[status_idx], + &bigtk[notif_idx]); + status_idx++; + } +} + +static bool +iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_rx_packet *pkt) +{ + const struct iwl_wowlan_info_notif *notif = (void *)pkt->data; + u32 expected_len, len = iwl_rx_packet_payload_len(pkt); + + expected_len = sizeof(*notif); + + if (IWL_FW_CHECK(mld, len < expected_len, + "Invalid wowlan_info_notif (expected=%ud got=%ud)\n", + expected_len, len)) + return true; + + if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID, + "Invalid tid_offloaded_tx %d\n", + wowlan_status->tid_offloaded_tx)) + return true; + + iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk, + ¬if->gtk[0].sc); + iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc); + /* only one igtk is passed by FW */ + iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]); + iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk); + + wowlan_status->replay_ctr = le64_to_cpu(notif->replay_ctr); + wowlan_status->pattern_number = le16_to_cpu(notif->pattern_number); + + wowlan_status->tid_offloaded_tx = notif->tid_offloaded_tx; + wowlan_status->last_qos_seq = le16_to_cpu(notif->qos_seq_ctr); + wowlan_status->num_of_gtk_rekeys = + le32_to_cpu(notif->num_of_gtk_rekeys); + wowlan_status->wakeup_reasons = le32_to_cpu(notif->wakeup_reasons); + return false; + /* TODO: mlo_links (task=MLO)*/ +} + +static bool +iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_rx_packet *pkt) +{ + const struct iwl_wowlan_wake_pkt_notif *notif = (void *)pkt->data; + u32 actual_size, len = iwl_rx_packet_payload_len(pkt); + u32 expected_size = le32_to_cpu(notif->wake_packet_length); + + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n", + sizeof(*notif), len)) + return true; + + if (IWL_FW_CHECK(mld, !(wowlan_status->wakeup_reasons & + IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT), + "Got wake packet but wakeup reason is %x\n", + wowlan_status->wakeup_reasons)) + return true; + + actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, + wake_packet); + + /* actual_size got the padding from the notification, remove it. */ + if (expected_size < actual_size) + actual_size = expected_size; + wowlan_status->wake_packet = kmemdup(notif->wake_packet, actual_size, + GFP_ATOMIC); + if (!wowlan_status->wake_packet) + return true; + + wowlan_status->wake_packet_length = expected_size; + wowlan_status->wake_packet_bufsize = actual_size; + + return false; +} + +static void +iwl_mld_set_wake_packet(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_mld_wowlan_status *wowlan_status, + struct cfg80211_wowlan_wakeup *wakeup, + struct sk_buff **_pkt) +{ + int pkt_bufsize = wowlan_status->wake_packet_bufsize; + int expected_pktlen = wowlan_status->wake_packet_length; + const u8 *pktdata = wowlan_status->wake_packet; + const struct ieee80211_hdr *hdr = (const void *)pktdata; + int truncated = expected_pktlen - pkt_bufsize; + + if (ieee80211_is_data(hdr->frame_control)) { + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + int ivlen = 0, icvlen = 4; /* also FCS */ + + struct sk_buff *pkt = alloc_skb(pkt_bufsize, GFP_KERNEL); + *_pkt = pkt; + if (!pkt) + return; + + skb_put_data(pkt, pktdata, hdrlen); + pktdata += hdrlen; + pkt_bufsize -= hdrlen; + + /* if truncated, FCS/ICV is (partially) gone */ + if (truncated >= icvlen) { + truncated -= icvlen; + icvlen = 0; + } else { + icvlen -= truncated; + truncated = 0; + } + + pkt_bufsize -= ivlen + icvlen; + pktdata += ivlen; + + skb_put_data(pkt, pktdata, pkt_bufsize); + + if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) + return; + wakeup->packet = pkt->data; + wakeup->packet_present_len = pkt->len; + wakeup->packet_len = pkt->len - truncated; + wakeup->packet_80211 = false; + } else { + int fcslen = 4; + + if (truncated >= 4) { + truncated -= 4; + fcslen = 0; + } else { + fcslen -= truncated; + truncated = 0; + } + pkt_bufsize -= fcslen; + wakeup->packet = wowlan_status->wake_packet; + wakeup->packet_present_len = pkt_bufsize; + wakeup->packet_len = expected_pktlen - truncated; + wakeup->packet_80211 = true; + } +} + +static void +iwl_mld_report_wowlan_wakeup(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + struct sk_buff *pkt = NULL; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + u32 reasons = wowlan_status->wakeup_reasons; + + if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { + ieee80211_report_wowlan_wakeup(vif, NULL, GFP_KERNEL); + return; + } + + pm_wakeup_event(mld->dev, 0); + + if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) + wakeup.magic_pkt = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) + wakeup.pattern_idx = + wowlan_status->pattern_number; + + if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) + wakeup.disconnect = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) + wakeup.gtk_rekey_failure = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) + wakeup.eap_identity_req = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) + wakeup.four_way_handshake = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) + wakeup.tcp_connlost = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) + wakeup.tcp_nomoretokens = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) + wakeup.tcp_match = true; + + if (reasons & IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC) + wakeup.unprot_deauth_disassoc = true; + + if (wowlan_status->wake_packet) + iwl_mld_set_wake_packet(mld, vif, wowlan_status, &wakeup, &pkt); + + ieee80211_report_wowlan_wakeup(vif, &wakeup, GFP_KERNEL); + kfree_skb(pkt); +} + +static void +iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + int tid; + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_set_key_rx_seq(key, tid, &seq[tid]); +} + +static void +iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key, + struct iwl_mld_mcast_key_data *key_data) +{ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + iwl_mld_set_key_rx_seq_tids(key, + key_data->gtk.aes_seq); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mld_set_key_rx_seq_tids(key, + key_data->gtk.tkip_seq); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + /* igtk/bigtk ciphers*/ + ieee80211_set_key_rx_seq(key, 0, + &key_data->igtk_bigtk.cmac_gmac_seq); + break; + default: + WARN_ON(1); + } +} + +static void +iwl_mld_d3_update_mcast_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status, + struct ieee80211_key_conf *key, + struct iwl_mld_mcast_key_data *key_data) +{ + if (key->keyidx != key_data->id && + (key->keyidx < 4 || key->keyidx > 5)) { + IWL_ERR(mld, + "Unexpected keyId mismatch. Old keyId:%d, New keyId:%d\n", + key->keyidx, key_data->id); + return; + } + + /* All installed keys are sent by the FW, even weren't + * rekeyed during D3. + * We remove an existing key if it has the same index as + * a new key and a rekey has occurred during d3 + */ + if (wowlan_status->num_of_gtk_rekeys && key_data->len) { + if (key->keyidx == 4 || key->keyidx == 5) { + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link; + int link_id = vif->active_links ? + __ffs(vif->active_links) : 0; + + mld_link = iwl_mld_link_dereference_check(mld_vif, + link_id); + if (WARN_ON(!mld_link)) + return; + + if (mld_link->igtk == key) + mld_link->igtk = NULL; + mld->num_igtks--; + } + + ieee80211_remove_key(key); + return; + } + + iwl_mld_set_key_rx_seq(key, key_data); +} + +static void +iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + bool is_tkip) +{ + struct iwl_mld_sta *mld_sta = + iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_ptk_pn *mld_ptk_pn = + wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[key->keyidx]); + + iwl_mld_set_key_rx_seq_tids(key, is_tkip ? + wowlan_status->ptk.tkip_seq : + wowlan_status->ptk.aes_seq); + if (is_tkip) + return; + + if (WARN_ON(!mld_ptk_pn)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + for (int i = 1; i < mld->trans->num_rx_queues; i++) + memcpy(mld_ptk_pn->q[i].pn[tid], + wowlan_status->ptk.aes_seq[tid].ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } +} + +static void +iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld_resume_key_iter_data *data = _data; + struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; + u8 status_idx; + + /* TODO: check key link id (task=MLO) */ + if (data->unhandled_cipher) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* ignore WEP completely, nothing to do */ + return; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status, + sta, key, + key->cipher == + WLAN_CIPHER_SUITE_TKIP); + return; + } + + if (WARN_ON(data->gtk_cipher && + data->gtk_cipher != key->cipher)) + return; + + data->gtk_cipher = key->cipher; + status_idx = key->keyidx == wowlan_status->gtk[1].id; + iwl_mld_d3_update_mcast_key(data->mld, vif, wowlan_status, key, + &wowlan_status->gtk[status_idx]); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (key->keyidx == 4 || key->keyidx == 5) { + if (WARN_ON(data->igtk_cipher && + data->igtk_cipher != key->cipher)) + return; + + data->igtk_cipher = key->cipher; + iwl_mld_d3_update_mcast_key(data->mld, vif, + wowlan_status, + key, &wowlan_status->igtk); + } + if (key->keyidx == 6 || key->keyidx == 7) { + if (WARN_ON(data->bigtk_cipher && + data->bigtk_cipher != key->cipher)) + return; + + data->bigtk_cipher = key->cipher; + status_idx = key->keyidx == wowlan_status->bigtk[1].id; + iwl_mld_d3_update_mcast_key(data->mld, vif, + wowlan_status, key, + &wowlan_status->bigtk[status_idx]); + } + break; + default: + data->unhandled_cipher = true; + return; + } + data->num_keys++; +} + +static bool +iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, + struct iwl_mld *mld, + struct iwl_mld_mcast_key_data *key_data, + struct ieee80211_bss_conf *link_conf, + u32 cipher) +{ + struct ieee80211_key_conf *key_config; + struct { + struct ieee80211_key_conf conf; + u8 key[WOWLAN_KEY_MAX_SIZE]; + } conf = { + .conf.cipher = cipher, + .conf.keyidx = key_data->id, + }; + int link_id = vif->active_links ? __ffs(vif->active_links) : -1; + + BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_128); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_256); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_AES_CMAC); + BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key)); + + if (!key_data->len) + return true; + + switch (cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + conf.conf.keylen = WLAN_KEY_LEN_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + conf.conf.keylen = WLAN_KEY_LEN_GCMP_256; + break; + case WLAN_CIPHER_SUITE_TKIP: + conf.conf.keylen = WLAN_KEY_LEN_TKIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256; + break; + default: + WARN_ON(1); + } + + memcpy(conf.conf.key, key_data->key, conf.conf.keylen); + key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id); + if (IS_ERR(key_config)) + return false; + + iwl_mld_set_key_rx_seq(key_config, key_data); + + /* The FW holds only one igtk so we keep track of the valid one */ + if (key_config->keyidx == 4 || key_config->keyidx == 5) { + struct iwl_mld_link *mld_link = + iwl_mld_link_from_mac80211(link_conf); + mld_link->igtk = key_config; + mld->num_igtks++; + } + return true; +} + +static bool +iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_mld_resume_key_iter_data *key_iter_data, + struct ieee80211_bss_conf *link_conf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++) + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->gtk[i], + link_conf, + key_iter_data->gtk_cipher)) + return false; + + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->igtk, + link_conf, key_iter_data->igtk_cipher)) + return false; + + for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++) + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->bigtk[i], + link_conf, + key_iter_data->bigtk_cipher)) + return false; + + return true; +} + +static bool +iwl_mld_update_sec_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + __be64 replay_ctr = cpu_to_be64(wowlan_status->replay_ctr); + struct iwl_mld_resume_key_iter_data key_iter_data = { + .mld = mld, + .wowlan_status = wowlan_status, + }; + + if (WARN_ON(!link_conf)) + return false; + + ieee80211_iter_keys(mld->hw, vif, iwl_mld_resume_keys_iter, + &key_iter_data); + + if (key_iter_data.unhandled_cipher) + return false; + + IWL_DEBUG_WOWLAN(mld, + "Number of installed keys: %d, Number of rekeys: %d\n", + key_iter_data.num_keys, + wowlan_status->num_of_gtk_rekeys); + + if (!key_iter_data.num_keys || !wowlan_status->num_of_gtk_rekeys) + return true; + + iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data, + link_conf); + + ieee80211_gtk_rekey_notify(vif, link_conf->bssid, + (void *)&replay_ctr, GFP_KERNEL); + /* TODO: MLO rekey (task=MLO) */ + return true; +} + +static bool +iwl_mld_process_wowlan_status(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_mld_txq *mld_txq; + + iwl_mld_report_wowlan_wakeup(mld, vif, wowlan_status); + + if (WARN_ON(!ap_sta)) + return false; + + mld_txq = + iwl_mld_txq_from_mac80211(ap_sta->txq[wowlan_status->tid_offloaded_tx]); + + /* Update the pointers of the Tx queue that may have moved during + * suspend if the firmware sent frames. + * The firmware stores last-used value, we store next value. + */ + WARN_ON(!mld_txq->status.allocated); + iwl_trans_set_q_ptrs(mld->trans, mld_txq->fw_id, + (wowlan_status->last_qos_seq + + 0x10) >> 4); + + if (!iwl_mld_update_sec_keys(mld, vif, wowlan_status)) + return false; + + if (wowlan_status->wakeup_reasons & + (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) + return false; + + return true; +} + +static bool +iwl_mld_netdetect_match_info_handler(struct iwl_mld *mld, + struct iwl_mld_resume_data *resume_data, + struct iwl_rx_packet *pkt) +{ + struct iwl_mld_netdetect_res *results = resume_data->netdetect_res; + const struct iwl_scan_offload_match_info *notif = (void *)pkt->data; + u32 len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, !mld->netdetect, + "Got scan match info notif when mld->netdetect==%d\n", + mld->netdetect)) + return true; + + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid scan offload match notif of length: %d\n", + len)) + return true; + + if (IWL_FW_CHECK(mld, resume_data->wowlan_status->wakeup_reasons != + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, + "Ignore scan match info: unexpected wakeup reason (expected=0x%x got=0x%x)\n", + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, + resume_data->wowlan_status->wakeup_reasons)) + return true; + + results->matched_profiles = le32_to_cpu(notif->matched_profiles); + IWL_DEBUG_WOWLAN(mld, "number of matched profiles=%u\n", + results->matched_profiles); + + if (results->matched_profiles) + memcpy(results->matches, notif->matches, + NETDETECT_QUERY_BUF_LEN); + + /* No scan should be active at this point */ + mld->scan.status = 0; + memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); + return false; +} + +static void +iwl_mld_set_netdetect_info(struct iwl_mld *mld, + const struct cfg80211_sched_scan_request *netdetect_cfg, + struct cfg80211_wowlan_nd_info *netdetect_info, + struct iwl_mld_netdetect_res *netdetect_res, + unsigned long matched_profiles) +{ + int i; + + for_each_set_bit(i, &matched_profiles, netdetect_cfg->n_match_sets) { + struct cfg80211_wowlan_nd_match *match; + int idx, j, n_channels = 0; + struct iwl_scan_offload_profile_match *matches = + (void *)netdetect_res->matches; + + for (int k = 0; k < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; k++) + n_channels += + hweight8(matches[i].matching_channels[k]); + match = kzalloc(struct_size(match, channels, n_channels), + GFP_KERNEL); + if (!match) + return; + + netdetect_info->matches[netdetect_info->n_matches++] = match; + + /* We inverted the order of the SSIDs in the scan + * request, so invert the index here. + */ + idx = netdetect_cfg->n_match_sets - i - 1; + match->ssid.ssid_len = + netdetect_cfg->match_sets[idx].ssid.ssid_len; + memcpy(match->ssid.ssid, + netdetect_cfg->match_sets[idx].ssid.ssid, + match->ssid.ssid_len); + + if (netdetect_cfg->n_channels < n_channels) + continue; + + for_each_set_bit(j, + (unsigned long *)&matches[i].matching_channels[0], + sizeof(matches[i].matching_channels)) + match->channels[match->n_channels++] = + netdetect_cfg->channels[j]->center_freq; + } +} + +static void +iwl_mld_process_netdetect_res(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_resume_data *resume_data) +{ + struct cfg80211_wowlan_nd_info *netdetect_info = NULL; + const struct cfg80211_sched_scan_request *netdetect_cfg; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; + unsigned long matched_profiles; + u32 wakeup_reasons; + int n_matches; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld->wiphy->wowlan_config || + !mld->wiphy->wowlan_config->nd_config)) { + IWL_DEBUG_WOWLAN(mld, + "Netdetect isn't configured on resume flow\n"); + goto out; + } + + netdetect_cfg = mld->wiphy->wowlan_config->nd_config; + wakeup_reasons = resume_data->wowlan_status->wakeup_reasons; + + if (wakeup_reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (wakeup_reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) + goto out; + + if (!resume_data->netdetect_res->matched_profiles) { + IWL_DEBUG_WOWLAN(mld, + "Netdetect results aren't valid\n"); + wakeup_report = NULL; + goto out; + } + + matched_profiles = resume_data->netdetect_res->matched_profiles; + if (!netdetect_cfg->n_match_sets) { + IWL_DEBUG_WOWLAN(mld, + "No netdetect match sets are configured\n"); + goto out; + } + n_matches = hweight_long(matched_profiles); + netdetect_info = kzalloc(struct_size(netdetect_info, matches, + n_matches), GFP_KERNEL); + if (netdetect_info) + iwl_mld_set_netdetect_info(mld, netdetect_cfg, netdetect_info, + resume_data->netdetect_res, + matched_profiles); + + wakeup.net_detect = netdetect_info; + out: + ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); + if (netdetect_info) { + for (int i = 0; i < netdetect_info->n_matches; i++) + kfree(netdetect_info->matches[i]); + kfree(netdetect_info); + } +} + +static bool iwl_mld_handle_d3_notif(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_resume_data *resume_data = data; + struct iwl_mld *mld = + container_of(notif_wait, struct iwl_mld, notif_wait); + + switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { + case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { + if (resume_data->notifs_received & IWL_D3_NOTIF_WOWLAN_INFO) { + IWL_DEBUG_WOWLAN(mld, + "got additional wowlan_info notif\n"); + break; + } + resume_data->notif_handling_err = + iwl_mld_handle_wowlan_info_notif(mld, + resume_data->wowlan_status, + pkt); + resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_INFO; + + if (resume_data->wowlan_status->wakeup_reasons & + IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT) + resume_data->notifs_expected |= + IWL_D3_NOTIF_WOWLAN_WAKE_PKT; + break; + } + case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION): { + if (resume_data->notifs_received & + IWL_D3_NOTIF_WOWLAN_WAKE_PKT) { + /* We shouldn't get two wake packet notifications */ + IWL_DEBUG_WOWLAN(mld, + "Got additional wowlan wake packet notification\n"); + break; + } + resume_data->notif_handling_err = + iwl_mld_handle_wake_pkt_notif(mld, + resume_data->wowlan_status, + pkt); + resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_WAKE_PKT; + break; + } + case WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF): { + if (resume_data->notifs_received & IWL_D3_ND_MATCH_INFO) { + IWL_ERR(mld, + "Got additional netdetect match info\n"); + break; + } + + resume_data->notif_handling_err = + iwl_mld_netdetect_match_info_handler(mld, resume_data, + pkt); + resume_data->notifs_received |= IWL_D3_ND_MATCH_INFO; + break; + } + case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { + struct iwl_d3_end_notif *notif = (void *)pkt->data; + + resume_data->d3_end_flags = le32_to_cpu(notif->flags); + resume_data->notifs_received |= IWL_D3_NOTIF_D3_END_NOTIF; + break; + } + default: + WARN_ON(1); + } + + return resume_data->notifs_received == resume_data->notifs_expected; +} + +#define IWL_MLD_D3_NOTIF_TIMEOUT (HZ / 3) + +static int iwl_mld_wait_d3_notif(struct iwl_mld *mld, + struct iwl_mld_resume_data *resume_data, + bool with_wowlan) +{ + static const u16 wowlan_resume_notif[] = { + WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION), + WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION), + WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF), + WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) + }; + static const u16 d3_resume_notif[] = { + WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) + }; + struct iwl_notification_wait wait_d3_notif; + enum iwl_d3_status d3_status; + int ret; + + if (with_wowlan) + iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, + wowlan_resume_notif, + ARRAY_SIZE(wowlan_resume_notif), + iwl_mld_handle_d3_notif, + resume_data); + else + iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, + d3_resume_notif, + ARRAY_SIZE(d3_resume_notif), + iwl_mld_handle_d3_notif, + resume_data); + + ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false); + if (ret || d3_status != IWL_D3_STATUS_ALIVE) { + if (d3_status != IWL_D3_STATUS_ALIVE) { + IWL_INFO(mld, "Device was reset during suspend\n"); + ret = -ENOENT; + } else { + IWL_ERR(mld, "Transport resume failed\n"); + } + iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); + return ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &wait_d3_notif, + IWL_MLD_D3_NOTIF_TIMEOUT); + if (ret) + IWL_ERR(mld, "Couldn't get the d3 notif %d\n", ret); + + if (resume_data->notif_handling_err) + ret = -EIO; + + return ret; +} + +int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) +{ + struct iwl_d3_manager_config d3_cfg_cmd_data = {}; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n"); + + iwl_mld_low_latency_stop(mld); + + /* This will happen if iwl_mld_supsend failed with FW error */ + if (mld->trans->state == IWL_TRANS_NO_FW && + test_bit(STATUS_FW_ERROR, &mld->trans->status)) + return -ENODEV; + + ret = iwl_mld_update_device_power(mld, true); + if (ret) { + IWL_ERR(mld, + "d3 suspend: couldn't send power_device %d\n", ret); + goto out; + } + + ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD, + &d3_cfg_cmd_data); + if (ret) { + IWL_ERR(mld, + "d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret); + goto out; + } + + ret = iwl_trans_d3_suspend(mld->trans, false, false); + if (ret) { + IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret); + } else { + mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + mld->fw_status.in_d3 = true; + } + + out: + if (ret) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + + return ret; +} + +int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) +{ + struct iwl_mld_resume_data resume_data = { + .notifs_expected = + IWL_D3_NOTIF_D3_END_NOTIF, + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n"); + + mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + mld->fw_status.in_d3 = false; + iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); + + if (iwl_mld_fw_needs_restart(mld, NULL)) + ret = -ENODEV; + else + ret = iwl_mld_wait_d3_notif(mld, &resume_data, false); + + if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)) + return -ENODEV; + + if (ret) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + return ret; + } + iwl_mld_low_latency_restart(mld); + + return iwl_mld_update_device_power(mld, false); +} + +static void +iwl_mld_aes_seq_to_le64_pn(struct ieee80211_key_conf *key, + __le64 *key_rsc) +{ + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + struct ieee80211_key_seq seq; + u8 *pn = key->cipher == WLAN_CIPHER_SUITE_CCMP ? seq.ccmp.pn : + seq.gcmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + key_rsc[i] = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } +} + +static void +iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, __le64 *key_rsc) +{ + struct iwl_mld_sta *mld_sta = + iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_ptk_pn *mld_ptk_pn; + + if (WARN_ON(key->keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return; + + mld_ptk_pn = wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[key->keyidx]); + if (WARN_ON(!mld_ptk_pn)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_key_seq seq; + u8 *max_pn = seq.ccmp.pn; + + /* get the PN from mac80211, used on the default queue */ + ieee80211_get_key_rx_seq(key, tid, &seq); + + /* and use the internal data for all queues */ + for (int que = 1; que < mld->trans->num_rx_queues; que++) { + u8 *cur_pn = mld_ptk_pn->q[que].pn[tid]; + + if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0) + max_pn = cur_pn; + } + key_rsc[tid] = cpu_to_le64((u64)max_pn[5] | + ((u64)max_pn[4] << 8) | + ((u64)max_pn[3] << 16) | + ((u64)max_pn[2] << 24) | + ((u64)max_pn[1] << 32) | + ((u64)max_pn[0] << 40)); + } +} + +static void +iwl_mld_suspend_convert_tkip_ipn(struct ieee80211_key_conf *key, + __le64 *rsc) +{ + struct ieee80211_key_seq seq; + + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + rsc[i] = + cpu_to_le64(((u64)seq.tkip.iv32 << 16) | + seq.tkip.iv16); + } +} + +static void +iwl_mld_suspend_key_data_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_suspend_key_iter_data *data = _data; + __le64 *key_rsc; + __le32 cipher = 0; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + cipher = cpu_to_le32(STA_KEY_FLG_CCM); + fallthrough; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + fallthrough; + case WLAN_CIPHER_SUITE_TKIP: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_TKIP); + if (sta) { + key_rsc = data->rsc->ucast_rsc; + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); + else + iwl_mld_suspend_set_ucast_pn(mld, sta, key, + key_rsc); + + data->have_rsc = true; + return; + } + /* We're iterating from old to new, there're 4 possible + * gtk ids, and only the last two keys matter + */ + if (WARN_ON(data->gtks >= + ARRAY_SIZE(data->found_gtk_idx))) + return; + + if (WARN_ON(key->keyidx >= + ARRAY_SIZE(data->rsc->mcast_key_id_map))) + return; + data->gtk_cipher = cipher; + data->found_gtk_idx[data->gtks] = key->keyidx; + key_rsc = data->rsc->mcast_rsc[data->gtks % 2]; + data->rsc->mcast_key_id_map[key->keyidx] = + data->gtks % 2; + + if (data->gtks >= 2) { + int prev = data->gtks % 2; + int prev_idx = data->found_gtk_idx[prev]; + + data->rsc->mcast_key_id_map[prev_idx] = + IWL_MCAST_KEY_MAP_INVALID; + } + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); + else + iwl_mld_aes_seq_to_le64_pn(key, key_rsc); + + data->gtks++; + data->have_rsc = true; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + fallthrough; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_CCM); + if (key->keyidx == 4 || key->keyidx == 5) + data->igtk_cipher = cipher; + + if (key->keyidx == 6 || key->keyidx == 7) + data->bigtk_cipher = cipher; + + break; + } +} + +static int +iwl_mld_send_kek_kck_cmd(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif, + struct iwl_mld_suspend_key_iter_data data, + int ap_sta_id) +{ + struct iwl_wowlan_kek_kck_material_cmd_v4 kek_kck_cmd = {}; + struct iwl_mld_rekey_data *rekey_data = + &mld_vif->wowlan_data.rekey_data; + + memcpy(kek_kck_cmd.kck, rekey_data->kck, + rekey_data->kck_len); + kek_kck_cmd.kck_len = cpu_to_le16(rekey_data->kck_len); + memcpy(kek_kck_cmd.kek, rekey_data->kek, + rekey_data->kek_len); + kek_kck_cmd.kek_len = cpu_to_le16(rekey_data->kek_len); + kek_kck_cmd.replay_ctr = rekey_data->replay_ctr; + kek_kck_cmd.akm = cpu_to_le32(rekey_data->akm); + kek_kck_cmd.sta_id = cpu_to_le32(ap_sta_id); + kek_kck_cmd.gtk_cipher = data.gtk_cipher; + kek_kck_cmd.igtk_cipher = data.igtk_cipher; + kek_kck_cmd.bigtk_cipher = data.bigtk_cipher; + + IWL_DEBUG_WOWLAN(mld, "setting akm %d\n", + rekey_data->akm); + + return iwl_mld_send_cmd_pdu(mld, WOWLAN_KEK_KCK_MATERIAL, + &kek_kck_cmd); +} + +static int +iwl_mld_suspend_send_security_cmds(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_vif *mld_vif, + int ap_sta_id) +{ + struct iwl_mld_suspend_key_iter_data data = {}; + int ret; + + data.rsc = kzalloc(sizeof(*data.rsc), GFP_KERNEL); + if (!data.rsc) + return -ENOMEM; + + memset(data.rsc->mcast_key_id_map, IWL_MCAST_KEY_MAP_INVALID, + ARRAY_SIZE(data.rsc->mcast_key_id_map)); + + data.rsc->sta_id = cpu_to_le32(ap_sta_id); + ieee80211_iter_keys(mld->hw, vif, + iwl_mld_suspend_key_data_iter, + &data); + + if (data.have_rsc) + ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_TSC_RSC_PARAM, + data.rsc); + else + ret = 0; + + if (!ret && mld_vif->wowlan_data.rekey_data.valid) + ret = iwl_mld_send_kek_kck_cmd(mld, mld_vif, data, ap_sta_id); + + kfree(data.rsc); + + return ret; +} + +static void +iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan, + struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct ieee80211_sta *ap_sta) +{ + wowlan_config_cmd->is_11n_connection = + ap_sta->deflink.ht_cap.ht_supported; + wowlan_config_cmd->flags = ENABLE_L3_FILTERING | + ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; + + if (ap_sta->mfp) + wowlan_config_cmd->flags |= IS_11W_ASSOC; + + if (wowlan->disconnect) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); + + if (wowlan->any) { + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE | + IWL_WOWLAN_WAKEUP_RX_FRAME | + IWL_WOWLAN_WAKEUP_BCN_FILTERING); + } +} + +static int iwl_mld_send_patterns(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan, + int ap_sta_id) +{ + struct iwl_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int ret; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); + + pattern_cmd = kzalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = wowlan->n_patterns; + pattern_cmd->sta_id = ap_sta_id; + + for (int i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + pattern_cmd->patterns[i].pattern_type = + WOWLAN_PATTERN_TYPE_BITMASK; + + memcpy(&pattern_cmd->patterns[i].u.bitmask.mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].u.bitmask.pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].u.bitmask.mask_size = mask_len; + pattern_cmd->patterns[i].u.bitmask.pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + ret = iwl_mld_send_cmd(mld, &cmd); + kfree(pattern_cmd); + return ret; +} + +static int +iwl_mld_send_proto_offload(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u8 ap_sta_id) +{ + struct iwl_proto_offload_cmd_v4 *cmd __free(kfree); + struct iwl_host_cmd hcmd = { + .id = PROT_OFFLOAD_CONFIG_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(*cmd), + }; + u32 enabled = 0; + + cmd = kzalloc(hcmd.len[0], GFP_KERNEL); + +#if IS_ENABLED(CONFIG_IPV6) + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + struct iwl_ns_config *nsc; + struct iwl_targ_addr *addrs; + int n_nsc, n_addrs; + int i, c; + int num_skipped = 0; + + nsc = cmd->ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; + addrs = cmd->targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; + + /* For each address we have (and that will fit) fill a target + * address struct and combine for NS offload structs with the + * solicited node addresses. + */ + for (i = 0, c = 0; + i < wowlan_data->num_target_ipv6_addrs && + i < n_addrs && c < n_nsc; i++) { + int j; + struct in6_addr solicited_addr; + + /* Because ns is offloaded skip tentative address to avoid + * violating RFC4862. + */ + if (test_bit(i, wowlan_data->tentative_addrs)) { + num_skipped++; + continue; + } + + addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i], + &solicited_addr); + for (j = 0; j < c; j++) + if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, + &solicited_addr) == 0) + break; + if (j == c) + c++; + addrs[i].addr = wowlan_data->target_ipv6_addrs[i]; + addrs[i].config_num = cpu_to_le32(j); + nsc[j].dest_ipv6_addr = solicited_addr; + memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); + } + + if (wowlan_data->num_target_ipv6_addrs - num_skipped) + enabled |= IWL_D3_PROTO_IPV6_VALID; + + cmd->num_valid_ipv6_addrs = cpu_to_le32(i - num_skipped); + if (enabled & IWL_D3_PROTO_IPV6_VALID) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; +#endif + + if (vif->cfg.arp_addr_cnt) { + enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID; + cmd->common.host_ipv4_addr = vif->cfg.arp_addr_list[0]; + ether_addr_copy(cmd->common.arp_mac_addr, vif->addr); + } + + enabled |= IWL_D3_PROTO_OFFLOAD_BTM; + cmd->common.enabled = cpu_to_le32(enabled); + cmd->sta_id = cpu_to_le32(ap_sta_id); + hcmd.data[0] = cmd; + return iwl_mld_send_cmd(mld, &hcmd); +} + +static int +iwl_mld_wowlan_config(struct iwl_mld *mld, struct ieee80211_vif *bss_vif, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_wowlan_config_cmd wowlan_config_cmd = { + .offloading_tid = IWL_WOWLAN_OFFLOAD_TID, + }; + u32 sta_id_mask; + int ap_sta_id, ret; + int link_id = iwl_mld_get_primary_link(bss_vif); + struct ieee80211_bss_conf *link_conf; + + ret = iwl_mld_block_emlsr_sync(mld, bss_vif, + IWL_MLD_EMLSR_BLOCKED_WOWLAN, link_id); + if (ret) + return ret; + + link_conf = link_conf_dereference_protected(bss_vif, link_id); + + if (WARN_ON(!ap_sta || !link_conf)) + return -EINVAL; + + sta_id_mask = iwl_mld_fw_sta_id_mask(mld, ap_sta); + if (WARN_ON(hweight32(sta_id_mask) != 1)) + return -EINVAL; + + ap_sta_id = __ffs(sta_id_mask); + wowlan_config_cmd.sta_id = ap_sta_id; + + ret = iwl_mld_ensure_queue(mld, + ap_sta->txq[wowlan_config_cmd.offloading_tid]); + if (ret) + return ret; + + iwl_mld_set_wowlan_config_cmd(mld, wowlan, + &wowlan_config_cmd, ap_sta); + ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_CONFIGURATION, + &wowlan_config_cmd); + if (ret) + return ret; + + ret = iwl_mld_suspend_send_security_cmds(mld, bss_vif, mld_vif, + ap_sta_id); + if (ret) + return ret; + + ret = iwl_mld_send_patterns(mld, wowlan, ap_sta_id); + if (ret) + return ret; + + ret = iwl_mld_send_proto_offload(mld, bss_vif, ap_sta_id); + if (ret) + return ret; + + iwl_mld_enable_beacon_filter(mld, link_conf, true); + return iwl_mld_update_mac_power(mld, bss_vif, true); +} + +int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan) +{ + struct ieee80211_vif *bss_vif; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!wowlan)) + return 1; + + IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n"); + + bss_vif = iwl_mld_get_bss_vif(mld); + if (WARN_ON(!bss_vif)) + return 1; + + if (!bss_vif->cfg.assoc) { + int ret; + /* If we're not associated, this must be netdetect */ + if (WARN_ON(!wowlan->nd_config)) + return 1; + + ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan); + if (!ret) + mld->netdetect = true; + + return ret; + } + + return iwl_mld_wowlan_config(mld, bss_vif, wowlan); +} + +/* Returns 0 on success, 1 if an error occurred in firmware during d3, + * A negative value is expected only in unrecovreable cases. + */ +int iwl_mld_wowlan_resume(struct iwl_mld *mld) +{ + struct ieee80211_vif *bss_vif; + struct ieee80211_bss_conf *link_conf; + struct iwl_mld_netdetect_res netdetect_res; + struct iwl_mld_resume_data resume_data = { + .notifs_expected = + IWL_D3_NOTIF_WOWLAN_INFO | + IWL_D3_NOTIF_D3_END_NOTIF, + .netdetect_res = &netdetect_res, + }; + int link_id; + int ret; + bool fw_err = false; + bool keep_connection; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n"); + + mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + if (!mld->fw_status.in_d3) { + IWL_DEBUG_WOWLAN(mld, + "Device_powered_off() was called during wowlan\n"); + goto err; + } + + mld->fw_status.in_d3 = false; + mld->scan.last_start_time_jiffies = jiffies; + + bss_vif = iwl_mld_get_bss_vif(mld); + if (WARN_ON(!bss_vif)) + goto err; + + /* We can't have several links upon wowlan entry, + * this is enforced in the suspend flow. + */ + WARN_ON(hweight16(bss_vif->active_links) > 1); + link_id = bss_vif->active_links ? __ffs(bss_vif->active_links) : 0; + link_conf = link_conf_dereference_protected(bss_vif, link_id); + + if (WARN_ON(!link_conf)) + goto err; + + iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); + + if (iwl_mld_fw_needs_restart(mld, bss_vif)) { + fw_err = true; + goto err; + } + + resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status), + GFP_KERNEL); + if (!resume_data.wowlan_status) + return -1; + + if (mld->netdetect) + resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO; + + ret = iwl_mld_wait_d3_notif(mld, &resume_data, true); + if (ret) { + IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret); + fw_err = true; + goto err; + } + + if (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE) { + mld->fw_status.in_hw_restart = true; + goto process_wakeup_results; + } + + iwl_mld_update_changed_regdomain(mld); + iwl_mld_update_mac_power(mld, bss_vif, false); + iwl_mld_enable_beacon_filter(mld, link_conf, false); + iwl_mld_update_device_power(mld, false); + + if (mld->netdetect) + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_NETDETECT, false); + + process_wakeup_results: + if (mld->netdetect) { + iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data); + mld->netdetect = false; + } else { + keep_connection = + iwl_mld_process_wowlan_status(mld, bss_vif, + resume_data.wowlan_status); + + /* EMLSR state will be cleared if the connection is not kept */ + if (keep_connection) + iwl_mld_unblock_emlsr(mld, bss_vif, + IWL_MLD_EMLSR_BLOCKED_WOWLAN); + } + + if (!mld->netdetect && !keep_connection) + ieee80211_resume_disconnect(bss_vif); + + goto out; + + err: + if (fw_err) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + + mld->fw_status.in_hw_restart = true; + ret = 1; + out: + if (resume_data.wowlan_status) { + kfree(resume_data.wowlan_status->wake_packet); + kfree(resume_data.wowlan_status); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.h b/drivers/net/wireless/intel/iwlwifi/mld/d3.h new file mode 100644 index 000000000000..618d6fb3c796 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_d3_h__ +#define __iwl_mld_d3_h__ + +#include "fw/api/d3.h" + +struct iwl_mld_rekey_data { + bool valid; + u8 kck[NL80211_KCK_EXT_LEN]; + u8 kek[NL80211_KEK_EXT_LEN]; + size_t kck_len; + size_t kek_len; + __le64 replay_ctr; + u32 akm; +}; + +/** + * struct iwl_mld_wowlan_data - data used by the wowlan suspend flow + * + * @target_ipv6_addrs: IPv6 addresses on this interface for offload + * @tentative_addrs: bitmap of tentative IPv6 addresses in @target_ipv6_addrs + * @num_target_ipv6_addrs: number of @target_ipv6_addrs + * @rekey_data: security key data used for rekeying during D3 + */ +struct iwl_mld_wowlan_data { +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; + unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)]; + int num_target_ipv6_addrs; +#endif + struct iwl_mld_rekey_data rekey_data; +}; + +int iwl_mld_no_wowlan_resume(struct iwl_mld *mld); +int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld); +int iwl_mld_wowlan_suspend(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan); +int iwl_mld_wowlan_resume(struct iwl_mld *mld); +void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); +#if IS_ENABLED(CONFIG_IPV6) +void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev); +#endif + +#endif /* __iwl_mld_d3_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c new file mode 100644 index 000000000000..c759c5c68dc0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "debugfs.h" +#include "iwl-io.h" +#include "hcmd.h" +#include "iface.h" +#include "sta.h" +#include "tlc.h" +#include "power.h" +#include "notif.h" +#include "ap.h" +#include "iwl-utils.h" +#ifdef CONFIG_THERMAL +#include "thermal.h" +#endif + +#include "fw/api/rs.h" +#include "fw/api/dhc.h" +#include "fw/api/rfi.h" + +#define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \ + _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld) + +#define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, mld, \ + &iwl_dbgfs_##name##_ops) +#define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \ + MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld) +{ +#ifdef CONFIG_PM_SLEEP + return !mld->fw_status.running || mld->fw_status.in_d3; +#else + return !mld->fw_status.running; +#endif /* CONFIG_PM_SLEEP */ +} + +static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + /* If the firmware is not running, silently succeed since there is + * no data to clear. + */ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return 0; + + iwl_fw_dbg_clear_monitor_buf(&mld->fwrt); + + return count; +} + +static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + IWL_ERR(mld, "Triggering an NMI from debugfs\n"); + + if (count == 6 && !strcmp(buf, "nolog\n")) + mld->fw_status.do_not_dump_once = true; + + iwl_force_nmi(mld->trans); + + return count; +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + int __maybe_unused ret; + + if (!iwlwifi_mod_params.fw_restart) + return -EPERM; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (count == 6 && !strcmp(buf, "nolog\n")) { + mld->fw_status.do_not_dump_once = true; + set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status); + } + + /* take the return value to make compiler happy - it will + * fail anyway + */ + ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR)); + + return count; +} + +static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count; +} + +struct iwl_mld_sniffer_apply { + struct iwl_mld *mld; + const u8 *bssid; + u16 aid; +}; + +static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_sniffer_apply *apply = data; + + apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid); + memcpy(apply->mld->monitor.cur_bssid, apply->bssid, + sizeof(apply->mld->monitor.cur_bssid)); + + return true; +} + +static ssize_t +iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + struct iwl_notification_wait wait; + struct iwl_he_monitor_cmd he_mon_cmd = {}; + struct iwl_mld_sniffer_apply apply = { + .mld = mld, + }; + u16 wait_cmds[] = { + WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), + }; + u32 aid; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (!mld->monitor.on) + return -ENODEV; + + ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid, + &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1], + &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3], + &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]); + if (ret != 7) + return -EINVAL; + + he_mon_cmd.aid = cpu_to_le16(aid); + + apply.aid = aid; + apply.bssid = (void *)he_mon_cmd.bssid; + + /* Use the notification waiter to get our function triggered + * in sequence with other RX. This ensures that frames we get + * on the RX queue _before_ the new configuration is applied + * still have mld->cur_aid pointing to the old AID, and that + * frames on the RX queue _after_ the firmware processed the + * new configuration (and sent the response, synchronously) + * get mld->cur_aid correctly set to the new AID. + */ + iwl_init_notification_wait(&mld->notif_wait, &wait, + wait_cmds, ARRAY_SIZE(wait_cmds), + iwl_mld_sniffer_apply, &apply); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + HE_AIR_SNIFFER_CONFIG_CMD), + &he_mon_cmd); + + /* no need to really wait, we already did anyway */ + iwl_remove_notification(&mld->notif_wait, &wait); + + return ret ?: count; +} + +static ssize_t +iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, size_t count, + char *buf) +{ + return scnprintf(buf, count, + "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", + le16_to_cpu(mld->monitor.cur_aid), + mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1], + mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3], + mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]); +} + +/* The size computation is as follows: + * each number needs at most 3 characters, number of rows is the size of + * the table; So, need 5 chars for the "freq: " part and each tuple afterwards + * needs 6 characters for numbers and 5 for the punctuation around. 32 bytes + * for feature support message. + */ +#define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\ + (5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\ + (6 + 5)) + 32) +#define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\ + (5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\ + (6 + 5)) + 32) +#define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE + +/* Extra 32 for "DDR and DLVR table" message */ +#define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\ + IWL_RFI_DESENSE_BUF_SIZE + 32) + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10); +WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8); + +static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld, + size_t count, u8 *buf) +{ + int err; + u32 value; + + err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value); + if (err) + return err; + + return scnprintf(buf, count, "0x%08x\n", value); +} + +MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64); + +static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + struct iwl_op_mode *opmode = container_of((void *)mld, + struct iwl_op_mode, + op_mode_specific); + struct iwl_rx_cmd_buffer rxb = {}; + struct iwl_rx_packet *pkt; + int n_bytes = count / 2; + int ret = -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + rxb._page = alloc_pages(GFP_KERNEL, 0); + if (!rxb._page) + return -ENOMEM; + pkt = rxb_addr(&rxb); + + ret = hex2bin(page_address(rxb._page), buf, n_bytes); + if (ret) + goto out; + + /* avoid invalid memory access and malformed packet */ + if (n_bytes < sizeof(*pkt) || + n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt)) + goto out; + + local_bh_disable(); + iwl_mld_rx(opmode, NULL, &rxb); + local_bh_enable(); + ret = 0; + +out: + iwl_free_rxb(&rxb); + + return ret ?: count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512); + +#ifdef CONFIG_THERMAL + +static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_STOP) ? : count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8); + +static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_START) ? : count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8); + +#endif /* CONFIG_THERMAL */ + +void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) +{ + /* Add debugfs files here */ + + MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400); + MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600); + MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200); +#ifdef CONFIG_THERMAL + MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200); +#endif + MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); + + /* Create a symlink with mac80211. It will be removed when mac80211 + * exits (before the opmode exits which removes the target.) + */ + if (!IS_ERR(debugfs_dir)) { + char buf[100]; + + snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent); + debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir, + buf); + } +} + +#define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif) + +#define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \ + +#define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, vif, \ + &iwl_dbgfs_vif_##name##_ops) +#define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \ + VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct ieee80211_bss_conf *link_conf; + int val; + + if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf + 24, "%d", &val) != 1) + return -EINVAL; + } else { + return -EINVAL; + } + + if (val != 0 && val != 1) + return -EINVAL; + + link_conf = link_conf_dereference_protected(vif, link_id); + if (WARN_ON(!link_conf)) + return -ENODEV; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + mld_vif->disable_bf = !val; + + if (val) + return iwl_mld_enable_beacon_filter(mld, link_conf, + false) ?: count; + else + return iwl_mld_disable_beacon_filter(mld, vif) ?: count; +} + +static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld, + char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int val; + + if (!strncmp("use_ps_poll=", buf, 12)) { + if (sscanf(buf + 12, "%d", &val) != 1) + return -EINVAL; + } else { + return -EINVAL; + } + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + mld_vif->use_ps_poll = val; + + return iwl_mld_update_mac_power(mld, vif, false) ?: count; +} + +static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld, + char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + u8 value; + int ret; + + ret = kstrtou8(buf, 0, &value); + if (ret) + return ret; + + if (value > 1) + return -EINVAL; + + iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS); + + return count; +} + +static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif, + size_t count, char *buf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n"; + u8 ll_causes; + + if (WARN_ON(count < sizeof(format))) + return -EINVAL; + + ll_causes = READ_ONCE(mld_vif->low_latency_causes); + + /* all values in format are boolean so the size of format is enough + * for holding the result string + */ + return scnprintf(buf, count, format, + !!(ll_causes & LOW_LATENCY_TRAFFIC), + !!(ll_causes & LOW_LATENCY_DEBUGFS), + !!(ll_causes & LOW_LATENCY_VIF_TYPE), + !!(ll_causes)); +} + +VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32); +VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32); +VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45); + +static int +_iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif, + char *bin, ssize_t len, + bool restore) +{ + struct iwl_mld_vif *mld_vif; + struct iwl_mld_link *mld_link; + struct iwl_mac_beacon_cmd beacon_cmd = {}; + int n_bytes = len / 2; + + /* Element len should be represented by u8 */ + if (n_bytes >= U8_MAX) + return -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (!vif) + return -EINVAL; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + mld_vif->beacon_inject_active = true; + mld->hw->extra_beacon_tailroom = n_bytes; + + for_each_mld_vif_valid_link(mld_vif, mld_link) { + u32 offset; + struct ieee80211_tx_info *info; + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + struct ieee80211_chanctx_conf *ctx = + wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + struct sk_buff *beacon = + ieee80211_beacon_get_template(mld->hw, vif, + NULL, link_id); + + if (!beacon) + return -EINVAL; + + if (!restore && (WARN_ON(!n_bytes || !bin) || + hex2bin(skb_put_zero(beacon, n_bytes), + bin, n_bytes))) { + dev_kfree_skb(beacon); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(beacon); + + beacon_cmd.flags = + cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif, + link_conf, + ctx->def.chan->band)); + beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); + beacon_cmd.link_id = + cpu_to_le32(mld_link->fw_id); + + iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx, + beacon->data, beacon->len); + + offset = iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len); + + beacon_cmd.btwt_offset = cpu_to_le32(offset); + + iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd); + dev_kfree_skb(beacon); + } + + if (restore) + mld_vif->beacon_inject_active = false; + + return 0; +} + +static ssize_t +iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld, + char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf, + count, false); + + mld->hw->extra_beacon_tailroom = 0; + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512); + +static ssize_t +iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld, + char *buf, + size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL, + 0, true); + + mld->hw->extra_beacon_tailroom = 0; + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512); + +static ssize_t +iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count, + void *data) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND), + }; + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_dhc_twt_operation *dhc_twt_cmd; + struct iwl_dhc_cmd *cmd __free(kfree); + u64 target_wake_time; + u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration; + u8 trigger, flow_type, flow_id, protection, tenth_param; + u8 twt_request = 1, broadcast = 0; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu", + &twt_operation, &target_wake_time, &interval_exp, + &interval_mantissa, &min_wake_duration, &trigger, + &flow_type, &flow_id, &protection, &tenth_param); + + /* the new twt_request parameter is optional for station */ + if ((ret != 9 && ret != 10) || + (ret == 10 && vif->type != NL80211_IFTYPE_STATION && + tenth_param == 1)) + return -EINVAL; + + /* The 10th parameter: + * In STA mode - the TWT type (broadcast or individual) + * In AP mode - the role (0 responder, 2 unsolicited) + */ + if (ret == 10) { + if (vif->type == NL80211_IFTYPE_STATION) + broadcast = tenth_param; + else + twt_request = tenth_param; + } + + cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + dhc_twt_cmd = (void *)cmd->data; + dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id); + dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation); + dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time); + dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp); + dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa); + dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration); + dhc_twt_cmd->trigger = trigger; + dhc_twt_cmd->flow_type = flow_type; + dhc_twt_cmd->flow_id = flow_id; + dhc_twt_cmd->protection = protection; + dhc_twt_cmd->twt_request = twt_request; + dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0; + + cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2); + cmd->index_and_mask = + cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | + DHC_INT_UMAC_TWT_OPERATION); + + hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd); + hcmd.data[0] = cmd; + + ret = iwl_mld_send_cmd(mld, &hcmd); + + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256); + +static ssize_t +iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_twt_operation_cmd twt_cmd = {}; + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif, + link_id); + int ret; + + if (WARN_ON(!mld_link)) + return -ENODEV; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (hweight16(vif->active_links) > 1) + return -EOPNOTSUPP; + + ret = sscanf(buf, + "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu", + &twt_cmd.twt_operation, &twt_cmd.target_wake_time, + &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa, + &twt_cmd.minimum_wake_duration, &twt_cmd.trigger, + &twt_cmd.flow_type, &twt_cmd.flow_id, + &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator, + &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type, + &twt_cmd.twt_request, &twt_cmd.implicit, + &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel, + &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid, + &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap, + &twt_cmd.ul_tid_bitmap); + + if (ret != 21) + return -EINVAL; + + twt_cmd.link_id = cpu_to_le32(mld_link->fw_id); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD), + &twt_cmd); + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256); + +void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct dentry *mld_vif_dbgfs = + debugfs_create_dir("iwlmld", vif->debugfs_dir); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) + + (7 + IFNAMSIZ + 1) + 6 + 1]; + char name[7 + IFNAMSIZ + 1]; + + /* Create symlink for convenience pointing to interface specific + * debugfs entries for the driver. For example, under + * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/ + * find + * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/ + */ + snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); + snprintf(target, sizeof(target), "../../../%pd3/iwlmld", + vif->debugfs_dir); + mld_vif->dbgfs_slink = + debugfs_create_symlink(name, mld->debugfs_dir, target); + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + vif->type == NL80211_IFTYPE_STATION) { + VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200); + } + + if (vif->type == NL80211_IFTYPE_AP) { + VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, + mld_vif_dbgfs, 0200); + } + + VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600); + VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); +} + +#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) + +#define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, link_conf, \ + &iwl_dbgfs_link_##name##_ops) +#define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \ + LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir) +{ + struct dentry *mld_link_dir; + + mld_link_dir = debugfs_lookup("iwlmld", dir); + + /* For non-MLO vifs, the dir of deflink is the same as the vif's one. + * so if iwlmld dir already exists, this means that this is deflink. + * If not, this is a per-link dir of a MLO vif, add in it the iwlmld + * dir. + */ + if (!mld_link_dir) + mld_link_dir = debugfs_create_dir("iwlmld", dir); +} + +static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_link_sta *link_sta = data; + struct iwl_mld_link_sta *mld_link_sta; + u32 rate; + u32 partial = false; + char pretty_rate[100]; + int ret; + u8 fw_sta_id; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + fw_sta_id = mld_link_sta->fw_id; + + if (sscanf(buf, "%i %i", &rate, &partial) == 0) + return -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, + partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : + IWL_TLC_DEBUG_FIXED_RATE, + rate); + + rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate); + + IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n", + fw_sta_id, pretty_rate, partial, ret); + + return ret ? : count; +} + +static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_link_sta *link_sta = data; + struct iwl_mld_link_sta *mld_link_sta; + u32 type, value; + int ret; + u8 fw_sta_id; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + fw_sta_id = mld_link_sta->fw_id; + + if (sscanf(buf, "%i %i", &type, &value) != 2) { + IWL_DEBUG_RATE(mld, "usage \n"); + return -EINVAL; + } + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value); + + return ret ? : count; +} + +#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, link_sta, \ + &iwl_dbgfs_##name##_ops) +#define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \ + LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +#define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta) + +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); + +void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h new file mode 100644 index 000000000000..04c536db8b16 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#include "iface.h" +#include "sta.h" + +#define MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +struct dbgfs_##name##_data { \ + argtype *arg; \ + bool read_done; \ + ssize_t rlen; \ + char buf[buflen]; \ +}; \ +static int _iwl_dbgfs_##name##_open(struct inode *inode, \ + struct file *file) \ +{ \ + struct dbgfs_##name##_data *data; \ + \ + if ((file->f_flags & O_ACCMODE) == O_RDWR) \ + return -EOPNOTSUPP; \ + \ + data = kzalloc(sizeof(*data), GFP_KERNEL); \ + if (!data) \ + return -ENOMEM; \ + \ + data->read_done = false; \ + data->arg = inode->i_private; \ + file->private_data = data; \ + \ + return 0; \ +} + +#define MLD_DEBUGFS_READ_WRAPPER(name) \ +static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + \ + if (!data->read_done) { \ + data->read_done = true; \ + data->rlen = iwl_dbgfs_##name##_read(data->arg, \ + sizeof(data->buf),\ + data->buf); \ + } \ + \ + if (data->rlen < 0) \ + return data->rlen; \ + return simple_read_from_buffer(user_buf, count, ppos, \ + data->buf, data->rlen); \ +} + +static int _iwl_dbgfs_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +#define _MLD_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \ +MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +MLD_DEBUGFS_READ_WRAPPER(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ +} + +#define WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_write(mld, buf, count, data); \ +} + +static inline struct iwl_mld * +iwl_mld_from_link_sta(struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_vif *vif = + iwl_mld_sta_from_mac80211(link_sta->sta)->vif; + return iwl_mld_vif_from_mac80211(vif)->mld; +} + +static inline struct iwl_mld * +iwl_mld_from_bss_conf(struct ieee80211_bss_conf *link) +{ + return iwl_mld_vif_from_mac80211(link->vif)->mld; +} + +static inline struct iwl_mld *iwl_mld_from_vif(struct ieee80211_vif *vif) +{ + return iwl_mld_vif_from_mac80211(vif)->mld; +} + +#define WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \ +WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct ieee80211_##objtype *arg = file->private_data; \ + struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \ + char buf[bufsz] = {}; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + buf, sizeof(buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + arg); \ +} + +#define WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, objtype) \ + WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ + } + +#define WIPHY_DEBUGFS_HANDLER_WRAPPER_MLD(name) \ +static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_write(mld, buf, count); \ +} + +#define WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ +WIPHY_DEBUGFS_HANDLER_WRAPPER_MLD(name) \ +static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct iwl_mld *mld = data->arg; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + data->buf, sizeof(data->buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + NULL); \ +} + +#define WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + +#define WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ + MLD_DEBUGFS_READ_WRAPPER(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + +#define WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \ +WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct ieee80211_##objtype *arg = data->arg; \ + struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \ + char buf[bufsz] = {}; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + buf, sizeof(buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + arg); \ +} + +#define IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, objtype) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct ieee80211_##objtype) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \ + MLD_DEBUGFS_READ_WRAPPER(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c new file mode 100644 index 000000000000..f77ba21a174d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ +#include +#include +#include +#include "mld.h" +#include "iface.h" +#include "phy.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "constants.h" +#include "fw/api/location.h" +#include "ftm-initiator.h" + +static void iwl_mld_ftm_cmd_common(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_tof_range_req_cmd *cmd, + struct cfg80211_pmsr_request *req) +{ + int i; + + cmd->initiator_flags = + cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM | + IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT); + cmd->request_id = req->cookie; + cmd->num_of_ap = req->n_peers; + + /* Use a large value for "no timeout". Don't use the maximum value + * because of fw limitations. + */ + if (req->timeout) + cmd->req_timeout_ms = cpu_to_le32(min(req->timeout, 0xfffff)); + else + cmd->req_timeout_ms = cpu_to_le32(0xfffff); + + memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; + + if (vif->cfg.assoc) { + memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); + + /* AP's TSF is only relevant if associated */ + for (i = 0; i < req->n_peers; i++) { + if (req->peers[i].report_ap_tsf) { + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(vif); + + cmd->tsf_mac_id = cpu_to_le32(mld_vif->fw_id); + return; + } + } + } else { + eth_broadcast_addr(cmd->range_req_bssid); + } + + /* Don't report AP's TSF */ + cmd->tsf_mac_id = cpu_to_le32(0xff); +} + +static int +iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + u32 freq = peer->chandef.chan->center_freq; + + target->channel_num = ieee80211_frequency_to_channel(freq); + + switch (peer->chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; + target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_20: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; + target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_40: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; + target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_80: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; + target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_160: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE; + target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; + break; + default: + IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n", + peer->chandef.width); + return -EINVAL; +} + + /* non EDCA based measurement must use HE preamble */ + if (peer->ftm.trigger_based || peer->ftm.non_trigger_based) + target->format_bw |= IWL_LOCATION_FRAME_FORMAT_HE; + + target->ctrl_ch_position = + (peer->chandef.width > NL80211_CHAN_WIDTH_20) ? + iwl_mld_get_fw_ctrl_pos(&peer->chandef) : 0; + + target->band = iwl_mld_nl80211_band_to_fw(peer->chandef.chan->band); + return 0; +} + +#define FTM_SET_FLAG(flag) (target->initiator_ap_flags |= \ + cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag)) + +static void +iwl_mld_ftm_set_target_flags(struct iwl_mld *mld, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + target->initiator_ap_flags = cpu_to_le32(0); + + if (peer->ftm.asap) + FTM_SET_FLAG(ASAP); + + if (peer->ftm.request_lci) + FTM_SET_FLAG(LCI_REQUEST); + + if (peer->ftm.request_civicloc) + FTM_SET_FLAG(CIVIC_REQUEST); + + if (IWL_MLD_FTM_INITIATOR_DYNACK) + FTM_SET_FLAG(DYN_ACK); + + if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG) + FTM_SET_FLAG(ALGO_LR); + else if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT) + FTM_SET_FLAG(ALGO_FFT); + + if (peer->ftm.trigger_based) + FTM_SET_FLAG(TB); + else if (peer->ftm.non_trigger_based) + FTM_SET_FLAG(NON_TB); + + if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) && + peer->ftm.lmr_feedback) + FTM_SET_FLAG(LMR_FEEDBACK); +} + +static void iwl_mld_ftm_set_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + u32 sta_id_mask; + + target->sta_id = IWL_INVALID_STA; + + /* TODO: add ftm_unprotected debugfs support */ + + if (!vif->cfg.assoc || !mld_vif->ap_sta) + return; + + sta_id_mask = iwl_mld_fw_sta_id_mask(mld, mld_vif->ap_sta); + if (WARN_ON(hweight32(sta_id_mask) != 1)) + return; + + target->sta_id = __ffs(sta_id_mask); + + if (mld_vif->ap_sta->mfp && + (peer->ftm.trigger_based || peer->ftm.non_trigger_based)) + FTM_SET_FLAG(PMF); +} + +static int +iwl_mld_ftm_set_target(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + u32 i2r_max_sts; + int ret; + + ret = iwl_mld_ftm_set_target_chandef(mld, peer, target); + if (ret) + return ret; + + memcpy(target->bssid, peer->addr, ETH_ALEN); + target->burst_period = cpu_to_le16(peer->ftm.burst_period); + target->samples_per_burst = peer->ftm.ftms_per_burst; + target->num_of_bursts = peer->ftm.num_bursts_exp; + iwl_mld_ftm_set_target_flags(mld, peer, target); + iwl_mld_ftm_set_sta(mld, vif, peer, target); + + /* TODO: add secured ranging support */ + + i2r_max_sts = IWL_MLD_FTM_I2R_MAX_STS > 1 ? 1 : + IWL_MLD_FTM_I2R_MAX_STS; + + target->r2i_ndp_params = IWL_MLD_FTM_R2I_MAX_REP | + (IWL_MLD_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) | + (IWL_MLD_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); + target->i2r_ndp_params = IWL_MLD_FTM_I2R_MAX_REP | + (i2r_max_sts << IWL_LOCATION_MAX_STS_POS) | + (IWL_MLD_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); + + if (peer->ftm.non_trigger_based) { + target->min_time_between_msr = + cpu_to_le16(IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR); + target->burst_period = + cpu_to_le16(IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR); + } else { + target->min_time_between_msr = cpu_to_le16(0); + } + + /* TODO: Beacon interval is currently unknown, so use the common value + * of 100 TUs. + */ + target->beacon_interval = cpu_to_le16(100); + + return 0; +} + +int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *req) +{ + struct iwl_tof_range_req_cmd cmd; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), + .dataflags[0] = IWL_HCMD_DFL_DUP, + .data[0] = &cmd, + .len[0] = sizeof(cmd), + }; + u8 i; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (mld->ftm_initiator.req) + return -EBUSY; + + if (req->n_peers > ARRAY_SIZE(cmd.ap)) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + + iwl_mld_ftm_cmd_common(mld, vif, (void *)&cmd, req); + + for (i = 0; i < cmd.num_of_ap; i++) { + struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; + struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i]; + + ret = iwl_mld_ftm_set_target(mld, vif, peer, target); + if (ret) + return ret; + } + + /* TODO: get the status from the response*/ + ret = iwl_mld_send_cmd(mld, &hcmd); + if (!ret) { + mld->ftm_initiator.req = req; + mld->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif); + } + + return ret; +} + +static void iwl_mld_ftm_reset(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + mld->ftm_initiator.req = NULL; + mld->ftm_initiator.req_wdev = NULL; + memset(mld->ftm_initiator.responses, 0, + sizeof(mld->ftm_initiator.responses)); +} + +static int iwl_mld_ftm_range_resp_valid(struct iwl_mld *mld, u8 request_id, + u8 num_of_aps) +{ + if (IWL_FW_CHECK(mld, request_id != (u8)mld->ftm_initiator.req->cookie, + "Request ID mismatch, got %u, active %u\n", + request_id, (u8)mld->ftm_initiator.req->cookie)) + return -EINVAL; + + if (IWL_FW_CHECK(mld, num_of_aps > mld->ftm_initiator.req->n_peers || + num_of_aps > IWL_TOF_MAX_APS, + "FTM range response: invalid num of APs (%u)\n", + num_of_aps)) + return -EINVAL; + + return 0; +} + +static int iwl_mld_ftm_find_peer(struct cfg80211_pmsr_request *req, + const u8 *addr) +{ + for (int i = 0; i < req->n_peers; i++) { + struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; + + if (ether_addr_equal_unaligned(peer->addr, addr)) + return i; + } + + return -ENOENT; +} + +static void iwl_mld_debug_range_resp(struct iwl_mld *mld, u8 index, + struct cfg80211_pmsr_result *res) +{ + s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666); + + IWL_DEBUG_INFO(mld, "entry %d\n", index); + IWL_DEBUG_INFO(mld, "\tstatus: %d\n", res->status); + IWL_DEBUG_INFO(mld, "\tBSSID: %pM\n", res->addr); + IWL_DEBUG_INFO(mld, "\thost time: %llu\n", res->host_time); + IWL_DEBUG_INFO(mld, "\tburst index: %d\n", res->ftm.burst_index); + IWL_DEBUG_INFO(mld, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes); + IWL_DEBUG_INFO(mld, "\trssi: %d\n", res->ftm.rssi_avg); + IWL_DEBUG_INFO(mld, "\trssi spread: %d\n", res->ftm.rssi_spread); + IWL_DEBUG_INFO(mld, "\trtt: %lld\n", res->ftm.rtt_avg); + IWL_DEBUG_INFO(mld, "\trtt var: %llu\n", res->ftm.rtt_variance); + IWL_DEBUG_INFO(mld, "\trtt spread: %llu\n", res->ftm.rtt_spread); + IWL_DEBUG_INFO(mld, "\tdistance: %lld\n", rtt_avg); +} + +void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tof_range_rsp_ntfy *fw_resp = (void *)pkt->data; + u8 num_of_aps, last_in_batch; + + if (IWL_FW_CHECK(mld, !mld->ftm_initiator.req, + "FTM response without a pending request\n")) + return; + + if (iwl_mld_ftm_range_resp_valid(mld, fw_resp->request_id, + fw_resp->num_of_aps)) + return; + + num_of_aps = fw_resp->num_of_aps; + last_in_batch = fw_resp->last_report; + + IWL_DEBUG_INFO(mld, "Range response received\n"); + IWL_DEBUG_INFO(mld, "request id: %llu, num of entries: %u\n", + mld->ftm_initiator.req->cookie, num_of_aps); + + for (int i = 0; i < num_of_aps; i++) { + struct cfg80211_pmsr_result result = {}; + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap; + int peer_idx; + + fw_ap = &fw_resp->ap[i]; + result.final = fw_ap->last_burst; + result.ap_tsf = le32_to_cpu(fw_ap->start_tsf); + result.ap_tsf_valid = 1; + + peer_idx = iwl_mld_ftm_find_peer(mld->ftm_initiator.req, + fw_ap->bssid); + if (peer_idx < 0) { + IWL_WARN(mld, + "Unknown address (%pM, target #%d) in FTM response\n", + fw_ap->bssid, i); + continue; + } + + switch (fw_ap->measure_status) { + case IWL_TOF_ENTRY_SUCCESS: + result.status = NL80211_PMSR_STATUS_SUCCESS; + break; + case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT: + result.status = NL80211_PMSR_STATUS_TIMEOUT; + break; + case IWL_TOF_ENTRY_NO_RESPONSE: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE; + break; + case IWL_TOF_ENTRY_REQUEST_REJECTED: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_PEER_BUSY; + result.ftm.busy_retry_time = fw_ap->refusal_period; + break; + default: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED; + break; + } + memcpy(result.addr, fw_ap->bssid, ETH_ALEN); + + /* TODO: convert the timestamp from the result to systime */ + result.host_time = ktime_get_boottime_ns(); + + result.type = NL80211_PMSR_TYPE_FTM; + result.ftm.burst_index = mld->ftm_initiator.responses[peer_idx]; + mld->ftm_initiator.responses[peer_idx]++; + result.ftm.rssi_avg = fw_ap->rssi; + result.ftm.rssi_avg_valid = 1; + result.ftm.rssi_spread = fw_ap->rssi_spread; + result.ftm.rssi_spread_valid = 1; + result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt); + result.ftm.rtt_avg_valid = 1; + result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance); + result.ftm.rtt_variance_valid = 1; + result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread); + result.ftm.rtt_spread_valid = 1; + + cfg80211_pmsr_report(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + &result, GFP_KERNEL); + + if (fw_has_api(&mld->fw->ucode_capa, + IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) + IWL_DEBUG_INFO(mld, "RTT confidence: %u\n", + fw_ap->rttConfidence); + + iwl_mld_debug_range_resp(mld, i, &result); + } + + if (last_in_batch) { + cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + GFP_KERNEL); + iwl_mld_ftm_reset(mld); + } +} + +void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld) +{ + struct cfg80211_pmsr_result result = { + .status = NL80211_PMSR_STATUS_FAILURE, + .final = 1, + .host_time = ktime_get_boottime_ns(), + .type = NL80211_PMSR_TYPE_FTM, + }; + + if (!mld->ftm_initiator.req) + return; + + for (int i = 0; i < mld->ftm_initiator.req->n_peers; i++) { + memcpy(result.addr, mld->ftm_initiator.req->peers[i].addr, + ETH_ALEN); + + cfg80211_pmsr_report(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + &result, GFP_KERNEL); + } + + cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, GFP_KERNEL); + iwl_mld_ftm_reset(mld); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h new file mode 100644 index 000000000000..e98fac34beba --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_ftm_initiator_h__ +#define __iwl_mld_ftm_initiator_h__ + +int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *req); + +void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld); + +#endif /* __iwl_mld_ftm_initiator_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c new file mode 100644 index 000000000000..ec93af362cfc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" + +#include "fw/api/alive.h" +#include "fw/api/scan.h" +#include "fw/api/rx.h" +#include "fw/dbg.h" +#include "fw/pnvm.h" +#include "hcmd.h" +#include "iwl-nvm-parse.h" +#include "power.h" +#include "mcc.h" +#include "led.h" +#include "coex.h" +#include "regulatory.h" +#include "thermal.h" + +static int iwl_mld_send_tx_ant_cfg(struct iwl_mld *mld) +{ + struct iwl_tx_ant_cfg_cmd cmd; + + lockdep_assert_wiphy(mld->wiphy); + + cmd.valid = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)); + + IWL_DEBUG_FW(mld, "select valid tx ant: %u\n", cmd.valid); + + return iwl_mld_send_cmd_pdu(mld, TX_ANT_CONFIGURATION_CMD, &cmd); +} + +static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld) +{ + struct iwl_rss_config_cmd cmd = { + .flags = cpu_to_le32(IWL_RSS_ENABLE), + .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) | + BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD), + }; + + lockdep_assert_wiphy(mld->wiphy); + + /* Do not direct RSS traffic to Q 0 which is our fallback queue */ + for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) + cmd.indirection_table[i] = + 1 + (i % (mld->trans->num_rx_queues - 1)); + netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key)); + + return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd); +} + +static int iwl_mld_config_scan(struct iwl_mld *mld) +{ + struct iwl_scan_config cmd = { + .tx_chains = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)), + .rx_chains = cpu_to_le32(iwl_mld_get_valid_rx_ant(mld)) + }; + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(LONG_GROUP, SCAN_CFG_CMD), + &cmd); +} + +static void iwl_mld_alive_imr_data(struct iwl_trans *trans, + const struct iwl_imr_alive_info *imr_info) +{ + struct iwl_imr_data *imr_data = &trans->dbg.imr_data; + + imr_data->imr_enable = le32_to_cpu(imr_info->enabled); + imr_data->imr_size = le32_to_cpu(imr_info->size); + imr_data->imr2sram_remainbyte = imr_data->imr_size; + imr_data->imr_base_addr = imr_info->base_addr; + imr_data->imr_curr_addr = le64_to_cpu(imr_data->imr_base_addr); + + if (imr_data->imr_enable) + return; + + for (int i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) { + struct iwl_fw_ini_region_tlv *reg; + + if (!trans->dbg.active_regions[i]) + continue; + + reg = (void *)trans->dbg.active_regions[i]->data; + + /* We have only one DRAM IMR region, so we + * can break as soon as we find the first + * one. + */ + if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) { + trans->dbg.unsupported_region_msk |= BIT(i); + break; + } + } +} + +static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + struct iwl_mld *mld = + container_of(notif_wait, struct iwl_mld, notif_wait); + struct iwl_trans *trans = mld->trans; + u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, + UCODE_ALIVE_NTFY, 0); + struct iwl_alive_ntf_v6 *palive; + bool *alive_valid = data; + struct iwl_umac_alive *umac; + struct iwl_lmac_alive *lmac1; + struct iwl_lmac_alive *lmac2 = NULL; + u32 lmac_error_event_table; + u32 umac_error_table; + u16 status; + + if (version < 6 || version > 7 || pkt_len != sizeof(*palive)) + return false; + + palive = (void *)pkt->data; + + iwl_mld_alive_imr_data(trans, &palive->imr); + + umac = &palive->umac_data; + lmac1 = &palive->lmac_data[0]; + lmac2 = &palive->lmac_data[1]; + status = le16_to_cpu(palive->status); + + trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]); + trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]); + trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]); + + IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", + trans->sku_id[0], trans->sku_id[1], trans->sku_id[2]); + + lmac_error_event_table = + le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr); + iwl_fw_lmac1_set_alive_err_table(trans, lmac_error_event_table); + + if (lmac2) + trans->dbg.lmac_error_event_table[1] = + le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr); + + umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) & + ~FW_ADDR_CACHE_CONTROL; + + if (umac_error_table >= trans->cfg->min_umac_error_event_table) + iwl_fw_umac_set_alive_err_table(trans, umac_error_table); + else + IWL_ERR(mld, "Not valid error log pointer 0x%08X\n", + umac_error_table); + + *alive_valid = status == IWL_ALIVE_STATUS_OK; + + IWL_DEBUG_FW(mld, + "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n", + status, lmac1->ver_type, lmac1->ver_subtype); + + if (lmac2) + IWL_DEBUG_FW(mld, "Alive ucode CDB\n"); + + IWL_DEBUG_FW(mld, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + le32_to_cpu(umac->umac_major), + le32_to_cpu(umac->umac_minor)); + + if (version >= 7) + IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n", + le16_to_cpu(palive->flags)); + + iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac); + + return true; +} + +#define MLD_ALIVE_TIMEOUT (2 * HZ) +#define MLD_INIT_COMPLETE_TIMEOUT (2 * HZ) + +static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld) +{ + struct iwl_trans *trans = mld->trans; + struct iwl_pc_data *pc_data; + u8 count; + + IWL_ERR(mld, + "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", + iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS), + iwl_read_umac_prph(trans, + UMAG_SB_CPU_2_STATUS)); +#define IWL_FW_PRINT_REG_INFO(reg_name) \ + IWL_ERR(mld, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name)) + + IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION); + + IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE); + + /* print OTP info */ + IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR); + IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA); +#undef IWL_FW_PRINT_REG_INFO + + pc_data = trans->dbg.pc_data; + for (count = 0; count < trans->dbg.num_pc; count++, pc_data++) + IWL_ERR(mld, "%s: 0x%x\n", pc_data->pc_name, + pc_data->pc_address); +} + +static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld) +{ + const struct fw_img *fw = + iwl_get_ucode_image(mld->fw, IWL_UCODE_REGULAR); + static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; + struct iwl_notification_wait alive_wait; + bool alive_valid = false; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + iwl_init_notification_wait(&mld->notif_wait, &alive_wait, + alive_cmd, ARRAY_SIZE(alive_cmd), + iwl_alive_fn, &alive_valid); + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); + + ret = iwl_trans_start_fw(mld->trans, fw, true); + if (ret) { + iwl_remove_notification(&mld->notif_wait, &alive_wait); + return ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &alive_wait, + MLD_ALIVE_TIMEOUT); + + if (ret) { + if (ret == -ETIMEDOUT) + iwl_fw_dbg_error_collect(&mld->fwrt, + FW_DBG_TRIGGER_ALIVE_TIMEOUT); + iwl_mld_print_alive_notif_timeout(mld); + goto alive_failure; + } + + if (!alive_valid) { + IWL_ERR(mld, "Loaded firmware is not valid!\n"); + ret = -EIO; + goto alive_failure; + } + + iwl_trans_fw_alive(mld->trans, 0); + + return 0; + +alive_failure: + iwl_trans_stop_device(mld->trans); + return ret; +} + +int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) +{ + struct iwl_notification_wait init_wait; + struct iwl_init_extended_cfg_cmd init_cfg = {}; + static const u16 init_complete[] = { + INIT_COMPLETE_NOTIF, + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_load_fw_wait_alive(mld); + if (ret) + return ret; + + mld->trans->step_urm = + !!(iwl_read_umac_prph(mld->trans, CNVI_PMU_STEP_FLOW) & + CNVI_PMU_STEP_FLOW_FORCE_URM); + + ret = iwl_pnvm_load(mld->trans, &mld->notif_wait, + &mld->fw->ucode_capa); + if (ret) { + IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret); + goto init_failure; + } + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, + NULL); + + iwl_init_notification_wait(&mld->notif_wait, + &init_wait, + init_complete, + ARRAY_SIZE(init_complete), + NULL, NULL); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD), + &init_cfg); + if (ret) { + IWL_ERR(mld, "Failed to send init config command: %d\n", ret); + iwl_remove_notification(&mld->notif_wait, &init_wait); + goto init_failure; + } + + ret = iwl_wait_notification(&mld->notif_wait, &init_wait, + MLD_INIT_COMPLETE_TIMEOUT); + if (ret) { + IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret); + goto init_failure; + } + + if (!mld->nvm_data) { + mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); + if (IS_ERR(mld->nvm_data)) { + ret = PTR_ERR(mld->nvm_data); + mld->nvm_data = NULL; + IWL_ERR(mld, "Failed to read NVM: %d\n", ret); + goto init_failure; + } + } + + return 0; + +init_failure: + iwl_trans_stop_device(mld->trans); + return ret; +} + +int iwl_mld_load_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_trans_start_hw(mld->trans); + if (ret) + return ret; + + ret = iwl_mld_run_fw_init_sequence(mld); + if (ret) + return ret; + + mld->fw_status.running = true; + + return 0; +} + +void iwl_mld_stop_fw(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + iwl_abort_notification_waits(&mld->notif_wait); + + iwl_fw_dbg_stop_sync(&mld->fwrt); + + iwl_trans_stop_device(mld->trans); + + mld->fw_status.running = false; +} + +static void iwl_mld_restart_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_hw_restart_disconnect(vif); +} + +void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags) +{ + u32 error_log_size = mld->fw->ucode_capa.error_log_size; + struct iwl_fw_error_recovery_cmd recovery_cmd = { + .flags = cpu_to_le32(flags), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD), + .flags = CMD_WANT_SKB, + .data = {&recovery_cmd, }, + .len = {sizeof(recovery_cmd), }, + }; + int ret; + + /* no error log was defined in TLV */ + if (!error_log_size) + return; + + if (flags & ERROR_RECOVERY_UPDATE_DB) { + /* no buf was allocated upon NIC error */ + if (!mld->error_recovery_buf) + return; + + cmd.data[1] = mld->error_recovery_buf; + cmd.len[1] = error_log_size; + cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + recovery_cmd.buf_size = cpu_to_le32(error_log_size); + } + + ret = iwl_mld_send_cmd(mld, &cmd); + + /* we no longer need the recovery buffer */ + kfree(mld->error_recovery_buf); + mld->error_recovery_buf = NULL; + + if (ret) { + IWL_ERR(mld, "Failed to send recovery cmd %d\n", ret); + return; + } + + if (flags & ERROR_RECOVERY_UPDATE_DB) { + struct iwl_rx_packet *pkt = cmd.resp_pkt; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u32 resp; + + if (IWL_FW_CHECK(mld, pkt_len != sizeof(resp), + "Unexpected recovery cmd response size %u (expected %zu)\n", + pkt_len, sizeof(resp))) + goto out; + + resp = le32_to_cpup((__le32 *)cmd.resp_pkt->data); + if (!resp) + goto out; + + IWL_ERR(mld, + "Failed to send recovery cmd blob was invalid %d\n", + resp); + + ieee80211_iterate_interfaces(mld->hw, 0, + iwl_mld_restart_disconnect_iter, + NULL); + } + +out: + iwl_free_resp(&cmd); +} + +static int iwl_mld_config_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + iwl_fw_disable_dbg_asserts(&mld->fwrt); + iwl_get_shared_mem_conf(&mld->fwrt); + + ret = iwl_mld_send_tx_ant_cfg(mld); + if (ret) + return ret; + + ret = iwl_mld_send_bt_init_conf(mld); + if (ret) + return ret; + + ret = iwl_set_soc_latency(&mld->fwrt); + if (ret) + return ret; + + iwl_mld_configure_lari(mld); + + ret = iwl_mld_config_temp_report_ths(mld); + if (ret) + return ret; + +#ifdef CONFIG_THERMAL + ret = iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_START); + if (ret) + return ret; +#endif + + ret = iwl_configure_rxq(&mld->fwrt); + if (ret) + return ret; + + ret = iwl_mld_send_rss_cfg_cmd(mld); + if (ret) + return ret; + + ret = iwl_mld_config_scan(mld); + if (ret) + return ret; + + ret = iwl_mld_update_device_power(mld, false); + if (ret) + return ret; + + ret = iwl_mld_init_mcc(mld); + if (ret) + return ret; + + if (mld->fw_status.in_hw_restart) { + iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB); + iwl_mld_time_sync_fw_config(mld); + } + + iwl_mld_led_config_fw(mld); + + ret = iwl_mld_init_ppag(mld); + if (ret) + return ret; + + ret = iwl_mld_init_sar(mld); + if (ret) + return ret; + + ret = iwl_mld_init_sgom(mld); + if (ret) + return ret; + + iwl_mld_init_tas(mld); + iwl_mld_init_uats(mld); + + return 0; +} + +int iwl_mld_start_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_load_fw(mld); + if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) { + iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER); + goto error; + } + + IWL_DEBUG_INFO(mld, "uCode started.\n"); + + ret = iwl_mld_config_fw(mld); + if (ret) + goto error; + + return 0; + +error: + iwl_mld_stop_fw(mld); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h new file mode 100644 index 000000000000..773bc1b09392 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_hcmd_h__ +#define __iwl_mld_hcmd_h__ + +static inline int iwl_mld_send_cmd(struct iwl_mld *mld, struct iwl_host_cmd *cmd) +{ + /* No commands, including the d3 related commands, should be sent + * after entering d3 + */ + if (WARN_ON(mld->fw_status.in_d3)) + return -EIO; + + if (!(cmd->flags & CMD_ASYNC)) + lockdep_assert_wiphy(mld->wiphy); + + /* Devices that need to shutdown immediately on rfkill are not + * supported, so we can send all the cmds in rfkill + */ + cmd->flags |= CMD_SEND_IN_RFKILL; + + return iwl_trans_send_cmd(mld->trans, cmd); +} + +static inline int +__iwl_mld_send_cmd_with_flags_pdu(struct iwl_mld *mld, u32 id, + u32 flags, const void *data, u16 len) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { data ? len : 0, }, + .data = { data, }, + .flags = flags, + }; + + return iwl_mld_send_cmd(mld, &cmd); +} + +#define _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len, \ + ignored...) \ + __iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len) +#define iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len...) \ + _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, ##len, \ + sizeof(*(data))) + +#define iwl_mld_send_cmd_pdu(mld, id, ...) \ + iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, __VA_ARGS__) + +#define iwl_mld_send_cmd_empty(mld, id) \ + iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, NULL, 0) + +#endif /* __iwl_mld_hcmd_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c new file mode 100644 index 000000000000..691062b44461 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include + +#include "iface.h" +#include "hcmd.h" +#include "key.h" +#include "mlo.h" +#include "mac80211.h" + +#include "fw/api/context.h" +#include "fw/api/mac.h" +#include "fw/api/time-event.h" +#include "fw/api/datapath.h" + +/* 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) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_link *link; + + /* EMLSR is turned back on during recovery */ + vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + for_each_mld_vif_valid_link(mld_vif, link) { + iwl_mld_cleanup_link(mld_vif->mld, link); + + /* Correctly allocated primary link in non-MLO mode */ + if (!ieee80211_vif_is_mld(vif) && + link_id == 0 && link == &mld_vif->deflink) + continue; + + if (vif->active_links & BIT(link_id)) + continue; + + /* Should not happen as link removal should always succeed */ + WARN_ON(1); + if (link != &mld_vif->deflink) + kfree_rcu(link, rcu_head); + RCU_INIT_POINTER(mld_vif->link[link_id], NULL); + } + + ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL); + + CLEANUP_STRUCT(mld_vif); +} + +static int iwl_mld_send_mac_cmd(struct iwl_mld *mld, + struct iwl_mac_config_cmd *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 (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) +{ + switch (vif->type) { + case NL80211_IFTYPE_STATION: + return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA; + case NL80211_IFTYPE_AP: + return FW_MAC_TYPE_GO; + case NL80211_IFTYPE_MONITOR: + return FW_MAC_TYPE_LISTENER; + case NL80211_IFTYPE_P2P_DEVICE: + return FW_MAC_TYPE_P2P_DEVICE; + case NL80211_IFTYPE_ADHOC: + return FW_MAC_TYPE_IBSS; + default: + WARN_ON_ONCE(1); + } + return FW_MAC_TYPE_BSS_STA; +} + +static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + const struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *own_he_cap; + + lockdep_assert_wiphy(mld->wiphy); + + /* This capability is the same for all bands, + * so take it from one of them. + */ + sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ]; + own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_ACK_EN); +} + +/* fill the common part for all interface types */ +static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + u32 action) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + + lockdep_assert_wiphy(mld->wiphy); + + cmd->id_and_color = cpu_to_le32(mld_vif->fw_id); + cmd->action = cpu_to_le32(action); + + cmd->mac_type = + cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif)); + + memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); + + if (iwlwifi_mod_params.disable_11ax) + return; + + cmd->nic_not_ack_enabled = + cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif)); + + /* If we have MLO enabled, then the firmware needs to enable + * address translation for the station(s) we add. That depends + * on having EHT enabled in firmware, which in turn depends on + * mac80211 in the code below. + * However, mac80211 doesn't enable HE/EHT until it has parsed + * the association response successfully, so just skip all that + * and enable both when we have MLO. + */ + if (ieee80211_vif_is_mld(vif)) { + if (vif->type == NL80211_IFTYPE_AP) + cmd->he_ap_support = cpu_to_le16(1); + else + cmd->he_support = cpu_to_le16(1); + + cmd->eht_support = cpu_to_le32(1); + return; + } + + for_each_vif_active_link(vif, link_conf, link_id) { + if (!link_conf->he_support) + continue; + + if (vif->type == NL80211_IFTYPE_AP) + cmd->he_ap_support = cpu_to_le16(1); + else + cmd->he_support = cpu_to_le16(1); + + /* EHT, if supported, was already set above */ + break; + } +} + +static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, u32 action, + struct iwl_mac_config_cmd *cmd) +{ + struct ieee80211_bss_conf *link; + u32 twt_policy = 0; + int link_id; + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* We always want to hear MCAST frames, if we're not authorized yet, + * we'll drop them. + */ + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); + + /* Adding a MAC ctxt with is_assoc set is not allowed in fw + * (and shouldn't happen) + */ + if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) { + cmd->client.is_assoc = 1; + + if (!iwl_mld_vif_from_mac80211(vif)->authorized) + cmd->client.data_policy |= + cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); + } else { + /* Allow beacons to pass through as long as we are not + * associated + */ + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); + } + + cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid); + + if (ieee80211_vif_is_mld(vif)) { + u16 esr_transition_timeout = + u16_get_bits(vif->cfg.eml_cap, + IEEE80211_EML_CAP_TRANSITION_TIMEOUT); + + cmd->client.esr_transition_timeout = + min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, + esr_transition_timeout); + cmd->client.medium_sync_delay = + cpu_to_le16(vif->cfg.eml_med_sync_delay); + } + + for_each_vif_active_link(vif, link, link_id) { + if (!link->he_support) + continue; + + if (link->twt_requester) + twt_policy |= TWT_SUPPORTED; + if (link->twt_protected) + twt_policy |= PROTECTED_TWT_SUPPORTED; + if (link->twt_broadcast) + twt_policy |= BROADCAST_TWT_SUPPORTED; + } + + if (!iwlwifi_mod_params.disable_11ax) + cmd->client.data_policy |= cpu_to_le16(twt_policy); + + if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) + cmd->filter_flags |= + cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + + if (vif->p2p) + cmd->client.ctwin = + cpu_to_le32(vif->bss_conf.p2p_noa_attr.oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); +} + +static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_AP); + + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + + /* in AP mode, pass beacons from other APs (needed for ht protection). + * When there're no any associated station, which means that we are not + * TXing anyway, don't ask FW to pass beacons to prevent unnecessary + * wake-ups. + */ + if (mld_vif->num_associated_stas) + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); +} + +static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *go_active = _data; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && + iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) + *go_active = true; +} + +static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld) +{ + bool go_active = false; + + /* This flag should be set to true when the P2P Device is + * discoverable and there is at least a P2P GO. Setting + * this flag will allow the P2P Device to be discoverable on other + * channels in addition to its listen channel. + * Note that this flag should not be set in other cases as it opens the + * Rx filters on all MAC and increases the number of interrupts. + */ + ieee80211_iterate_active_interfaces(mld->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mld_go_iterator, &go_active); + + return go_active; +} + +static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld); + + lockdep_assert_wiphy(mld->wiphy); + + /* Override the filter flags to accept all management frames. This is + * needed to support both P2P device discovery using probe requests and + * P2P service discovery using action frames + */ + cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); + + if (ext_disc) + cmd->p2p_dev.is_disc_extended = cpu_to_le32(1); +} + +static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); +} + +static int +iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mac_config_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .id_and_color = cpu_to_le32(mld_vif->fw_id), + }; + + return iwl_mld_send_mac_cmd(mld, &cmd); +} + +int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (action == FW_CTXT_ACTION_REMOVE) + return iwl_mld_rm_mac_from_fw(mld, vif); + + iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd); + break; + case NL80211_IFTYPE_AP: + iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd); + break; + case NL80211_IFTYPE_MONITOR: + cmd.filter_flags = + cpu_to_le32(MAC_CFG_FILTER_PROMISC | + MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | + MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); + break; + case NL80211_IFTYPE_P2P_DEVICE: + iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd); + break; + case NL80211_IFTYPE_ADHOC: + iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd); + break; + default: + WARN(1, "not supported yet\n"); + return -EOPNOTSUPP; + } + + return iwl_mld_send_mac_cmd(mld, &cmd); +} + +IWL_MLD_ALLOC_FN(vif, vif) + +/* Constructor function for struct iwl_mld_vif */ +static int +iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + mld_vif->mld = mld; + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); + if (ret) + return ret; + + if (!mld->fw_status.in_hw_restart) { + wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk, + iwl_mld_emlsr_unblock_tpt_wk); + wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk, + iwl_mld_emlsr_check_tpt); + wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk, + iwl_mld_emlsr_prevent_done_wk); + wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk, + iwl_mld_emlsr_tmp_non_bss_done_wk); + } + + return 0; +} + +int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_init_vif(mld, vif); + if (ret) + return ret; + + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD); + if (ret) + RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + + return ret; +} + +int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = 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))) + return -EINVAL; + + RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF, + mld_vif->fw_id); + + return ret; +} + +void iwl_mld_set_vif_associated(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *link; + unsigned int link_id; + + for_each_vif_active_link(vif, link, link_id) { + if (iwl_mld_link_set_associated(mld, vif, link)) + IWL_ERR(mld, "failed to update link %d\n", link_id); + } + + iwl_mld_recalc_multicast_filter(mld); +} + +static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u8 *fw_id_bitmap = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + return; + + *fw_id_bitmap |= BIT(mld_vif->fw_id); +} + +u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld) +{ + u8 fw_id_bitmap = 0; + + ieee80211_iterate_interfaces(mld->hw, + IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, + iwl_mld_get_fw_id_bss_bitmap_iter, + &fw_id_bitmap); + + return fw_id_bitmap; +} + +void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data; + struct iwl_probe_resp_data *old_data, *new_data; + struct ieee80211_vif *vif; + struct iwl_mld_link *mld_link; + + IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n", + notif->noa_active, notif->csa_counter); + + if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >= + ARRAY_SIZE(mld->fw_id_to_vif), + "mac id is invalid: %d\n", + le32_to_cpu(notif->mac_id))) + return; + + vif = wiphy_dereference(mld->wiphy, + mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]); + + /* the firmware gives us the mac_id (and not the link_id), mac80211 + * gets a vif and not a link, bottom line, this flow is not MLD ready + * yet. + */ + if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) + return; + + if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA && + notif->csa_counter >= 1) + ieee80211_beacon_set_cntdwn(vif, notif->csa_counter); + + if (!vif->p2p) + return; + + mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink; + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) + return; + + memcpy(&new_data->notif, notif, sizeof(new_data->notif)); + + /* noa_attr contains 1 reserved byte, need to substruct it */ + new_data->noa_len = sizeof(struct ieee80211_vendor_ie) + + sizeof(new_data->notif.noa_attr) - 1; + + /* + * If it's a one time NoA, only one descriptor is needed, + * adjust the length according to len_low. + */ + if (new_data->notif.noa_attr.len_low == + sizeof(struct ieee80211_p2p_noa_desc) + 2) + new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc); + + old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); + rcu_assign_pointer(mld_link->probe_resp_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); +} + +void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; + struct ieee80211_vif *vif; + + if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif), + "mac id is invalid: %d\n", notif->mac_id)) + return; + + vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]); + + if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) + return; + + IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid); +} + +void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_datapath_monitor_notif *notif = (void *)pkt->data; + struct ieee80211_bss_conf *link; + struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + + if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA)) + return; + + link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id); + if (WARN_ON(!link)) + return; + + vif = link->vif; + if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION || + !vif->cfg.assoc) + return; + + if (!link->chanreq.oper.chan || + link->chanreq.oper.chan->band != NL80211_BAND_2GHZ || + link->chanreq.oper.width < NL80211_CHAN_WIDTH_40) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* this shouldn't happen *again*, ignore it */ + if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE) + return; + + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT; + + /* + * This capability manipulation isn't really ideal, but it's the + * easiest choice - otherwise we'd have to do some major changes + * in mac80211 to support this, which isn't worth it. This does + * mean that userspace may have outdated information, but that's + * actually not an issue at all. + */ + sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; + + WARN_ON(!sband->ht_cap.ht_supported); + WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)); + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + if (he_cap) { + /* we know that ours is writable */ + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; + + WARN_ON(!he->has_he); + WARN_ON(!(he->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)); + he->he_cap_elem.phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } + + ieee80211_disconnect(vif, true); +} + +void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *he_cap; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE) + return; + + /* Now we are just reconnecting with the new capabilities, + * but remember to reset the capabilities when we disconnect for real + */ + if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) { + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET; + return; + } + + /* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */ + + sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; + + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + if (he_cap) { + /* we know that ours is writable */ + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; + + he->he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } + + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE; +} + +struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld) +{ + unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld); + int fw_id; + + if (hweight8(fw_id_bitmap) != 1) + return NULL; + + fw_id = __ffs(fw_id_bitmap); + + return wiphy_dereference(mld->wiphy, + mld->fw_id_to_vif[fw_id]); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h new file mode 100644 index 000000000000..2c928113f680 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_iface_h__ +#define __iwl_mld_iface_h__ + +#include + +#include "link.h" +#include "session-protect.h" +#include "d3.h" + +enum iwl_mld_cca_40mhz_wa_status { + CCA_40_MHZ_WA_NONE, + CCA_40_MHZ_WA_RESET, + CCA_40_MHZ_WA_RECONNECT, +}; + +/** + * enum iwl_mld_emlsr_blocked - defines reasons for which EMLSR is blocked + * + * These blocks are applied/stored per-VIF. + * + * @IWL_MLD_EMLSR_BLOCKED_PREVENTION: Prevent repeated EMLSR enter/exit + * @IWL_MLD_EMLSR_BLOCKED_WOWLAN: WOWLAN is preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_FW: FW did not recommend MLO + * @IWL_MLD_EMLSR_BLOCKED_ROC: remain-on-channel is preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_NON_BSS: An active non-BSS interface's link is + * preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS: An expected active non-BSS interface's + * link is preventing EMLSR. This is a temporary blocking that is set when + * 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 + */ +enum iwl_mld_emlsr_blocked { + IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1, + IWL_MLD_EMLSR_BLOCKED_WOWLAN = 0x2, + IWL_MLD_EMLSR_BLOCKED_FW = 0x4, + IWL_MLD_EMLSR_BLOCKED_ROC = 0x8, + IWL_MLD_EMLSR_BLOCKED_NON_BSS = 0x10, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x20, + IWL_MLD_EMLSR_BLOCKED_TPT = 0x40, +}; + +/** + * enum iwl_mld_emlsr_exit - defines reasons for exiting EMLSR + * + * Reasons to exit EMLSR may be either link specific or even specific to a + * combination of links. + * + * @IWL_MLD_EMLSR_EXIT_BLOCK: Exit due to a block reason being set + * @IWL_MLD_EMLSR_EXIT_MISSED_BEACON: Exit due to missed beacons + * @IWL_MLD_EMLSR_EXIT_FAIL_ENTRY: FW failed to enter EMLSR + * @IWL_MLD_EMLSR_EXIT_CSA: EMLSR prevented due to channel switch on link + * @IWL_MLD_EMLSR_EXIT_EQUAL_BAND: EMLSR prevented as both links share the band + * @IWL_MLD_EMLSR_EXIT_BANDWIDTH: Bandwidths of primary and secondary links are + * not equal + * @IWL_MLD_EMLSR_EXIT_LOW_RSSI: Link RSSI is unsuitable for EMLSR + * @IWL_MLD_EMLSR_EXIT_LINK_USAGE: Exit EMLSR due to low TPT on secondary link + * @IWL_MLD_EMLSR_EXIT_BT_COEX: Exit EMLSR due to BT coexistence + * @IWL_MLD_EMLSR_EXIT_CHAN_LOAD: Exit EMLSR because the primary channel is not + * loaded enough to justify EMLSR. + * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI + */ +enum iwl_mld_emlsr_exit { + IWL_MLD_EMLSR_EXIT_BLOCK = 0x1, + IWL_MLD_EMLSR_EXIT_MISSED_BEACON = 0x2, + IWL_MLD_EMLSR_EXIT_FAIL_ENTRY = 0x4, + IWL_MLD_EMLSR_EXIT_CSA = 0x8, + IWL_MLD_EMLSR_EXIT_EQUAL_BAND = 0x10, + IWL_MLD_EMLSR_EXIT_BANDWIDTH = 0x20, + IWL_MLD_EMLSR_EXIT_LOW_RSSI = 0x40, + IWL_MLD_EMLSR_EXIT_LINK_USAGE = 0x80, + IWL_MLD_EMLSR_EXIT_BT_COEX = 0x100, + IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x200, + IWL_MLD_EMLSR_EXIT_RFI = 0x400, +}; + +/** + * struct iwl_mld_emlsr - per-VIF data about EMLSR operation + * + * @primary: The current primary link + * @selected_primary: Primary link as selected during the last link selection + * @selected_links: Links as selected during the last link selection + * @blocked_reasons: Reasons preventing EMLSR from being enabled + * @last_exit_reason: Reason for the last EMLSR exit + * @last_exit_ts: Time of the last EMLSR exit (if @last_exit_reason is non-zero) + * @exit_repeat_count: Number of times EMLSR was exited for the same reason + * @unblock_tpt_wk: Unblock EMLSR because the throughput limit was reached + * @check_tpt_wk: a worker to check if IWL_MLD_EMLSR_BLOCKED_TPT should be + * added, for example if there is no longer enough traffic. + * @prevent_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_PREVENTION + * @tmp_non_bss_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS + */ +struct iwl_mld_emlsr { + struct_group(zeroed_on_not_authorized, + u8 primary; + + u8 selected_primary; + u16 selected_links; + + enum iwl_mld_emlsr_blocked blocked_reasons; + + enum iwl_mld_emlsr_exit last_exit_reason; + unsigned long last_exit_ts; + u8 exit_repeat_count; + ); + + struct wiphy_work unblock_tpt_wk; + struct wiphy_delayed_work check_tpt_wk; + + struct wiphy_delayed_work prevent_done_wk; + struct wiphy_delayed_work tmp_non_bss_done_wk; +}; + +/** + * struct iwl_mld_vif - virtual interface (MAC context) configuration parameters + * + * @fw_id: fw id of the mac context. + * @session_protect: session protection parameters + * @ap_sta: pointer to AP sta, for easier access to it. + * Relevant only for STA vifs. + * @authorized: indicates the AP station was set to authorized + * @bigtks: BIGTKs of the AP, for beacon protection. + * Only valid for STA. (FIXME: needs to be per link) + * @num_associated_stas: number of associated STAs. Relevant only for AP mode. + * @ap_ibss_active: whether the AP/IBSS was started + * @roc_activity: the id of the roc_activity running. Relevant for p2p device + * only. Set to %ROC_NUM_ACTIVITIES when not in use. + * @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the + * environment is too loaded, we work around this by reconnecting to the + * same AP with 20 MHz. This manages the status of the workaround. + * @beacon_inject_active: indicates an active debugfs beacon ie injection + * @low_latency_causes: bit flags, indicating the causes for low-latency, + * see @iwl_mld_low_latency_cause. + * @mld: pointer to the mld structure. + * @deflink: default link data, for use in non-MLO, + * @link: reference to link data for each valid link, for use in MLO. + * @emlsr: information related to EMLSR + * @wowlan_data: data used by the wowlan suspend flow + * @use_ps_poll: use ps_poll frames + * @disable_bf: disable beacon filter + * @dbgfs_slink: debugfs symlink for this interface + */ +struct iwl_mld_vif { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + struct iwl_mld_session_protect session_protect; + struct ieee80211_sta *ap_sta; + bool authorized; + struct ieee80211_key_conf __rcu *bigtks[2]; + u8 num_associated_stas; + bool ap_ibss_active; + u32 roc_activity; + enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround; +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool beacon_inject_active; +#endif + u8 low_latency_causes; + ); + /* And here fields that survive a fw restart */ + struct iwl_mld *mld; + struct iwl_mld_link deflink; + struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + + struct iwl_mld_emlsr emlsr; + +#if CONFIG_PM_SLEEP + struct iwl_mld_wowlan_data wowlan_data; +#endif +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool use_ps_poll; + bool disable_bf; + struct dentry *dbgfs_slink; +#endif +}; + +static inline struct iwl_mld_vif * +iwl_mld_vif_from_mac80211(struct ieee80211_vif *vif) +{ + return (void *)vif->drv_priv; +} + +#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)) + +#define for_each_mld_vif_valid_link(mld_vif, mld_link) \ + for (int link_id = 0; link_id < ARRAY_SIZE((mld_vif)->link); \ + link_id++) \ + if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id))) + +/* Retrieve pointer to mld link from mac80211 structures */ +static inline struct iwl_mld_link * +iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + + 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, + u32 action); +int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); +int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); +void iwl_mld_set_vif_associated(struct iwl_mld *mld, + struct ieee80211_vif *vif); +u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld); +void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif) +{ + return !!mld_vif->low_latency_causes; +} + +struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld); + +#endif /* __iwl_mld_iface_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c new file mode 100644 index 000000000000..0eff13e5ffd5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include "key.h" +#include "iface.h" +#include "sta.h" +#include "fw/api/datapath.h" + +static u32 iwl_mld_get_key_flags(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE; + bool igtk = key->keyidx == 4 || key->keyidx == 5; + u32 flags = 0; + + if (!pairwise) + flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_CCMP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + flags |= IWL_SEC_KEY_FLAG_KEY_SIZE; + fallthrough; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP; + break; + } + + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mld_vif->ap_sta; + + /* If we are installing an iGTK (in AP or STA mode), we need to tell + * the firmware this key will en/decrypt MGMT frames. + * Same goes if we are installing a pairwise key for an MFP station. + * In case we're installing a groupwise key (which is not an iGTK), + * then, we will not use this key for MGMT frames. + */ + if ((sta && sta->mfp && pairwise) || igtk) + flags |= IWL_SEC_KEY_FLAG_MFP; + + if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) + flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU; + + return flags; +} + +static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_link_sta *link_sta; + int sta_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* AP group keys are per link and should be on the mcast/bcast STA */ + if (vif->type == NL80211_IFTYPE_AP && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + struct iwl_mld_link *link = NULL; + + if (key->link_id >= 0) + link = iwl_mld_link_dereference_check(mld_vif, + key->link_id); + + if (WARN_ON(!link)) + return 0; + + /* In this stage we should have both the bcast and mcast STAs */ + if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA || + link->mcast_sta.sta_id == IWL_INVALID_STA)) + return 0; + + /* IGTK/BIGTK to bcast STA */ + if (key->keyidx >= 4) + return BIT(link->bcast_sta.sta_id); + + /* GTK for data to mcast STA */ + return BIT(link->mcast_sta.sta_id); + } + + /* for client mode use the AP STA also for group keys */ + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mld_vif->ap_sta; + + /* STA should be non-NULL now */ + if (WARN_ON(!sta)) + return 0; + + /* Key is not per-link, get the full sta mask */ + if (key->link_id < 0) + return iwl_mld_fw_sta_id_mask(mld, sta); + + /* The link_sta shouldn't be NULL now, but this is checked in + * iwl_mld_fw_sta_id_mask + */ + link_sta = link_sta_dereference_check(sta, key->link_id); + + sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + if (sta_id < 0) + return 0; + + return BIT(sta_id); +} + +static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask, + u32 key_flags, struct ieee80211_key_conf *key) +{ + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .u.add.sta_mask = cpu_to_le32(sta_mask), + .u.add.key_id = cpu_to_le32(key->keyidx), + .u.add.key_flags = cpu_to_le32(key_flags), + .u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)), + }; + bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP; + int max_key_len = sizeof(cmd.u.add.key); + + if (WARN_ON(!sta_mask)) + return -EINVAL; + + if (WARN_ON(key->keylen > max_key_len)) + return -EINVAL; + + memcpy(cmd.u.add.key, key->key, key->keylen); + + if (tkip) { + memcpy(cmd.u.add.tkip_mic_rx_key, + key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + 8); + memcpy(cmd.u.add.tkip_mic_tx_key, + key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, + 8); + } + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), + &cmd); +} + +static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask, + u32 key_flags, u32 keyidx) +{ + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .u.remove.sta_mask = cpu_to_le32(sta_mask), + .u.remove.key_id = cpu_to_le32(keyidx), + .u.remove.key_flags = cpu_to_le32(key_flags), + }; + + if (WARN_ON(!sta_mask)) + return; + + iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd); +} + +void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); + u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!sta_mask) + return; + + if (key->keyidx == 4 || key->keyidx == 5) { + struct iwl_mld_link *mld_link; + unsigned int link_id = 0; + + /* set to -1 for non-MLO right now */ + if (key->link_id >= 0) + link_id = key->link_id; + + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (WARN_ON(!mld_link)) + return; + + if (mld_link->igtk == key) + mld_link->igtk = NULL; + + mld->num_igtks--; + } + + iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx); + + /* no longer in HW */ + key->hw_key_idx = STA_KEY_IDX_INVALID; +} + +int iwl_mld_add_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); + u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = NULL; + bool igtk = key->keyidx == 4 || key->keyidx == 5; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (!sta_mask) + return -EINVAL; + + if (igtk) { + if (mld->num_igtks == IWL_MAX_NUM_IGTKS) + return -EOPNOTSUPP; + + u8 link_id = 0; + + /* set to -1 for non-MLO right now */ + if (key->link_id >= 0) + link_id = key->link_id; + + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (mld_link->igtk) { + IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n", + mld_link->igtk->keyidx); + iwl_mld_remove_key(mld, vif, sta, mld_link->igtk); + } + + WARN_ON(mld_link->igtk); + } + + ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key); + if (ret) + return ret; + + if (mld_link) { + mld_link->igtk = key; + mld->num_igtks++; + } + + /* We don't really need this, but need it to be not invalid, + * so we will know if the key is in fw. + */ + key->hw_key_idx = 0; + + return 0; +} + +struct remove_ap_keys_iter_data { + u8 link_id; + struct ieee80211_sta *sta; +}; + +static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct remove_ap_keys_iter_data *data = _data; + + if (key->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + /* All the pairwise keys should have been removed by now */ + if (WARN_ON(sta)) + return; + + if (key->link_id >= 0 && key->link_id != data->link_id) + return; + + iwl_mld_remove_key(mld, vif, data->sta, key); +} + +void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned int link_id) +{ + struct remove_ap_keys_iter_data iter_data = { + .link_id = link_id, + .sta = sta, + }; + + if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION)) + return; + + ieee80211_iter_keys(mld->hw, vif, + iwl_mld_remove_ap_keys_iter, + &iter_data); +} + +struct iwl_mvm_sta_key_update_data { + struct ieee80211_sta *sta; + u32 old_sta_mask; + u32 new_sta_mask; + int err; +}; + +static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm_sta_key_update_data *data = _data; + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(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_mld_get_key_flags(mld, 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_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), + &cmd); + + if (err) + data->err = err; +} + +int iwl_mld_update_sta_keys(struct iwl_mld *mld, + 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(mld->hw, vif, iwl_mld_update_sta_key_iter, + &data); + return data.err; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.h b/drivers/net/wireless/intel/iwlwifi/mld/key.h new file mode 100644 index 000000000000..a68ea48913be --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_key_h__ +#define __iwl_mld_key_h__ + +#include "mld.h" +#include +#include "fw/api/sta.h" + +void iwl_mld_remove_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int iwl_mld_add_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void iwl_mld_remove_ap_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + unsigned int link_id); + +int iwl_mld_update_sta_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask); + +static inline void +iwl_mld_cleanup_keys_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, void *data) +{ + key->hw_key_idx = STA_KEY_IDX_INVALID; +} + +#endif /* __iwl_mld_key_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.c b/drivers/net/wireless/intel/iwlwifi/mld/led.c new file mode 100644 index 000000000000..a37b32cbc6e6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/led.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include +#include + +#include "fw/api/led.h" +#include "mld.h" +#include "led.h" +#include "hcmd.h" + +static void iwl_mld_send_led_fw_cmd(struct iwl_mld *mld, bool on) +{ + struct iwl_led_cmd led_cmd = { + .status = cpu_to_le32(on), + }; + int err; + + if (WARN_ON(!mld->fw_status.running)) + return; + + err = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(LONG_GROUP, + LEDS_CMD), + CMD_ASYNC, &led_cmd); + + if (err) + IWL_WARN(mld, "LED command failed: %d\n", err); +} + +static void iwl_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_mld *mld = container_of(led_cdev, struct iwl_mld, led); + + if (!mld->fw_status.running) + return; + + iwl_mld_send_led_fw_cmd(mld, brightness > 0); +} + +int iwl_mld_leds_init(struct iwl_mld *mld) +{ + int mode = iwlwifi_mod_params.led_mode; + int ret; + + switch (mode) { + case IWL_LED_BLINK: + IWL_ERR(mld, "Blink led mode not supported, used default\n"); + fallthrough; + case IWL_LED_DEFAULT: + case IWL_LED_RF_STATE: + mode = IWL_LED_RF_STATE; + break; + case IWL_LED_DISABLE: + IWL_INFO(mld, "Led disabled\n"); + return 0; + default: + return -EINVAL; + } + + mld->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(mld->hw->wiphy)); + if (!mld->led.name) + return -ENOMEM; + + mld->led.brightness_set = iwl_led_brightness_set; + mld->led.max_brightness = 1; + + if (mode == IWL_LED_RF_STATE) + mld->led.default_trigger = + ieee80211_get_radio_led_name(mld->hw); + + ret = led_classdev_register(mld->trans->dev, &mld->led); + if (ret) { + kfree(mld->led.name); + mld->led.name = NULL; + IWL_INFO(mld, "Failed to enable led\n"); + } + + return ret; +} + +void iwl_mld_led_config_fw(struct iwl_mld *mld) +{ + if (!mld->led.name) + return; + + iwl_mld_send_led_fw_cmd(mld, mld->led.brightness > 0); +} + +void iwl_mld_leds_exit(struct iwl_mld *mld) +{ + if (!mld->led.name) + return; + + led_classdev_unregister(&mld->led); + kfree(mld->led.name); + mld->led.name = NULL; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.h b/drivers/net/wireless/intel/iwlwifi/mld/led.h new file mode 100644 index 000000000000..0954e214e73d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/led.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_led_h__ +#define __iwl_mld_led_h__ + +#include "mld.h" + +#ifdef CONFIG_IWLWIFI_LEDS +int iwl_mld_leds_init(struct iwl_mld *mld); +void iwl_mld_leds_exit(struct iwl_mld *mld); +void iwl_mld_led_config_fw(struct iwl_mld *mld); +#else +static inline int iwl_mld_leds_init(struct iwl_mld *mld) +{ + return 0; +} + +static inline void iwl_mld_leds_exit(struct iwl_mld *mld) +{ +} + +static inline void iwl_mld_led_config_fw(struct iwl_mld *mld) +{ +} +#endif + +#endif /* __iwl_mld_led_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c new file mode 100644 index 000000000000..64ebafc35c9b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -0,0 +1,1216 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "constants.h" +#include "link.h" +#include "iface.h" +#include "mlo.h" +#include "hcmd.h" +#include "phy.h" +#include "fw/api/rs.h" +#include "fw/api/txq.h" +#include "fw/api/mac.h" + +#include "fw/api/context.h" +#include "fw/dbg.h" + +static int iwl_mld_send_link_cmd(struct iwl_mld *mld, + struct iwl_link_config_cmd *cmd, + enum iwl_ctxt_action action) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + cmd->action = cpu_to_le32(action); + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), + cmd); + if (ret) + IWL_ERR(mld, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n", + action, ret); + return ret; +} + +static int iwl_mld_add_link_to_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(link_conf); + struct iwl_link_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!link)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(link->fw_id); + cmd.mac_id = cpu_to_le32(mld_vif->fw_id); + cmd.spec_link_id = link_conf->link_id; + cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); + + ether_addr_copy(cmd.local_link_addr, link_conf->addr); + + if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) + ether_addr_copy(cmd.ibss_bssid_addr, link_conf->bssid); + + return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD); +} + +/* Get the basic rates of the used band and add the mandatory ones */ +static void iwl_mld_fill_rates(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *chan_ctx, + __le32 *cck_rates, __le32 *ofdm_rates) +{ + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(chan_ctx); + struct ieee80211_supported_band *sband = + mld->hw->wiphy->bands[chandef->chan->band]; + unsigned long basic = link->basic_rates; + int lowest_present_ofdm = 100; + int lowest_present_cck = 100; + u32 cck = 0; + u32 ofdm = 0; + int i; + + for_each_set_bit(i, &basic, BITS_PER_LONG) { + int hw = sband->bitrates[i].hw_value; + + if (hw >= IWL_FIRST_OFDM_RATE) { + ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); + if (lowest_present_ofdm > hw) + lowest_present_ofdm = hw; + } else { + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + cck |= BIT(hw); + if (lowest_present_cck > hw) + lowest_present_cck = hw; + } + } + + /* Now we've got the basic rates as bitmaps in the ofdm and cck + * variables. This isn't sufficient though, as there might not + * be all the right rates in the bitmap. E.g. if the only basic + * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps + * and 6 Mbps because the 802.11-2007 standard says in 9.6: + * + * [...] a STA responding to a received frame shall transmit + * its Control Response frame [...] at the highest rate in the + * BSSBasicRateSet parameter that is less than or equal to the + * rate of the immediately previous frame in the frame exchange + * sequence ([...]) and that is of the same modulation class + * ([...]) as the received frame. If no rate contained in the + * BSSBasicRateSet parameter meets these conditions, then the + * control frame sent in response to a received frame shall be + * transmitted at the highest mandatory rate of the PHY that is + * less than or equal to the rate of the received frame, and + * that is of the same modulation class as the received frame. + * + * As a consequence, we need to add all mandatory rates that are + * lower than all of the basic rates to these bitmaps. + */ + + if (lowest_present_ofdm > IWL_RATE_24M_INDEX) + ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; + if (lowest_present_ofdm > IWL_RATE_12M_INDEX) + ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; + /* 6M already there or needed so always add */ + ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; + + /* CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. + * Note, however: + * - if no CCK rates are basic, it must be ERP since there must + * be some basic rates at all, so they're OFDM => ERP PHY + * (or we're in 5 GHz, and the cck bitmap will never be used) + * - if 11M is a basic rate, it must be ERP as well, so add 5.5M + * - if 5.5M is basic, 1M and 2M are mandatory + * - if 2M is basic, 1M is mandatory + * - if 1M is basic, that's the only valid ACK rate. + * As a consequence, it's not as complicated as it sounds, just add + * any lower rates to the ACK rate bitmap. + */ + if (lowest_present_cck > IWL_RATE_11M_INDEX) + cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; + if (lowest_present_cck > IWL_RATE_5M_INDEX) + cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; + if (lowest_present_cck > IWL_RATE_2M_INDEX) + cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; + /* 1M already there or needed so always add */ + cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE; + + *cck_rates = cpu_to_le32((u32)cck); + *ofdm_rates = cpu_to_le32((u32)ofdm); +} + +static void iwl_mld_fill_protection_flags(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + __le32 *protection_flags) +{ + u8 protection_mode = link->ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + u8 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT; + + IWL_DEBUG_RATE(mld, "HT protection mode: %d\n", protection_mode); + + if (link->use_cts_prot) + *protection_flags |= cpu_to_le32(LINK_PROT_FLG_TGG_PROTECT); + + /* See section 9.23.3.1 of IEEE 80211-2012. + * Nongreenfield HT STAs Present is not supported. + */ + switch (protection_mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + *protection_flags |= cpu_to_le32(ht_flag); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* Protect when channel wider than 20MHz */ + if (link->chanreq.oper.width > NL80211_CHAN_WIDTH_20) + *protection_flags |= cpu_to_le32(ht_flag); + break; + } +} + +static u8 iwl_mld_mac80211_ac_to_fw_ac(enum ieee80211_ac_numbers ac) +{ + static const u8 mac80211_ac_to_fw[] = { + AC_VO, + AC_VI, + AC_BE, + AC_BK + }; + + return mac80211_ac_to_fw[ac]; +} + +static void iwl_mld_fill_qos_params(struct ieee80211_bss_conf *link, + struct iwl_ac_qos *ac, __le32 *qos_flags) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + /* no need to check mld_link since it is done in the caller */ + + for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) { + u8 txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(mac_ac); + u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac); + + ac[fw_ac].cw_min = + cpu_to_le16(mld_link->queue_params[mac_ac].cw_min); + ac[fw_ac].cw_max = + cpu_to_le16(mld_link->queue_params[mac_ac].cw_max); + ac[fw_ac].edca_txop = + cpu_to_le16(mld_link->queue_params[mac_ac].txop * 32); + ac[fw_ac].aifsn = mld_link->queue_params[mac_ac].aifs; + ac[fw_ac].fifos_mask = BIT(txf); + } + + if (link->qos) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + + if (link->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); +} + +static bool iwl_mld_fill_mu_edca(struct iwl_mld *mld, + const struct iwl_mld_link *mld_link, + struct iwl_he_backoff_conf *trig_based_txf) +{ + for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) { + const struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = + &mld_link->queue_params[mac_ac].mu_edca_param_rec; + u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac); + + if (!mld_link->queue_params[mac_ac].mu_edca) + return false; + + trig_based_txf[fw_ac].cwmin = + cpu_to_le16(mu_edca->ecw_min_max & 0xf); + trig_based_txf[fw_ac].cwmax = + cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); + trig_based_txf[fw_ac].aifsn = + cpu_to_le16(mu_edca->aifsn & 0xf); + trig_based_txf[fw_ac].mu_time = + cpu_to_le16(mu_edca->mu_edca_timer); + } + return true; +} + +static u8 iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw) +{ + switch (bw) { + default: /* potential future values not supported by this hw/driver */ + case IEEE80211_STA_RX_BW_20: + return IWL_LINK_MODIFY_BW_20; + case IEEE80211_STA_RX_BW_40: + return IWL_LINK_MODIFY_BW_40; + case IEEE80211_STA_RX_BW_80: + return IWL_LINK_MODIFY_BW_80; + case IEEE80211_STA_RX_BW_160: + return IWL_LINK_MODIFY_BW_160; + case IEEE80211_STA_RX_BW_320: + return IWL_LINK_MODIFY_BW_320; + } +} + +static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw, + u32 changes) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct ieee80211_vif *vif = link->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *chan_ctx; + struct iwl_link_config_cmd cmd = {}; + u32 flags = 0; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(mld_link->fw_id); + cmd.spec_link_id = link->link_id; + cmd.mac_id = cpu_to_le32(mld_vif->fw_id); + + chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + + cmd.phy_id = cpu_to_le32(chan_ctx ? + iwl_mld_phy_from_mac80211(chan_ctx)->fw_id : + FW_CTXT_ID_INVALID); + + ether_addr_copy(cmd.local_link_addr, link->addr); + + cmd.active = cpu_to_le32(mld_link->active); + + if ((changes & LINK_CONTEXT_MODIFY_ACTIVE) && !mld_link->active && + mld_link->silent_deactivation) { + /* We are de-activating a link that is having CSA with + * immediate quiet in EMLSR. Tell the firmware not to send any + * frame. + */ + cmd.block_tx = 1; + mld_link->silent_deactivation = false; + } + + if (vif->type == NL80211_IFTYPE_ADHOC && link->bssid) + ether_addr_copy(cmd.ibss_bssid_addr, link->bssid); + + /* Channel context is needed to get the rates */ + if (chan_ctx) + iwl_mld_fill_rates(mld, link, chan_ctx, &cmd.cck_rates, + &cmd.ofdm_rates); + + cmd.cck_short_preamble = cpu_to_le32(link->use_short_preamble); + cmd.short_slot = cpu_to_le32(link->use_short_slot); + + iwl_mld_fill_protection_flags(mld, link, &cmd.protection_flags); + + iwl_mld_fill_qos_params(link, cmd.ac, &cmd.qos_flags); + + cmd.bi = cpu_to_le32(link->beacon_int); + cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period); + + if (changes & LINK_CONTEXT_MODIFY_BANDWIDTH) + cmd.modify_bandwidth = iwl_mld_sta_rx_bw_to_fw(bw); + + /* Configure HE parameters only if HE is supported, and only after + * the parameters are set in mac80211 (meaning after assoc) + */ + if (!link->he_support || iwlwifi_mod_params.disable_11ax || + (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) { + changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS; + goto send_cmd; + } + + /* ap_sta may be NULL if we're disconnecting */ + if (mld_vif->ap_sta) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_check(mld_vif->ap_sta, + link->link_id); + + if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he && + link_sta->he_cap.he_cap_elem.mac_cap_info[5] & + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX) + cmd.ul_mu_data_disable = 1; + } + + cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext; + + if (link->uora_exists) { + cmd.rand_alloc_ecwmin = link->uora_ocw_range & 0x7; + cmd.rand_alloc_ecwmax = (link->uora_ocw_range >> 3) & 0x7; + } + + if (iwl_mld_fill_mu_edca(mld, mld_link, cmd.trig_based_txf)) + flags |= LINK_FLG_MU_EDCA_CW; + + cmd.bss_color = link->he_bss_color.color; + + if (!link->he_bss_color.enabled) + flags |= LINK_FLG_BSS_COLOR_DIS; + + cmd.frame_time_rts_th = cpu_to_le16(link->frame_time_rts_th); + + /* Block 26-tone RU OFDMA transmissions */ + if (mld_link->he_ru_2mhz_block) + flags |= LINK_FLG_RU_2MHZ_BLOCK; + + if (link->nontransmitted) { + ether_addr_copy(cmd.ref_bssid_addr, link->transmitter_bssid); + cmd.bssid_index = link->bssid_index; + } + + /* The only EHT parameter is puncturing, and starting from PHY cmd + * version 6 - it is sent there. For older versions of the PHY cmd, + * puncturing is not needed at all. + */ + if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS)) + changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + +send_cmd: + cmd.modify_mask = cpu_to_le32(changes); + cmd.flags = cpu_to_le32(flags); + + return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY); +} + +int iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + u32 changes) +{ + if (WARN_ON(changes & LINK_CONTEXT_MODIFY_BANDWIDTH)) + changes &= ~LINK_CONTEXT_MODIFY_BANDWIDTH; + + return _iwl_mld_change_link_in_fw(mld, link, 0, changes); +} + +int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw) +{ + return _iwl_mld_change_link_in_fw(mld, link, bw, + LINK_CONTEXT_MODIFY_BANDWIDTH); +} + +int iwl_mld_activate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link || mld_link->active)) + return -EINVAL; + + mld_link->rx_omi.exit_ts = jiffies; + mld_link->active = true; + + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_ACTIVE); + if (ret) + mld_link->active = false; + + return ret; +} + +void iwl_mld_deactivate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_probe_resp_data *probe_data; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link || !mld_link->active)) + return; + + iwl_mld_cancel_session_protection(mld, link->vif, link->link_id); + + /* If we deactivate the link, we will probably remove it, or switch + * channel. In both cases, the CSA or Notice of Absence information is + * now irrelevant. Remove the data here. + */ + probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); + RCU_INIT_POINTER(mld_link->probe_resp_data, NULL); + if (probe_data) + kfree_rcu(probe_data, rcu_head); + + mld_link->active = false; + + iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE); + + /* Now that the link is not active in FW, we don't expect any new + * notifications for it. Cancel the ones that are already pending + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK, + mld_link->fw_id); +} + +static int +iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_link_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(mld_link->fw_id); + cmd.spec_link_id = link->link_id; + cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); + + return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); +} + +static void iwl_mld_omi_bw_update(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + struct iwl_mld_link *mld_link, + struct ieee80211_link_sta *link_sta, + enum ieee80211_sta_rx_bandwidth bw, + bool ap_update) +{ + enum ieee80211_sta_rx_bandwidth apply_bw; + + mld_link->rx_omi.desired_bw = bw; + + /* Can't update OMI while already in progress, desired_bw was + * set so on FW notification the worker will see the change + * and apply new the new desired bw. + */ + if (mld_link->rx_omi.bw_in_progress) + return; + + if (bw == IEEE80211_STA_RX_BW_MAX) + apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); + else + apply_bw = bw; + + if (!ap_update) { + /* The update isn't due to AP tracking after leaving OMI, + * where the AP could increase BW and then we must tell + * it that we can do the increased BW as well, if we did + * update the chandef. + * In this case, if we want MAX, then we will need to send + * a new OMI to the AP if it increases its own bandwidth as + * we can (due to internal and FW limitations, and being + * worried the AP might break) only send to what we're doing + * at the moment. In this case, set last_max_bw; otherwise + * if we really want to decrease our bandwidth set it to 0 + * to indicate no updates are needed if the AP changes. + */ + if (bw != IEEE80211_STA_RX_BW_MAX) + mld_link->rx_omi.last_max_bw = apply_bw; + else + mld_link->rx_omi.last_max_bw = 0; + } else { + /* Otherwise, if we're already trying to do maximum and + * the AP is changing, set last_max_bw to the new max the + * AP is using, we'll only get to this code path if the + * new bandwidth of the AP is bigger than what we sent it + * previously. This avoids repeatedly sending updates if + * it changes bandwidth, only doing it once on an increase. + */ + mld_link->rx_omi.last_max_bw = apply_bw; + } + + if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) { + mld_link->rx_omi.bw_in_progress = apply_bw; + iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw); + } +} + +static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = + container_of(work, typeof(*mld_link), rx_omi.finished_work.work); + enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw; + struct ieee80211_vif *vif = mld_link->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + + if (!mld_vif->ap_sta) + return; + + link_sta = wiphy_dereference(mld->wiphy, + mld_vif->ap_sta->link[mld_link->link_id]); + if (WARN_ON_ONCE(!link_sta)) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (WARN_ON(!mld_link->rx_omi.bw_in_progress)) + return; + + desired_bw = mld_link->rx_omi.desired_bw; + switched_to_bw = mld_link->rx_omi.bw_in_progress; + + ieee80211_finalize_rx_omi_bw(link_sta); + mld_link->rx_omi.bw_in_progress = 0; + + if (desired_bw != switched_to_bw) + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, + desired_bw, false); +} + +static struct ieee80211_vif * +iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld, + struct ieee80211_link_sta **link_sta, + struct iwl_mld_link **mld_link) +{ + struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + int n_link_stas = 0; + + *link_sta = NULL; + + if (mld->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_SC) + return NULL; + + vif = iwl_mld_get_bss_vif(mld); + if (!vif) + return NULL; + + for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) { + struct ieee80211_link_sta *tmp; + + tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(tmp)) + continue; + + n_link_stas++; + *link_sta = tmp; + } + + /* can't do anything if we have TDLS peers or EMLSR */ + if (n_link_stas != 1) + return NULL; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + *mld_link = iwl_mld_link_dereference_check(mld_vif, + (*link_sta)->link_id); + if (WARN_ON(!*mld_link)) + return NULL; + + return vif; +} + +void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + enum ieee80211_sta_rx_bandwidth bw) +{ + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + if (WARN_ON(link_conf->vif != vif)) + return; + + /* This is 0 if we requested an OMI BW reduction and don't want to + * be sending an OMI when the AP's bandwidth changes. + */ + if (!mld_link->rx_omi.last_max_bw) + return; + + /* We only need to tell the AP if it increases BW over what we last + * told it we were using, if it reduces then our last OMI to it will + * not get used anyway (e.g. we said we want 160 but it's doing 80.) + */ + if (bw < mld_link->rx_omi.last_max_bw) + return; + + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true); +} + +void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n")) + return; + + if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress, + "OMI notification when not requested\n")) + return; + + wiphy_delayed_work_queue(mld->hw->wiphy, + &mld_link->rx_omi.finished_work, + msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY)); +} + +void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld) +{ + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (!link_conf->he_support) + return; + + mld_link->rx_omi.exit_ts = jiffies; + + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, + IEEE80211_STA_RX_BW_MAX, false); +} + +void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld) +{ + enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX; + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct cfg80211_chan_def chandef; + struct iwl_mld_link *mld_link; + struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + struct iwl_mld_phy *phy; + u16 punctured; + int exit_thr; + + /* not allowed in CAM mode */ + if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + return; + + /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */ + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (!link_conf->he_support) + return; + + chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + if (WARN_ON(!chanctx)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + if (!mld_vif->authorized) + goto apply; + + /* must not be in low-latency mode */ + if (iwl_mld_vif_low_latency(mld_vif)) + goto apply; + + chandef = link_conf->chanreq.oper; + + switch (chandef.width) { + case NL80211_CHAN_WIDTH_320: + exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320; + break; + case NL80211_CHAN_WIDTH_160: + exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160; + break; + default: + /* since we reduce to 80 MHz, must have more to start with */ + goto apply; + } + + /* not to be done if primary 80 MHz is punctured */ + if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80, + &punctured) < 0 || + punctured != 0) + goto apply; + + phy = iwl_mld_phy_from_mac80211(chanctx); + + if (phy->channel_load_by_us > exit_thr) { + /* send OMI for max bandwidth */ + goto apply; + } + + if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) { + /* no changes between enter/exit thresholds */ + return; + } + + if (time_is_before_jiffies(mld_link->rx_omi.exit_ts + + msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) + return; + + /* reduce bandwidth to 80 MHz to save power */ + bw = IEEE80211_STA_RX_BW_80; +apply: + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false); +} + +IWL_MLD_ALLOC_FN(link, bss_conf) + +/* Constructor function for struct iwl_mld_link */ +static int +iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link, + struct iwl_mld_link *mld_link) +{ + mld_link->vif = link->vif; + mld_link->link_id = link->link_id; + + iwl_mld_init_internal_sta(&mld_link->bcast_sta); + iwl_mld_init_internal_sta(&mld_link->mcast_sta); + iwl_mld_init_internal_sta(&mld_link->aux_sta); + + wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, + iwl_mld_omi_bw_finished_work); + + return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); +} + +/* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and + * adds a link to the fw + */ +int iwl_mld_add_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = bss_conf == &bss_conf->vif->bss_conf; + int ret; + + if (!link) { + if (is_deflink) + link = &mld_vif->deflink; + else + link = kzalloc(sizeof(*link), GFP_KERNEL); + } else { + WARN_ON(!mld->fw_status.in_hw_restart); + } + + ret = iwl_mld_init_link(mld, bss_conf, link); + if (ret) + goto free; + + rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link); + + ret = iwl_mld_add_link_to_fw(mld, bss_conf); + if (ret) { + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + goto free; + } + + return ret; + +free: + if (!is_deflink) + kfree(link); + return ret; +} + +/* Remove link from fw, unmap the bss_conf, and destroy the link structure */ +int iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = link == &mld_vif->deflink; + int ret; + + if (WARN_ON(!link || link->active)) + return -EINVAL; + + ret = iwl_mld_rm_link_from_fw(mld, bss_conf); + /* Continue cleanup on failure */ + + if (!is_deflink) + kfree_rcu(link, rcu_head); + + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + + wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work); + + if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) + return -EINVAL; + + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); + + return ret; +} + +void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data; + union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; + u32 link_id = le32_to_cpu(notif->link_id); + u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons); + u32 missed_bcon_since_rx = + le32_to_cpu(notif->consec_missed_beacons_since_last_rx); + u32 scnd_lnk_bcn_lost = + le32_to_cpu(notif->consec_missed_beacons_other_link); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, link_id); + u32 bss_param_ch_cnt_link_id; + struct ieee80211_vif *vif; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id; + + IWL_DEBUG_INFO(mld, + "missed bcn link_id=%u, %u consecutive=%u\n", + link_id, missed_bcon, missed_bcon_since_rx); + + if (WARN_ON(!vif)) + return; + + mld->trans->dbg.dump_file_name_ext_valid = true; + snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "LinkId_%d_MacType_%d", link_id, + iwl_mld_mac80211_iftype_to_fw(vif)); + + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data); + + if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) { + if (missed_bcon_since_rx >= + IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) { + ieee80211_connection_loss(vif); + return; + } + IWL_WARN(mld, + "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n"); + return; + } + + if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) { + ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); + + /* try to switch links, no-op if we don't have MLO */ + iwl_mld_trigger_link_selection(mld, vif); + } + + /* no more logic if we're not in EMLSR */ + if (hweight16(vif->active_links) <= 1) + return; + + /* We are processing a notification before link activation */ + if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID) + return; + + /* Exit EMLSR if we lost more than + * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links + * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link. + * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED + * on current link and the link's bss_param_ch_count has changed on + * the other link's beacon. + */ + if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && + scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) || + missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH || + (bss_param_ch_cnt_link_id != link_id && + missed_bcon >= + IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON, + iwl_mld_get_primary_link(vif)); + } +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif); + +bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 removed_link_id) +{ + struct iwl_missed_beacons_notif *notif = (void *)pkt->data; + + if (le32_to_cpu(notif->other_link_id) == removed_link_id) { + /* Second link is being removed. Don't cancel the notification, + * but mark second link as invalid. + */ + notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID); + } + + /* If the primary link is removed, cancel the notification */ + return le32_to_cpu(notif->link_id) == removed_link_id; +} + +int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL & + ~(LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_EHT_PARAMS)); +} + +struct iwl_mld_rssi_to_grade { + s8 rssi[2]; + u16 grade; +}; + +#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \ + { \ + .rssi = {_lb, _hb_uhb}, \ + .grade = _grade \ + } + +/* + * This array must be sorted by increasing RSSI for proper functionality. + * The grades are actually estimated throughput, represented as fixed-point + * with a scale factor of 1/10. + */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = { + RSSI_TO_GRADE_LINE(-85, -89, 172), + RSSI_TO_GRADE_LINE(-83, -86, 344), + RSSI_TO_GRADE_LINE(-82, -85, 516), + RSSI_TO_GRADE_LINE(-80, -83, 688), + RSSI_TO_GRADE_LINE(-77, -79, 1032), + RSSI_TO_GRADE_LINE(-73, -76, 1376), + RSSI_TO_GRADE_LINE(-70, -74, 1548), + RSSI_TO_GRADE_LINE(-69, -72, 1720), + RSSI_TO_GRADE_LINE(-65, -68, 2064), + RSSI_TO_GRADE_LINE(-61, -66, 2294), + RSSI_TO_GRADE_LINE(-58, -61, 2580), + RSSI_TO_GRADE_LINE(-55, -58, 2868), + RSSI_TO_GRADE_LINE(-46, -55, 3098), + RSSI_TO_GRADE_LINE(-43, -54, 3442) +}; + +#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade) + +#define DEFAULT_CHAN_LOAD_2GHZ 30 +#define DEFAULT_CHAN_LOAD_5GHZ 15 +#define DEFAULT_CHAN_LOAD_6GHZ 0 + +/* Factors calculation is done with fixed-point with a scaling factor of 1/256 */ +#define SCALE_FACTOR 256 +#define MAX_CHAN_LOAD 256 + +static unsigned int +iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_chan_width chan_width = + link_conf->chanreq.oper.width; + int mhz = nl80211_chan_width_to_mhz(chan_width); + unsigned int n_subchannels; + + if (WARN_ONCE(mhz < 20 || mhz > 320, + "Invalid channel width : (%d)\n", mhz)) + return 1; + + /* total number of subchannels */ + n_subchannels = mhz / 20; + + /* No puncturing if less than 80 MHz */ + if (mhz >= 80) + n_subchannels -= hweight16(link_conf->chanreq.oper.punctured); + + return n_subchannels; +} + +static int +iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + const struct cfg80211_bss_ies *ies; + const struct element *bss_load_elem = NULL; + const struct ieee80211_bss_load_elem *bss_load; + + guard(rcu)(); + + if (ieee80211_vif_link_active(vif, link_conf->link_id)) + ies = rcu_dereference(link_conf->bss->beacon_ies); + else + ies = rcu_dereference(link_conf->bss->ies); + + if (ies) + bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD, + ies->data, ies->len); + + if (!bss_load_elem || + bss_load_elem->datalen != sizeof(*bss_load)) + return -EINVAL; + + bss_load = (const void *)bss_load_elem->data; + + return bss_load->channel_util; +} + +static unsigned int +iwl_mld_get_chan_load_by_us(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + struct ieee80211_chanctx_conf *chan_ctx; + struct iwl_mld_phy *phy; + + if (!mld_link || !mld_link->active) { + WARN_ON(expect_active_link); + return 0; + } + + if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx), + "Active link (%u) without channel ctxt assigned!\n", + link_conf->link_id)) + return 0; + + chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + phy = iwl_mld_phy_from_mac80211(chan_ctx); + + return phy->channel_load_by_us; +} + +/* Returns error if the channel utilization element is invalid/unavailable */ +int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link) +{ + int chan_load; + unsigned int chan_load_by_us; + + /* get overall load */ + chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf); + if (chan_load < 0) + return chan_load; + + chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf, + expect_active_link); + + /* channel load by us is given in percentage */ + chan_load_by_us = + NORMALIZE_PERCENT_TO_255(chan_load_by_us); + + /* Use only values that firmware sends that can possibly be valid */ + if (chan_load_by_us <= chan_load) + chan_load -= chan_load_by_us; + + return chan_load; +} + +static unsigned int +iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_band band = link_conf->chanreq.oper.chan->band; + + switch (band) { + case NL80211_BAND_2GHZ: + return DEFAULT_CHAN_LOAD_2GHZ; + case NL80211_BAND_5GHZ: + return DEFAULT_CHAN_LOAD_5GHZ; + case NL80211_BAND_6GHZ: + return DEFAULT_CHAN_LOAD_6GHZ; + default: + WARN_ON(1); + return 0; + } +} + +unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + int chan_load; + + chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false); + if (chan_load >= 0) + return chan_load; + + /* No information from the element, take the defaults */ + chan_load = iwl_mld_get_default_chan_load(link_conf); + + /* The defaults are given in percentage */ + return NORMALIZE_PERCENT_TO_255(chan_load); +} + +static unsigned int +iwl_mld_get_avail_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf); +} + +/* This function calculates the grade of a link. Returns 0 in error case */ +unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_band band; + int rssi_idx; + s32 link_rssi; + unsigned int grade = MAX_GRADE; + + if (WARN_ON_ONCE(!link_conf)) + return 0; + + band = link_conf->chanreq.oper.chan->band; + if (WARN_ONCE(band != NL80211_BAND_2GHZ && + band != NL80211_BAND_5GHZ && + band != NL80211_BAND_6GHZ, + "Invalid band (%u)\n", band)) + return 0; + + link_rssi = MBM_TO_DBM(link_conf->bss->signal); + /* + * For 6 GHz the RSSI of the beacons is lower than + * the RSSI of the data. + */ + if (band == NL80211_BAND_6GHZ && link_rssi) + link_rssi += 4; + + rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1; + + /* No valid RSSI - take the lowest grade */ + if (!link_rssi) + link_rssi = rssi_to_grade_map[0].rssi[rssi_idx]; + + IWL_DEBUG_EHT(mld, + "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n", + link_conf->link_id, band, + link_conf->chanreq.oper.width, + link_conf->chanreq.oper.punctured, link_rssi); + + /* Get grade based on RSSI */ + for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { + const struct iwl_mld_rssi_to_grade *line = + &rssi_to_grade_map[i]; + + if (link_rssi > line->rssi[rssi_idx]) + continue; + grade = line->grade; + break; + } + + /* Apply the channel load and puncturing factors */ + grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR; + grade = grade * iwl_mld_get_n_subchannels(link_conf); + + IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade); + + return grade; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h new file mode 100644 index 000000000000..3a4f3ac990d7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_link_h__ +#define __iwl_mld_link_h__ + +#include + +#include "mld.h" +#include "sta.h" + +/** + * struct iwl_probe_resp_data - data for NoA/CSA updates + * @rcu_head: used for freeing the data on update + * @notif: notification data + * @noa_len: length of NoA attribute, calculated from the notification + */ +struct iwl_probe_resp_data { + struct rcu_head rcu_head; + struct iwl_probe_resp_data_notif notif; + int noa_len; +}; + +/** + * struct iwl_mld_link - link configuration parameters + * + * @rcu_head: RCU head for freeing this data. + * @fw_id: the fw id of the link. + * @active: if the link is active or not. + * @queue_params: QoS data from mac80211. This is updated with a call to + * drv_conf_tx per each AC, and then notified once with BSS_CHANGED_QOS. + * So we store it here and then send one link cmd for all the ACs. + * @chan_ctx: pointer to the channel context assigned to the link. If a link + * has an assigned channel context it means that it is active. + * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked. + * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two. + * This tracks the one IGTK that currently exists in FW. + * @vif: the vif this link belongs to + * @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. + * @aux_sta: station used for remain on channel. Used in P2P device. + * @link_id: over the air link ID + * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs, + * but higher layers work differently, so we store the keys here for + * later installation. + * @silent_deactivation: next deactivation needs to be silent. + * @probe_resp_data: data from FW notification to store NOA related data to be + * inserted into probe response. + * @rx_omi: data for BW reduction with OMI + * @rx_omi.bw_in_progress: update is in progress (indicates target BW) + * @rx_omi.exit_ts: timestamp of last exit + * @rx_omi.finished_work: work for the delayed reaction to the firmware saying + * the change was applied, and for then applying a new mode if it was + * updated while waiting for firmware/AP settle delay. + * @rx_omi.desired_bw: desired bandwidth + * @rx_omi.last_max_bw: last maximum BW used by firmware, for AP BW changes + */ +struct iwl_mld_link { + struct rcu_head rcu_head; + + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + bool active; + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct ieee80211_chanctx_conf __rcu *chan_ctx; + bool he_ru_2mhz_block; + struct ieee80211_key_conf *igtk; + ); + /* And here fields that survive a fw restart */ + struct ieee80211_vif *vif; + struct iwl_mld_int_sta bcast_sta; + struct iwl_mld_int_sta mcast_sta; + struct iwl_mld_int_sta aux_sta; + u8 link_id; + + struct { + struct wiphy_delayed_work finished_work; + unsigned long exit_ts; + enum ieee80211_sta_rx_bandwidth bw_in_progress, + desired_bw, + last_max_bw; + } rx_omi; + + /* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */ + struct ieee80211_key_conf *ap_early_keys[6]; + bool silent_deactivation; + struct iwl_probe_resp_data __rcu *probe_resp_data; +}; + +/* Cleanup function for struct iwl_mld_phy, will be called in restart */ +static inline void +iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) +{ + struct iwl_probe_resp_data *probe_data; + + probe_data = wiphy_dereference(mld->wiphy, link->probe_resp_data); + RCU_INIT_POINTER(link->probe_resp_data, NULL); + if (probe_data) + kfree_rcu(probe_data, rcu_head); + + CLEANUP_STRUCT(link); + if (link->bcast_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->bcast_sta); + if (link->mcast_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->mcast_sta); + if (link->aux_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->aux_sta); +} + +/* Convert a percentage from [0,100] to [0,255] */ +#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100) + +int iwl_mld_add_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf); +int iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf); +int iwl_mld_activate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link); +void iwl_mld_deactivate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link); +int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw); +int iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, u32 changes); +void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 removed_link_id); +int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); + +unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); + +int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link); +void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld); +void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld); +void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + enum ieee80211_sta_rx_bandwidth bw); + +#endif /* __iwl_mld_link_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c new file mode 100644 index 000000000000..439fc10a4a41 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mld.h" +#include "iface.h" +#include "low_latency.h" +#include "hcmd.h" +#include "power.h" + +#define MLD_LL_WK_INTERVAL_MSEC 500 +#define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000) +#define MLD_LL_ACTIVE_WK_PERIOD (HZ * 10) + +/* packets/MLD_LL_WK_PERIOD seconds */ +#define MLD_LL_ENABLE_THRESH 100 + +static bool iwl_mld_calc_low_latency(struct iwl_mld *mld, + unsigned long timestamp) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + bool global_low_latency = false; + u8 num_rx_q = mld->trans->num_rx_queues; + + for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) { + u32 total_vo_vi_pkts = 0; + bool ll_period_expired; + + /* If it's not initialized yet, it means we have not yet + * received/transmitted any vo/vi packet on this MAC. + */ + if (!ll->window_start[mac_id]) + continue; + + ll_period_expired = + time_after(timestamp, ll->window_start[mac_id] + + MLD_LL_ACTIVE_WK_PERIOD); + + if (ll_period_expired) + ll->window_start[mac_id] = timestamp; + + for (int q = 0; q < num_rx_q; q++) { + struct iwl_mld_low_latency_packets_counters *counters = + &mld->low_latency.pkts_counters[q]; + + spin_lock_bh(&counters->lock); + + total_vo_vi_pkts += counters->vo_vi[mac_id]; + + if (ll_period_expired) + counters->vo_vi[mac_id] = 0; + + spin_unlock_bh(&counters->lock); + } + + /* enable immediately with enough packets but defer + * disabling only if the low-latency period expired and + * below threshold. + */ + if (total_vo_vi_pkts > MLD_LL_ENABLE_THRESH) + mld->low_latency.result[mac_id] = true; + else if (ll_period_expired) + mld->low_latency.result[mac_id] = false; + + global_low_latency |= mld->low_latency.result[mac_id]; + } + + return global_low_latency; +} + +static void iwl_mld_low_latency_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + 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))) + return; + + low_latency = mld->low_latency.result[mld_vif->fw_id]; + + if (prev != low_latency) + iwl_mld_vif_update_low_latency(mld, vif, low_latency, + LOW_LATENCY_TRAFFIC); +} + +static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + low_latency.work.work); + unsigned long timestamp = jiffies; + bool low_latency_active; + + if (mld->fw_status.in_hw_restart) + return; + + /* It is assumed that the work was scheduled only after checking + * at least MLD_LL_PERIOD has passed since the last update. + */ + + low_latency_active = iwl_mld_calc_low_latency(mld, timestamp); + + /* Update the timestamp now after the low-latency calculation */ + mld->low_latency.timestamp = timestamp; + + /* If low-latency is active we need to force re-evaluation after + * 10 seconds, so that we can disable low-latency when + * the low-latency traffic ends. + * + * Otherwise, we don't need to run the work because there is nothing to + * disable. + * + * Note that this has no impact on the regular scheduling of the + * updates triggered by traffic - those happen whenever the + * MLD_LL_PERIOD timeout expire. + */ + if (low_latency_active) + wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, + MLD_LL_ACTIVE_WK_PERIOD); + + ieee80211_iterate_active_interfaces(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_low_latency_iter, mld); +} + +int iwl_mld_low_latency_init(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + unsigned long ts = jiffies; + + ll->pkts_counters = kcalloc(mld->trans->num_rx_queues, + sizeof(*ll->pkts_counters), GFP_KERNEL); + if (!ll->pkts_counters) + return -ENOMEM; + + for (int q = 0; q < mld->trans->num_rx_queues; q++) + spin_lock_init(&ll->pkts_counters[q].lock); + + wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk); + + ll->timestamp = ts; + + /* The low-latency window_start will be initialized per-MAC on + * the first vo/vi packet received/transmitted. + */ + + return 0; +} + +void iwl_mld_low_latency_free(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + + kfree(ll->pkts_counters); + ll->pkts_counters = NULL; +} + +void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + + ll->timestamp = jiffies; + + memset(ll->window_start, 0, sizeof(ll->window_start)); + memset(ll->result, 0, sizeof(ll->result)); + + for (int q = 0; q < mld->trans->num_rx_queues; q++) + memset(ll->pkts_counters[q].vo_vi, 0, + sizeof(ll->pkts_counters[q].vo_vi)); +} + +static int iwl_mld_send_low_latency_cmd(struct iwl_mld *mld, bool low_latency, + u16 mac_id) +{ + struct iwl_mac_low_latency_cmd cmd = { + .mac_id = cpu_to_le32(mac_id) + }; + u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD); + int ret; + + if (low_latency) { + /* Currently we don't care about the direction */ + cmd.low_latency_rx = 1; + cmd.low_latency_tx = 1; + } + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send low latency command\n"); + + return ret; +} + +static void iwl_mld_vif_set_low_latency(struct iwl_mld_vif *mld_vif, bool set, + enum iwl_mld_low_latency_cause cause) +{ + if (set) + mld_vif->low_latency_causes |= cause; + else + mld_vif->low_latency_causes &= ~cause; +} + +void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency, + enum iwl_mld_low_latency_cause cause) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool prev; + + prev = iwl_mld_vif_low_latency(mld_vif); + iwl_mld_vif_set_low_latency(mld_vif, low_latency, cause); + + low_latency = iwl_mld_vif_low_latency(mld_vif); + if (low_latency == prev) + return; + + if (iwl_mld_send_low_latency_cmd(mld, low_latency, mld_vif->fw_id)) { + /* revert to previous low-latency state */ + iwl_mld_vif_set_low_latency(mld_vif, prev, cause); + return; + } + + if (low_latency) + iwl_mld_leave_omi_bw_reduction(mld); + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT) + return; + + iwl_mld_update_mac_power(mld, vif, false); +} + +static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr) +{ + u8 tid; + static const u8 tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, + }; + + if (!hdr || !ieee80211_is_data_qos(hdr->frame_control)) + return false; + + tid = ieee80211_get_tid(hdr); + if (tid >= IWL_MAX_TID_COUNT) + return false; + + return tid_to_mac80211_ac[tid] < IEEE80211_AC_VI; +} + +void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, + struct ieee80211_hdr *hdr, + struct ieee80211_sta *sta, + u8 queue) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + struct iwl_mld_low_latency_packets_counters *counters; + unsigned long ts = jiffies ? jiffies : 1; + u8 fw_id = mld_vif->fw_id; + + /* we should have failed op mode init if NULL */ + if (WARN_ON_ONCE(!mld->low_latency.pkts_counters)) + return; + + if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || + queue >= mld->trans->num_rx_queues)) + return; + + if (mld->low_latency.stopped) + return; + + if (!iwl_mld_is_vo_vi_pkt(hdr)) + return; + + counters = &mld->low_latency.pkts_counters[queue]; + + spin_lock_bh(&counters->lock); + counters->vo_vi[fw_id]++; + spin_unlock_bh(&counters->lock); + + /* Initialize the window_start on the first vo/vi packet */ + if (!mld->low_latency.window_start[fw_id]) + mld->low_latency.window_start[fw_id] = ts; + + if (time_is_before_jiffies(mld->low_latency.timestamp + MLD_LL_PERIOD)) + wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, + 0); +} + +void iwl_mld_low_latency_stop(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + mld->low_latency.stopped = true; + + wiphy_delayed_work_cancel(mld->wiphy, &mld->low_latency.work); +} + +void iwl_mld_low_latency_restart(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + bool low_latency = false; + unsigned long ts = jiffies; + + lockdep_assert_wiphy(mld->wiphy); + + ll->timestamp = ts; + mld->low_latency.stopped = false; + + for (int mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) { + ll->window_start[mac] = 0; + low_latency |= ll->result[mac]; + + for (int q = 0; q < mld->trans->num_rx_queues; q++) { + spin_lock_bh(&ll->pkts_counters[q].lock); + ll->pkts_counters[q].vo_vi[mac] = 0; + spin_unlock_bh(&ll->pkts_counters[q].lock); + } + } + + /* if low latency is active, force re-evaluation to cover the case of + * no traffic. + */ + if (low_latency) + wiphy_delayed_work_queue(mld->wiphy, &ll->work, MLD_LL_PERIOD); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h new file mode 100644 index 000000000000..f59684d235af --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_low_latency_h__ +#define __iwl_mld_low_latency_h__ + +/** + * struct iwl_mld_low_latency_packets_counters - Packets counters + * @lock: synchronize the counting in data path against the worker + * @vo_vi: per-mac, counts the number of TX and RX voice and video packets + */ +struct iwl_mld_low_latency_packets_counters { + spinlock_t lock; + u32 vo_vi[NUM_MAC_INDEX_DRIVER]; +} ____cacheline_aligned_in_smp; + +/** + * enum iwl_mld_low_latency_cause - low-latency set causes + * + * @LOW_LATENCY_TRAFFIC: indicates low-latency traffic was detected + * @LOW_LATENCY_DEBUGFS: low-latency mode set from debugfs + * @LOW_LATENCY_VIF_TYPE: low-latency mode set because of vif type (AP) + */ +enum iwl_mld_low_latency_cause { + LOW_LATENCY_TRAFFIC = BIT(0), + LOW_LATENCY_DEBUGFS = BIT(1), + LOW_LATENCY_VIF_TYPE = BIT(2), +}; + +/** + * struct iwl_mld_low_latency - Manage low-latency detection and activation. + * @work: this work is used to detect low-latency by monitoring the number of + * voice and video packets transmitted in a period of time. If the + * threshold is reached, low-latency is activated. When active, + * it is deactivated if the threshold is not reached within a + * 10-second period. + * @timestamp: timestamp of the last update. + * @window_start: per-mac, timestamp of the start of the current window. when + * the window is over, the counters are reset. + * @pkts_counters: per-queue array voice/video packet counters + * @result: per-mac latest low-latency result + * @stopped: if true, ignore the requests to update the counters + */ +struct iwl_mld_low_latency { + struct wiphy_delayed_work work; + unsigned long timestamp; + unsigned long window_start[NUM_MAC_INDEX_DRIVER]; + struct iwl_mld_low_latency_packets_counters *pkts_counters; + bool result[NUM_MAC_INDEX_DRIVER]; + bool stopped; +}; + +int iwl_mld_low_latency_init(struct iwl_mld *mld); +void iwl_mld_low_latency_free(struct iwl_mld *mld); +void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld); +void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency, + enum iwl_mld_low_latency_cause cause); +void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, + struct ieee80211_hdr *hdr, + struct ieee80211_sta *sta, + u8 queue); +void iwl_mld_low_latency_stop(struct iwl_mld *mld); +void iwl_mld_low_latency_restart(struct iwl_mld *mld); + +#endif /* __iwl_mld_low_latency_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c new file mode 100644 index 000000000000..d73bd0179f7d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -0,0 +1,2671 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include +#include + +#include "mld.h" +#include "mac80211.h" +#include "phy.h" +#include "iface.h" +#include "power.h" +#include "sta.h" +#include "agg.h" +#include "scan.h" +#include "d3.h" +#include "tlc.h" +#include "key.h" +#include "ap.h" +#include "tx.h" +#include "roc.h" +#include "mlo.h" +#include "stats.h" +#include "ftm-initiator.h" +#include "low_latency.h" +#include "fw/api/scan.h" +#include "fw/api/context.h" +#include "fw/api/filter.h" +#include "fw/api/sta.h" +#include "fw/api/tdls.h" +#ifdef CONFIG_PM_SLEEP +#include "fw/api/d3.h" +#endif /* CONFIG_PM_SLEEP */ +#include "iwl-trans.h" + +#define IWL_MLD_LIMITS(ap) \ + { \ + .max = 2, \ + .types = BIT(NL80211_IFTYPE_STATION), \ + }, \ + { \ + .max = 1, \ + .types = ap | \ + BIT(NL80211_IFTYPE_P2P_CLIENT) | \ + BIT(NL80211_IFTYPE_P2P_GO), \ + }, \ + { \ + .max = 1, \ + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), \ + } + +static const struct ieee80211_iface_limit iwl_mld_limits[] = { + IWL_MLD_LIMITS(0) +}; + +static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = { + IWL_MLD_LIMITS(BIT(NL80211_IFTYPE_AP)) +}; + +static const struct ieee80211_iface_combination +iwl_mld_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 4, + .limits = iwl_mld_limits, + .n_limits = ARRAY_SIZE(iwl_mld_limits), + }, + { + .num_different_channels = 1, + .max_interfaces = 4, + .limits = iwl_mld_limits_ap, + .n_limits = ARRAY_SIZE(iwl_mld_limits_ap), + }, +}; + +static const u8 if_types_ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF | + WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB, + [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB, + [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, +}; + +#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \ + IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \ + __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ + IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ + __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) +#define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ + IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT) + +static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = { + { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = if_types_ext_capa_sta, + .extended_capabilities_mask = if_types_ext_capa_sta, + .extended_capabilities_len = sizeof(if_types_ext_capa_sta), + /* relevant only if EHT is supported */ + .eml_capabilities = IWL_MLD_EMLSR_CAPA, + .mld_capa_and_ops = IWL_MLD_CAPA_OPS, + }, +}; + +static void iwl_mld_hw_set_addresses(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + int num_addrs = 1; + + /* Extract MAC address */ + memcpy(mld->addresses[0].addr, mld->nvm_data->hw_addr, ETH_ALEN); + wiphy->addresses = mld->addresses; + wiphy->n_addresses = 1; + + /* Extract additional MAC addresses if available */ + if (mld->nvm_data->n_hw_addrs > 1) + num_addrs = min(mld->nvm_data->n_hw_addrs, + IWL_MLD_MAX_ADDRESSES); + + for (int i = 1; i < num_addrs; i++) { + memcpy(mld->addresses[i].addr, + mld->addresses[i - 1].addr, + ETH_ALEN); + mld->addresses[i].addr[ETH_ALEN - 1]++; + wiphy->n_addresses++; + } +} + +static void iwl_mld_hw_set_channels(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + struct ieee80211_supported_band *bands = mld->nvm_data->bands; + + wiphy->bands[NL80211_BAND_2GHZ] = &bands[NL80211_BAND_2GHZ]; + wiphy->bands[NL80211_BAND_5GHZ] = &bands[NL80211_BAND_5GHZ]; + + if (bands[NL80211_BAND_6GHZ].n_channels) + wiphy->bands[NL80211_BAND_6GHZ] = &bands[NL80211_BAND_6GHZ]; +} + +static void iwl_mld_hw_set_security(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + static const u32 mld_ciphers[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 + }; + + hw->wiphy->n_cipher_suites = ARRAY_SIZE(mld_ciphers); + hw->wiphy->cipher_suites = mld_ciphers; + + ieee80211_hw_set(hw, MFP_CAPABLE); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION); +} + +static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; +} + +static void iwl_mld_hw_set_antennas(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + + wiphy->available_antennas_tx = iwl_mld_get_valid_tx_ant(mld); + wiphy->available_antennas_rx = iwl_mld_get_valid_rx_ant(mld); +} + +static void iwl_mld_hw_set_pm(struct iwl_mld *mld) +{ +#ifdef CONFIG_PM_SLEEP + struct wiphy *wiphy = mld->wiphy; + + if (!device_can_wakeup(mld->trans->dev)) + return; + + mld->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE | + WIPHY_WOWLAN_NET_DETECT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + + mld->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; + mld->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; + mld->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; + mld->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES_V2; + + wiphy->wowlan = &mld->wowlan; +#endif /* CONFIG_PM_SLEEP */ +} + +static void iwl_mac_hw_set_radiotap(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US | + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ; + + /* this is the case for CCK frames, it's better (only 8) for OFDM */ + hw->radiotap_timestamp.accuracy = 22; +} + +static void iwl_mac_hw_set_flags(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + ieee80211_hw_set(hw, USES_RSS); + ieee80211_hw_set(hw, HANDLES_QUIET_CSA); + ieee80211_hw_set(hw, AP_LINK_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); + ieee80211_hw_set(hw, STA_MMPDU_TXQ); + ieee80211_hw_set(hw, TX_AMSDU); + ieee80211_hw_set(hw, TX_FRAG_LIST); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(hw, DISALLOW_PUNCTURING_5GHZ); + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(hw, TDLS_WIDER_BW); +} + +static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + struct wiphy *wiphy = hw->wiphy; + const struct iwl_ucode_capabilities *ucode_capa = &mld->fw->ucode_capa; + + snprintf(wiphy->fw_version, + sizeof(wiphy->fw_version), + "%.31s", mld->fw->fw_version); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_ADHOC); + + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_ND_RANDOM_MAC_ADDR | + NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_P2P_GO_OPPPS | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION | + NL80211_FEATURE_TX_POWER_INSERTION | + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; + + wiphy->flags |= WIPHY_FLAG_IBSS_RSN | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_SPLIT_SCAN_6GHZ | + WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK; + + if (mld->nvm_data->sku_cap_11be_enable && + !iwlwifi_mod_params.disable_11ax && + !iwlwifi_mod_params.disable_11be) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + + /* the firmware uses u8 for num of iterations, but 0xff is saved for + * infinite loop, so the maximum number of iterations is actually 254. + */ + wiphy->max_sched_scan_plan_iterations = 254; + wiphy->max_sched_scan_ie_len = iwl_mld_scan_max_template_size(); + wiphy->max_scan_ie_len = iwl_mld_scan_max_template_size(); + wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; + wiphy->max_scan_ssids = PROBE_OPTION_MAX; + wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; + wiphy->max_sched_scan_reqs = 1; + wiphy->max_sched_scan_plan_interval = U16_MAX; + wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES_V2; + + wiphy->max_remain_on_channel_duration = 10000; + + wiphy->hw_version = mld->trans->hw_id; + + wiphy->hw_timestamp_max_peers = 1; + + wiphy->iface_combinations = iwl_mld_iface_combinations; + wiphy->n_iface_combinations = ARRAY_SIZE(iwl_mld_iface_combinations); + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_START_TIME); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_PARENT_TSF); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT); + + if (fw_has_capa(ucode_capa, IWL_UCODE_TLV_CAPA_PROTECTED_TWT)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PROTECTED_TWT); + + wiphy->iftype_ext_capab = NULL; + wiphy->num_iftype_ext_capab = 0; + + if (!iwlwifi_mod_params.disable_11ax) { + wiphy->iftype_ext_capab = iftypes_ext_capa; + wiphy->num_iftype_ext_capab = ARRAY_SIZE(iftypes_ext_capa); + + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID); + } + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +} + +static void iwl_mac_hw_set_misc(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + hw->queues = IEEE80211_NUM_ACS; + + hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; + hw->netdev_features |= mld->cfg->features; + + hw->max_tx_fragments = mld->trans->max_skb_frags; + hw->max_listen_interval = IWL_MLD_CONN_LISTEN_INTERVAL; + + hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; + hw->uapsd_queues = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO | + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE; + + hw->chanctx_data_size = sizeof(struct iwl_mld_phy); + hw->vif_data_size = sizeof(struct iwl_mld_vif); + hw->sta_data_size = sizeof(struct iwl_mld_sta); + hw->txq_data_size = sizeof(struct iwl_mld_txq); + + /* TODO: Remove this division when IEEE80211_MAX_AMPDU_BUF_EHT size + * is supported. + * Note: ensure that IWL_DEFAULT_QUEUE_SIZE_EHT is updated accordingly. + */ + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT / 2; +} + +static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld) +{ + /* 11ax is expected to be enabled for all supported devices */ + if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable)) + return -EINVAL; + + /* LAR is expected to be enabled for all supported devices */ + if (WARN_ON(!mld->nvm_data->lar_enabled)) + return -EINVAL; + + /* All supported devices are currently using version 3 of the cmd. + * Since version 3, IWL_SCAN_MAX_PROFILES_V2 shall be used where + * necessary. + */ + if (WARN_ON(iwl_fw_lookup_cmd_ver(mld->fw, + SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + IWL_FW_CMD_VER_UNKNOWN) != 3)) + return -EINVAL; + + return 0; +} + +int iwl_mld_register_hw(struct iwl_mld *mld) +{ + /* verify once essential preconditions required for setting + * the hw capabilities + */ + if (iwl_mld_hw_verify_preconditions(mld)) + return -EINVAL; + + iwl_mld_hw_set_addresses(mld); + iwl_mld_hw_set_channels(mld); + iwl_mld_hw_set_security(mld); + iwl_mld_hw_set_regulatory(mld); + iwl_mld_hw_set_pm(mld); + iwl_mld_hw_set_antennas(mld); + iwl_mac_hw_set_radiotap(mld); + iwl_mac_hw_set_flags(mld); + iwl_mac_hw_set_wiphy(mld); + iwl_mac_hw_set_misc(mld); + + SET_IEEE80211_DEV(mld->hw, mld->trans->dev); + + return ieee80211_register_hw(mld->hw); +} + +static void +iwl_mld_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, struct sk_buff *skb) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_sta *sta = control->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + /* In AP mode, mgmt frames are sent on the bcast station, + * so the FW can't translate the MLD addr to the link addr. Do it here + */ + if (ieee80211_is_mgmt(hdr->frame_control) && sta && + link_id != IEEE80211_LINK_UNSPECIFIED && + !ieee80211_is_probe_resp(hdr->frame_control)) { + /* translate MLD addresses to LINK addresses */ + struct ieee80211_link_sta *link_sta = + rcu_dereference(sta->link[link_id]); + struct ieee80211_bss_conf *link_conf = + rcu_dereference(info->control.vif->link_conf[link_id]); + struct ieee80211_mgmt *mgmt; + + if (WARN_ON(!link_sta || !link_conf)) { + ieee80211_free_txskb(hw, skb); + return; + } + + mgmt = (void *)hdr; + memcpy(mgmt->da, link_sta->addr, ETH_ALEN); + memcpy(mgmt->sa, link_conf->addr, ETH_ALEN); + memcpy(mgmt->bssid, link_conf->bssid, ETH_ALEN); + } + + iwl_mld_tx_skb(mld, skb, NULL); +} + +static void +iwl_mld_restart_cleanup(struct iwl_mld *mld) +{ + iwl_cleanup_mld(mld); + + ieee80211_iterate_interfaces(mld->hw, IEEE80211_IFACE_ITER_ACTIVE, + iwl_mld_cleanup_vif, NULL); + + ieee80211_iterate_stations_atomic(mld->hw, + iwl_mld_cleanup_sta, NULL); + + iwl_mld_ftm_restart_cleanup(mld); +} + +static +int iwl_mld_mac80211_start(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + bool in_d3 = false; + + lockdep_assert_wiphy(mld->wiphy); + +#ifdef CONFIG_PM_SLEEP + /* Unless the host goes into hibernate the FW always stays on and + * the d3_resume flow is used. When wowlan is configured, mac80211 + * would call it's resume callback and the wowlan_resume flow + * would be used. + */ + + in_d3 = mld->fw_status.in_d3; + if (in_d3) { + /* mac80211 already cleaned up the state, no need for cleanup */ + ret = iwl_mld_no_wowlan_resume(mld); + if (ret) + iwl_mld_stop_fw(mld); + } +#endif /* CONFIG_PM_SLEEP */ + + if (mld->fw_status.in_hw_restart) { + iwl_mld_stop_fw(mld); + iwl_mld_restart_cleanup(mld); + } + + if (!in_d3 || ret) { + ret = iwl_mld_start_fw(mld); + if (ret) + goto error; + } + + mld->scan.last_start_time_jiffies = jiffies; + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT, + NULL); + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_PERIODIC, + NULL); + + return 0; + +error: + /* If we failed to restart the hw, there is nothing useful + * we can do but indicate we are no longer in restart. + */ + mld->fw_status.in_hw_restart = false; + + return ret; +} + +static +void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + lockdep_assert_wiphy(mld->wiphy); + + wiphy_work_cancel(mld->wiphy, &mld->add_txqs_wk); + + /* if the suspend flow fails the fw is in error. Stop it here, and it + * will be started upon wakeup + */ + if (!suspend || iwl_mld_no_wowlan_suspend(mld)) + iwl_mld_stop_fw(mld); + + /* HW is stopped, no more coming RX. OTOH, the worker can't run as the + * wiphy lock is held. Cancel it in case it was scheduled just before + * we stopped the HW. + */ + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); + + /* Empty out the list, as the worker won't do that */ + iwl_mld_purge_async_handlers_list(mld); + + /* Clear in_hw_restart flag when stopping the hw, as mac80211 won't + * execute the restart. + */ + mld->fw_status.in_hw_restart = false; + + /* We shouldn't have any UIDs still set. Loop over all the UIDs to + * make sure there's nothing left there and warn if any is found. + */ + for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++) + if (WARN_ONCE(mld->scan.uid_status[i], + "UMAC scan UID %d status was not cleaned (0x%x 0x%x)\n", + i, mld->scan.uid_status[i], mld->scan.status)) + mld->scan.uid_status[i] = 0; +} + +static +int iwl_mld_mac80211_config(struct ieee80211_hw *hw, u32 changed) +{ + return 0; +} + +static +int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* Construct mld_vif, add it to fw, and map its ID to ieee80211_vif */ + ret = iwl_mld_add_vif(mld, vif); + if (ret) + return ret; + + /* + * Add the default link, but not if this is an MLD vif as that implies + * the HW is restarting and it will be configured by change_vif_links. + */ + if (!ieee80211_vif_is_mld(vif)) + ret = iwl_mld_add_link(mld, &vif->bss_conf); + if (ret) + goto err; + + if (vif->type == NL80211_IFTYPE_STATION) { + vif->driver_flags |= IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC; + if (!vif->p2p) + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + } + + if (vif->p2p || iwl_fw_lookup_cmd_ver(mld->fw, PHY_CONTEXT_CMD, 0) < 5) + vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW; + + /* + * For an MLD vif (in restart) we may not have a link; delay the call + * the initial change_vif_links. + */ + if (vif->type == NL80211_IFTYPE_STATION && + !ieee80211_vif_is_mld(vif)) + iwl_mld_update_mac_power(mld, vif, false); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + mld->monitor.on = true; + ieee80211_hw_set(mld->hw, RX_INCLUDES_FCS); + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + mld->p2p_device_vif = vif; + + return 0; + +err: + iwl_mld_rm_vif(mld, vif); + return ret; +} + +static +void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + lockdep_assert_wiphy(mld->wiphy); + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mld->hw->flags); + mld->monitor.on = false; + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + mld->p2p_device_vif = NULL; + + iwl_mld_remove_link(mld, &vif->bss_conf); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink); +#endif + + iwl_mld_rm_vif(mld, vif); +} + +struct iwl_mld_mc_iter_data { + struct iwl_mld *mld; + int port_id; +}; + +static void iwl_mld_mc_iface_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_mc_iter_data *mc_data = data; + struct iwl_mld *mld = mc_data->mld; + struct iwl_mcast_filter_cmd *cmd = mld->mcast_filter_cmd; + struct iwl_host_cmd hcmd = { + .id = MCAST_FILTER_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int ret, len; + + /* If we don't have free ports, mcast frames will be dropped */ + if (WARN_ON_ONCE(mc_data->port_id >= MAX_PORT_ID_NUM)) + return; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + cmd->port_id = mc_data->port_id++; + ether_addr_copy(cmd->bssid, vif->bss_conf.bssid); + len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + + hcmd.len[0] = len; + hcmd.data[0] = cmd; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + IWL_ERR(mld, "mcast filter cmd error. ret=%d\n", ret); +} + +void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld) +{ + struct iwl_mld_mc_iter_data iter_data = { + .mld = mld, + }; + + if (WARN_ON_ONCE(!mld->mcast_filter_cmd)) + return; + + ieee80211_iterate_active_interfaces(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_mc_iface_iterator, + &iter_data); +} + +static u64 +iwl_mld_mac80211_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mcast_filter_cmd *cmd; + struct netdev_hw_addr *addr; + int addr_count = netdev_hw_addr_list_count(mc_list); + bool pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES; + int len; + + if (pass_all) + addr_count = 0; + + /* len must be a multiple of 4 */ + len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); + cmd = kzalloc(len, GFP_ATOMIC); + if (!cmd) + return 0; + + if (pass_all) { + cmd->pass_all = 1; + goto out; + } + + netdev_hw_addr_list_for_each(addr, mc_list) { + IWL_DEBUG_MAC80211(mld, "mcast addr (%d): %pM\n", + cmd->count, addr->addr); + ether_addr_copy(&cmd->addr_list[cmd->count * ETH_ALEN], + addr->addr); + cmd->count++; + } + +out: + return (u64)(unsigned long)cmd; +} + +static +void iwl_mld_mac80211_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; + + /* Replace previous configuration */ + kfree(mld->mcast_filter_cmd); + mld->mcast_filter_cmd = cmd; + + if (!cmd) + goto out; + + if (changed_flags & FIF_ALLMULTI) + cmd->pass_all = !!(*total_flags & FIF_ALLMULTI); + + if (cmd->pass_all) + cmd->count = 0; + + iwl_mld_recalc_multicast_filter(mld); +out: + *total_flags = 0; +} + +static +void iwl_mld_mac80211_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + + if (likely(mld_txq->status.allocated) || !txq->sta) { + iwl_mld_tx_from_txq(mld, txq); + return; + } + + /* We don't support TSPEC tids. %IEEE80211_NUM_TIDS is for mgmt */ + if (txq->tid != IEEE80211_NUM_TIDS && txq->tid >= IWL_MAX_TID_COUNT) { + IWL_DEBUG_MAC80211(mld, "TID %d is not supported\n", txq->tid); + return; + } + + /* The worker will handle any packets we leave on the txq now */ + + spin_lock_bh(&mld->add_txqs_lock); + /* The list is being deleted only after the queue is fully allocated. */ + if (list_empty(&mld_txq->list) && + /* recheck under lock, otherwise it can be added twice */ + !mld_txq->status.allocated) { + list_add_tail(&mld_txq->list, &mld->txqs_to_add); + wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk); + } + spin_unlock_bh(&mld->add_txqs_lock); +} + +static void iwl_mld_teardown_tdls_peers(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(link_sta)) + continue; + + if (!link_sta->sta->tdls) + continue; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + ieee80211_tdls_oper_request(mld_sta->vif, link_sta->addr, + NL80211_TDLS_TEARDOWN, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, + GFP_KERNEL); + } +} + +static +int iwl_mld_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + int fw_id = iwl_mld_allocate_fw_phy_id(mld); + int ret; + + if (fw_id < 0) + return fw_id; + + phy->fw_id = fw_id; + phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); + + ret = iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_ADD); + if (ret) { + mld->used_phy_ids &= ~BIT(phy->fw_id); + return ret; + } + + if (hweight8(mld->used_phy_ids) > 1) + iwl_mld_teardown_tdls_peers(mld); + + return 0; +} + +static +void iwl_mld_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_REMOVE); + mld->used_phy_ids &= ~BIT(phy->fw_id); +} + +static +void iwl_mld_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, u32 changed) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(ctx); + + /* We don't care about these */ + if (!(changed & ~(IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR | + IEEE80211_CHANCTX_CHANGE_CHANNEL))) + return; + + /* Check if a FW update is required */ + + if (changed & IEEE80211_CHANCTX_CHANGE_AP) + goto update; + + if (chandef->chan == phy->chandef.chan && + chandef->center_freq1 == phy->chandef.center_freq1 && + chandef->punctured == phy->chandef.punctured) { + /* Check if we are toggling between HT and non-HT, no-op */ + if (phy->chandef.width == chandef->width || + (phy->chandef.width <= NL80211_CHAN_WIDTH_20 && + chandef->width <= NL80211_CHAN_WIDTH_20)) + return; + } +update: + phy->chandef = *chandef; + + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); +} + +static u8 +iwl_mld_chandef_get_primary_80(struct cfg80211_chan_def *chandef) +{ + int data_start; + int control_start; + int bw; + + if (chandef->width == NL80211_CHAN_WIDTH_320) + bw = 320; + else if (chandef->width == NL80211_CHAN_WIDTH_160) + bw = 160; + else + return 0; + + /* data is bw wide so the start is half the width */ + data_start = chandef->center_freq1 - bw / 2; + /* control is 20Mhz width */ + control_start = chandef->chan->center_freq - 10; + + return (control_start - data_start) / 80; +} + +static bool iwl_mld_can_activate_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link_sta *link_sta; + + /* In association, we activate the assoc link before adding the STA. */ + if (!mld_vif->ap_sta || !vif->cfg.assoc) + return true; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* When switching links, we need to wait with the activation until the + * STA was added to the FW. It'll be activated in + * iwl_mld_update_link_stas + */ + link_sta = wiphy_dereference(mld->wiphy, mld_sta->link[link->link_id]); + + /* In restart we can have a link_sta that doesn't exist in FW yet */ + return link_sta && link_sta->in_fw; +} + +static +int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + unsigned int n_active = iwl_mld_count_active_links(mld, vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + /* if the assigned one was not counted yet, count it now */ + if (!rcu_access_pointer(mld_link->chan_ctx)) { + n_active++; + + /* Track addition of non-BSS link */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { + ret = iwl_mld_emlsr_check_non_bss_block(mld, 1); + if (ret) + return ret; + } + } + + /* for AP, mac parameters such as HE support are updated at this stage. */ + if (vif->type == NL80211_IFTYPE_AP) { + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + + if (ret) { + IWL_ERR(mld, "failed to update MAC %pM\n", vif->addr); + return -EINVAL; + } + } + + rcu_assign_pointer(mld_link->chan_ctx, ctx); + + if (n_active > 1) { + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_leave_omi_bw_reduction(mld); + + /* Indicate to mac80211 that EML is enabled */ + vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE; + + if (vif->active_links & BIT(mld_vif->emlsr.selected_links)) + mld_vif->emlsr.primary = mld_vif->emlsr.selected_primary; + else + mld_vif->emlsr.primary = __ffs(vif->active_links); + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP, + NULL); + } + + /* First send the link command with the phy context ID. + * Now that we have the phy, we know the band so also the rates + */ + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_RATES_INFO); + if (ret) + goto err; + + /* TODO: Initialize rate control for the AP station, since we might be + * doing a link switch here - we cannot initialize it before since + * this needs the phy context assigned (and in FW?), and we cannot + * do it later because it needs to be initialized as soon as we're + * able to TX on the link, i.e. when active. (task=link-switch) + */ + + /* Now activate the link */ + if (iwl_mld_can_activate_link(mld, vif, link)) { + ret = iwl_mld_activate_link(mld, link); + if (ret) + goto err; + } + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + /* TODO: task=sniffer add sniffer station */ + mld->monitor.p80 = + iwl_mld_chandef_get_primary_80(&vif->bss_conf.chanreq.oper); + } + + return 0; +err: + RCU_INIT_POINTER(mld_link->chan_ctx, NULL); + return ret; +} + +static +void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + unsigned int n_active = iwl_mld_count_active_links(mld, vif); + + if (WARN_ON(!mld_link)) + return; + + /* Track removal of non-BSS link */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + iwl_mld_emlsr_check_non_bss_block(mld, -1); + + iwl_mld_deactivate_link(mld, link); + + /* TODO: task=sniffer remove sniffer station */ + + if (n_active > 1) { + /* Indicate to mac80211 that EML is disabled */ + vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_ESR_LINK_DOWN, + NULL); + } + + RCU_INIT_POINTER(mld_link->chan_ctx, NULL); + + /* 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 + */ + if (!ieee80211_vif_is_mld(vif) && !mld_vif->ap_sta && + !WARN_ON_ONCE(vif->cfg.assoc) && + vif->type != NL80211_IFTYPE_AP && !mld->fw_status.in_hw_restart) { + iwl_mld_remove_link(mld, link); + iwl_mld_add_link(mld, link); + } +} + +static +int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return 0; +} + +static void +iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + u64 changes) +{ + u32 link_changes = 0; + + if (changes & BSS_CHANGED_ERP_SLOT) + link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT)) + link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; + + if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH)) + link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + + if (changes & BSS_CHANGED_HE_BSS_COLOR) + link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + + if (link_changes) + iwl_mld_change_link_in_fw(mld, link, link_changes); + + if (changes & BSS_CHANGED_BEACON) + iwl_mld_update_beacon_template(mld, vif, link); +} + +static +u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + u32 link_changes = 0; + bool has_he, has_eht; + + if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos) + link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + + if (changes & (BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_SLOT)) + link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + + if (changes & (BSS_CHANGED_HT | BSS_CHANGED_ERP_CTS_PROT)) + link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; + + /* TODO: task=MLO check mac80211's HE flags and if command is needed + * every time there's a link change. Currently used flags are + * BSS_CHANGED_HE_OBSS_PD and BSS_CHANGED_HE_BSS_COLOR. + */ + has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; + has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; + + if (vif->cfg.assoc && (has_he || has_eht)) { + IWL_DEBUG_MAC80211(mld, "Associated in HE mode\n"); + link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + } + + return link_changes; +} + +static void +iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + u32 link_changes = iwl_mld_link_changed_mapping(mld, vif, link_conf, + changes); + + if (link_changes) + iwl_mld_change_link_in_fw(mld, link_conf, link_changes); + + if (changes & BSS_CHANGED_TPE) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link_conf); + + if (changes & BSS_CHANGED_BEACON_INFO) + iwl_mld_update_mac_power(mld, vif, false); + + /* The firmware will wait quite a while after association before it + * starts filtering the beacons. We can safely enable beacon filtering + * upon CQM configuration, even if we didn't get a beacon yet. + */ + if (changes & (BSS_CHANGED_CQM | BSS_CHANGED_BEACON_INFO)) + iwl_mld_enable_beacon_filter(mld, link_conf, false); + + /* If we have used OMI before to reduce bandwidth to 80 MHz and then + * increased to 160 MHz again, and then the AP changes to 320 MHz, it + * will think that we're limited to 160 MHz right now. Update it by + * requesting a new OMI bandwidth. + */ + if (changes & BSS_CHANGED_BANDWIDTH) { + enum ieee80211_sta_rx_bandwidth bw; + + bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); + + iwl_mld_omi_ap_changed_bw(mld, link_conf, bw); + } + + if (changes & BSS_CHANGED_BANDWIDTH) + iwl_mld_emlsr_check_equal_bw(mld, vif, link_conf); +} + +static int iwl_mld_update_mu_groups(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mu_group_mgmt_cmd cmd = {}; + + BUILD_BUG_ON(sizeof(cmd.membership_status) != + sizeof(link_conf->mu_group.membership)); + BUILD_BUG_ON(sizeof(cmd.user_position) != + sizeof(link_conf->mu_group.position)); + + memcpy(cmd.membership_status, link_conf->mu_group.membership, + WLAN_MEMBERSHIP_LEN); + memcpy(cmd.user_position, link_conf->mu_group.position, + WLAN_USER_POSITION_LEN); + + return iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + UPDATE_MU_GROUPS_CMD), + &cmd); +} + +static void +iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + iwl_mld_mac80211_link_info_changed_sta(mld, vif, link_conf, + changes); + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + iwl_mld_link_info_changed_ap_ibss(mld, vif, link_conf, + changes); + break; + case NL80211_IFTYPE_MONITOR: + /* The firmware tracks this on its own in STATION mode, but + * obviously not in sniffer mode. + */ + if (changes & BSS_CHANGED_MU_GROUPS) + iwl_mld_update_mu_groups(mld, link_conf); + break; + default: + /* shouldn't happen */ + WARN_ON_ONCE(1); + } + + /* We now know our BSSID, we can configure the MAC context with + * eht_support if needed. + */ + if (changes & BSS_CHANGED_BSSID) + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + + if (changes & BSS_CHANGED_TXPOWER) + iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower); +} + +static +void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 changes) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (changes & BSS_CHANGED_ASSOC) { + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + if (ret) + IWL_ERR(mld, "failed to update context\n"); + + if (vif->cfg.assoc) { + /* Clear statistics to get clean beacon counter, and + * ask for periodic statistics, as they are needed for + * link selection and RX OMI decisions. + */ + iwl_mld_clear_stats_in_fw(mld); + iwl_mld_request_periodic_fw_stats(mld, true); + + iwl_mld_set_vif_associated(mld, vif); + } else { + iwl_mld_request_periodic_fw_stats(mld, false); + } + } + + if (changes & BSS_CHANGED_PS) { + /* Send both device-level and MAC-level power commands since the + * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to + * determine SMPS mode. + */ + iwl_mld_update_device_power(mld, false); + iwl_mld_update_mac_power(mld, vif, false); + } + + /* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */ +} + +static int +iwl_mld_mac80211_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + if (WARN_ON(!hw_req->req.n_channels || + hw_req->req.n_channels > + mld->fw->ucode_capa.n_scan_channels)) + return -EINVAL; + + return iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); +} + +static void +iwl_mld_mac80211_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a hw_scan when it's already stopped. This can + * happen, for instance, if we stopped the scan ourselves, + * called ieee80211_scan_completed() and the userspace called + * cancel scan before ieee80211_scan_work() could run. + * To handle that, simply return if the scan is not running. + */ + if (mld->scan.status & IWL_MLD_SCAN_REGULAR) + iwl_mld_scan_stop(mld, IWL_MLD_SCAN_REGULAR, true); +} + +static int +iwl_mld_mac80211_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_sched_scan_start(mld, vif, req, ies, IWL_MLD_SCAN_SCHED); +} + +static int +iwl_mld_mac80211_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a sched_scan when it's already stopped. This + * can happen, for instance, if we stopped the scan ourselves, + * called ieee80211_sched_scan_stopped() and the userspace called + * stop sched scan before ieee80211_sched_scan_stopped_work() + * could run. To handle this, simply return if the scan is + * not running. + */ + if (!(mld->scan.status & IWL_MLD_SCAN_SCHED)) + return 0; + + return iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, false); +} + +static void +iwl_mld_restart_complete_vif(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + struct iwl_mld *mld = data; + int link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + enum ieee80211_sta_rx_bandwidth bw; + struct iwl_mld_link *mld_link; + + mld_link = wiphy_dereference(mld->wiphy, + mld_vif->link[link_id]); + + if (WARN_ON_ONCE(!mld_link)) + continue; + + bw = mld_link->rx_omi.bw_in_progress; + if (bw) + iwl_mld_change_link_omi_bw(mld, link_conf, bw); + } +} + +static void +iwl_mld_mac80211_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + switch (reconfig_type) { + case IEEE80211_RECONFIG_TYPE_RESTART: + mld->fw_status.in_hw_restart = false; + iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_END_OF_RECOVERY); + + ieee80211_iterate_interfaces(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_restart_complete_vif, mld); + + iwl_trans_finish_sw_reset(mld->trans); + /* no need to lock, adding in parallel would schedule too */ + if (!list_empty(&mld->txqs_to_add)) + wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk); + + IWL_INFO(mld, "restart completed\n"); + break; + case IEEE80211_RECONFIG_TYPE_SUSPEND: + break; + } +} + +static +void iwl_mld_mac80211_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + u32 duration = IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS; + + /* After a successful association the connection is etalibeshed + * and we can rely on the quota to send the disassociation frame. + */ + if (info->was_assoc) + return; + + if (info->duration > duration) + duration = info->duration; + + iwl_mld_schedule_session_protection(mld, vif, duration, + IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS, + info->link_id); +} + +static +void iwl_mld_mac_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Successful authentication is the only case that requires to let + * the session protection go. We'll need it for the upcoming + * association. For all the other cases, we need to cancel the session + * protection. + * After successful association the connection is established and + * further mgd tx can rely on the quota. + */ + if (info->success && info->subtype == IEEE80211_STYPE_AUTH) + return; + + /* The firmware will be on medium after we configure the vif as + * associated. Removing the session protection allows the firmware + * to stop being on medium. In order to ensure the continuity of our + * presence on medium, we need first to configure the vif as associated + * and only then, remove the session protection. + * Currently, mac80211 calls vif_cfg_changed() first and then, + * drv_mgd_complete_tx(). Ensure that this assumption stays true by + * a warning. + */ + WARN_ON(info->success && + (info->subtype == IEEE80211_STYPE_ASSOC_REQ || + info->subtype == IEEE80211_STYPE_REASSOC_REQ) && + !vif->cfg.assoc); + + iwl_mld_cancel_session_protection(mld, vif, info->link_id); +} + +static int +iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link; + + lockdep_assert_wiphy(mld->wiphy); + + link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (!link) + return -EINVAL; + + link->queue_params[ac] = *params; + + /* No need to update right away, we'll get BSS_CHANGED_QOS + * The exception is P2P_DEVICE interface which needs immediate update. + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + iwl_mld_change_link_in_fw(mld, &vif->bss_conf, + LINK_CONTEXT_MODIFY_QOS_PARAMS); + + return 0; +} + +static void iwl_mld_set_uapsd(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (vif->p2p && + !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_P2P_CLIENT)) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + if (!vif->p2p && + !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_BSS)) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; +} + +int iwl_mld_tdls_sta_count(struct iwl_mld *mld) +{ + int count = 0; + + lockdep_assert_wiphy(mld->wiphy); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(link_sta)) + continue; + + if (!link_sta->sta->tdls) + continue; + + count++; + } + + return count; +} + +static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, + struct cfg80211_bss *bss, + void *_data) +{ + bool *tolerated = _data; + const struct cfg80211_bss_ies *ies; + const struct element *elem; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, + ies->len); + + if (!elem || elem->datalen < 10 || + !(elem->data[10] & + WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { + *tolerated = false; + } + rcu_read_unlock(); +} + +static void +iwl_mld_check_he_obss_narrow_bw_ru(struct iwl_mld *mld, + struct iwl_mld_link *mld_link, + struct ieee80211_bss_conf *link_conf) +{ + bool tolerated = true; + + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) + return; + + if (!(link_conf->chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) { + mld_link->he_ru_2mhz_block = false; + return; + } + + cfg80211_bss_iter(mld->wiphy, &link_conf->chanreq.oper, + iwl_mld_check_he_obss_narrow_bw_ru_iter, &tolerated); + + /* If there is at least one AP on radar channel that cannot + * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU. + */ + mld_link->he_ru_2mhz_block = !tolerated; +} + +static void iwl_mld_link_set_2mhz_block(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + struct iwl_mld_link *mld_link = + iwl_mld_link_from_mac80211(link_conf); + + if (WARN_ON(!link_conf || !mld_link)) + continue; + + if (link_sta->he_cap.has_he) + iwl_mld_check_he_obss_narrow_bw_ru(mld, mld_link, + link_conf); + } +} + +static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int tdls_count = 0; + int ret; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + if (sta->tdls) { + if (vif->p2p || hweight8(mld->used_phy_ids) != 1) + return -EBUSY; + + tdls_count = iwl_mld_tdls_sta_count(mld); + if (tdls_count >= IWL_TDLS_STA_COUNT) + return -EBUSY; + } + + /* + * If this is the first STA (i.e. the AP) it won't do + * anything, otherwise must leave for any new STA on + * any other interface, or for TDLS, etc. + * Need to call this _before_ adding the STA so it can + * look up the one STA to use to ask mac80211 to leave + * OMI; in the unlikely event that adding the new STA + * then fails we'll just re-enter OMI later (via the + * statistics notification handling.) + */ + iwl_mld_leave_omi_bw_reduction(mld); + + ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER); + if (ret) + return ret; + + /* just added first TDLS STA, so disable PM */ + if (sta->tdls && tdls_count == 0) + iwl_mld_update_mac_power(mld, vif, false); + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mld_vif->ap_sta = sta; + + /* Initialize TLC here already - this really tells + * the firmware only what the supported legacy rates are + * (may be) since it's initialized already from what the + * AP advertised in the beacon/probe response. This will + * allow the firmware to send auth/assoc frames with one + * of the supported rates already, rather than having to + * use a mandatory rate. + * If we're the AP, we'll just assume mandatory rates at + * this point, but we know nothing about the STA anyway. + */ + iwl_mld_config_tlc(mld, vif, sta); + + return ret; + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_AUTH) { + iwl_mld_set_uapsd(mld, vif); + return 0; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + ret = iwl_mld_update_all_link_stations(mld, sta); + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mld_link_set_2mhz_block(mld, vif, sta); + /* Now the link_sta's capabilities are set, update the FW */ + iwl_mld_config_tlc(mld, vif, sta); + + if (vif->type == NL80211_IFTYPE_AP) { + /* Update MAC_CFG_FILTER_ACCEPT_BEACON if at least + * one sta is associated + */ + if (++mld_vif->num_associated_stas == 1) + ret = iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + } + + return ret; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + ret = 0; + + if (!sta->tdls) { + mld_vif->authorized = true; + + /* Ensure any block due to a non-BSS link is synced */ + iwl_mld_emlsr_check_non_bss_block(mld, 0); + + /* Block EMLSR until a certain throughput it reached */ + if (!mld->fw_status.in_hw_restart && + IWL_MLD_ENTER_EMLSR_TPT_THRESH > 0) + iwl_mld_block_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TPT, + 0); + + /* Wait for the FW to send a recommendation */ + iwl_mld_block_emlsr(mld, vif, + IWL_MLD_EMLSR_BLOCKED_FW, 0); + + /* clear COEX_HIGH_PRIORITY_ENABLE */ + ret = iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + if (ret) + return ret; + } + + /* MFP is set by default before the station is authorized. + * Clear it here in case it's not used. + */ + if (!sta->mfp) + ret = iwl_mld_update_all_link_stations(mld, sta); + + /* We can use wide bandwidth now, not only 20 MHz */ + iwl_mld_config_tlc(mld, vif, sta); + + return ret; + } else { + return -EINVAL; + } +} + +static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { + if (!sta->tdls) { + mld_vif->authorized = false; + + memset(&mld_vif->emlsr.zeroed_on_not_authorized, 0, + sizeof(mld_vif->emlsr.zeroed_on_not_authorized)); + + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.prevent_done_wk); + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.tmp_non_bss_done_wk); + wiphy_work_cancel(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk); + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + + iwl_mld_reset_cca_40mhz_workaround(mld, vif); + } + + /* once we move into assoc state, need to update the FW to + * stop using wide bandwidth + */ + iwl_mld_config_tlc(mld, vif, sta); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + if (vif->type == NL80211_IFTYPE_AP && + !WARN_ON(!mld_vif->num_associated_stas)) { + /* Update MAC_CFG_FILTER_ACCEPT_BEACON if the last sta + * is disassociating + */ + if (--mld_vif->num_associated_stas == 0) + iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + } + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_NONE) { + /* nothing */ + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) { + iwl_mld_remove_sta(mld, sta); + + if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) { + /* just removed last TDLS STA, so enable PM */ + iwl_mld_update_mac_power(mld, vif, false); + } + } else { + return -EINVAL; + } + return 0; +} + +static int iwl_mld_mac80211_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + IWL_DEBUG_MAC80211(mld, "station %pM state change %d->%d\n", + sta->addr, old_state, new_state); + + mld_sta->sta_state = new_state; + + if (old_state < new_state) + return iwl_mld_move_sta_state_up(mld, vif, sta, old_state, + new_state); + else + return iwl_mld_move_sta_state_down(mld, vif, sta, old_state, + new_state); +} + +static void iwl_mld_mac80211_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Make sure we're done with the deferred traffic before flushing */ + iwl_mld_add_txq_list(mld); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + + if (IS_ERR_OR_NULL(link_sta)) + continue; + + /* Check that the sta belongs to the given vif */ + if (vif && vif != iwl_mld_sta_from_mac80211(link_sta->sta)->vif) + continue; + + if (drop) + iwl_mld_flush_sta_txqs(mld, link_sta->sta); + else + iwl_mld_wait_sta_txqs_empty(mld, link_sta->sta); + } +} + +static void iwl_mld_mac80211_flush_sta(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_flush_sta_txqs(mld, sta); +} + +static int +iwl_mld_mac80211_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 ssn = params->ssn; + u16 buf_size = params->buf_size; + u16 timeout = params->timeout; + int ret; + + IWL_DEBUG_HT(mld, "A-MPDU action on addr %pM tid: %d action: %d\n", + sta->addr, tid, action); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + ret = iwl_mld_ampdu_rx_start(mld, sta, tid, ssn, buf_size, + timeout); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = iwl_mld_ampdu_rx_stop(mld, sta, tid); + break; + default: + /* The mac80211 TX_AMPDU_SETUP_IN_HW flag is set for all + * devices, since all support TX A-MPDU offload in hardware. + * Therefore, no TX action should be requested here. + */ + WARN_ON_ONCE(1); + return -EINVAL; + } + + return ret; +} + +static bool iwl_mld_can_hw_csum(struct sk_buff *skb) +{ + u8 protocol = ip_hdr(skb)->protocol; + + return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP; +} + +static bool iwl_mld_mac80211_can_aggregate(struct ieee80211_hw *hw, + struct sk_buff *head, + struct sk_buff *skb) +{ + if (!IS_ENABLED(CONFIG_INET)) + return false; + + /* For now don't aggregate IPv6 in AMSDU */ + if (skb->protocol != htons(ETH_P_IP)) + return false; + + /* Allow aggregation only if both frames have the same HW csum offload + * capability, ensuring consistent HW or SW csum handling in A-MSDU. + */ + return iwl_mld_can_hw_csum(skb) == iwl_mld_can_hw_csum(head); +} + +static void iwl_mld_mac80211_sync_rx_queues(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_EMPTY, NULL, 0); +} + +static void iwl_mld_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + if (changed & (IEEE80211_RC_BW_CHANGED | + IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED)) { + struct ieee80211_bss_conf *link = + link_conf_dereference_check(vif, link_sta->link_id); + + if (WARN_ON(!link)) + return; + + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + } +} + +static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + device_set_wakeup_enable(mld->trans->dev, enabled); +} + +/* Returns 0 on success. 1 if failed to suspend with wowlan: + * If the circumstances didn't satisfy the conditions for suspension + * with wowlan, mac80211 would use the no_wowlan flow. + * If an error had occurred we update the trans status and state here + * and the result will be stopping the FW. + */ +static int +iwl_mld_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_fw_runtime_suspend(&mld->fwrt); + + ret = iwl_mld_wowlan_suspend(mld, wowlan); + if (ret) { + if (ret < 0) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + return 1; + } + + if (iwl_mld_no_wowlan_suspend(mld)) + return 1; + + return 0; +} + +static int iwl_mld_resume(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + ret = iwl_mld_wowlan_resume(mld); + if (ret) + return ret; + + iwl_fw_runtime_resume(&mld->fwrt); + + iwl_mld_low_latency_restart(mld); + + return 0; +} + +static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld, + struct iwl_mld_sta *mld_sta, + struct ieee80211_key_conf *key, + struct iwl_mld_ptk_pn **ptk_pn) +{ + u8 num_rx_queues = mld->trans->num_rx_queues; + int keyidx = key->keyidx; + struct ieee80211_key_seq seq; + + if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return -EINVAL; + + WARN_ON(rcu_access_pointer(mld_sta->ptk_pn[keyidx])); + *ptk_pn = kzalloc(struct_size(*ptk_pn, q, num_rx_queues), + GFP_KERNEL); + if (!*ptk_pn) + return -ENOMEM; + + for (u8 tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + ieee80211_get_key_rx_seq(key, tid, &seq); + for (u8 q = 0; q < num_rx_queues; q++) + memcpy((*ptk_pn)->q[q].pn[tid], seq.ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } + + rcu_assign_pointer(mld_sta->ptk_pn[keyidx], *ptk_pn); + + return 0; +} + +static int iwl_mld_set_key_add(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta = + sta ? iwl_mld_sta_from_mac80211(sta) : NULL; + struct iwl_mld_ptk_pn *ptk_pn = NULL; + int keyidx = key->keyidx; + int ret; + + /* Will be set to 0 if added successfully */ + key->hw_key_idx = STA_KEY_IDX_INVALID; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + IWL_DEBUG_MAC80211(mld, "Use SW encryption for WEP\n"); + return -EOPNOTSUPP; + case WLAN_CIPHER_SUITE_TKIP: + if (vif->type == NL80211_IFTYPE_STATION) { + key->flags |= IEEE80211_KEY_FLAG_PUT_MIC_SPACE; + break; + } + IWL_DEBUG_MAC80211(mld, "Use SW encryption for TKIP\n"); + return -EOPNOTSUPP; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + break; + default: + return -EOPNOTSUPP; + } + + if (vif->type == NL80211_IFTYPE_STATION && + (keyidx == 6 || keyidx == 7)) + rcu_assign_pointer(mld_vif->bigtks[keyidx - 6], key); + + /* After exiting from RFKILL, hostapd configures GTK/ITGK before the + * AP is started, but those keys can't be sent to the FW before the + * MCAST/BCAST STAs are added to it (which happens upon AP start). + * Store it here to be sent later when the AP is started. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP) && !sta && + !mld_vif->ap_ibss_active) + return iwl_mld_store_ap_early_key(mld, key, mld_vif); + + if (!mld->fw_status.in_hw_restart && mld_sta && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { + ret = iwl_mld_alloc_ptk_pn(mld, mld_sta, key, &ptk_pn); + if (ret) + return ret; + } + + IWL_DEBUG_MAC80211(mld, "set hwcrypto key (sta:%pM, id:%d)\n", + sta ? sta->addr : NULL, keyidx); + + ret = iwl_mld_add_key(mld, vif, sta, key); + if (ret) { + IWL_WARN(mld, "set key failed (%d)\n", ret); + if (ptk_pn) { + RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL); + kfree(ptk_pn); + } + + return -EOPNOTSUPP; + } + + return 0; +} + +static void iwl_mld_set_key_remove(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta = + sta ? iwl_mld_sta_from_mac80211(sta) : NULL; + int keyidx = key->keyidx; + + if (vif->type == NL80211_IFTYPE_STATION && + (keyidx == 6 || keyidx == 7)) + RCU_INIT_POINTER(mld_vif->bigtks[keyidx - 6], NULL); + + if (mld_sta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { + struct iwl_mld_ptk_pn *ptk_pn; + + if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return; + + ptk_pn = wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[keyidx]); + RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL); + if (!WARN_ON(!ptk_pn)) + kfree_rcu(ptk_pn, rcu_head); + } + + /* if this key was stored to be added later to the FW - free it here */ + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + iwl_mld_free_ap_early_key(mld, key, mld_vif); + + /* We already removed it */ + if (key->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + IWL_DEBUG_MAC80211(mld, "disable hwcrypto key\n"); + + iwl_mld_remove_key(mld, vif, sta, key); +} + +static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + switch (cmd) { + case SET_KEY: + ret = iwl_mld_set_key_add(mld, vif, sta, key); + if (ret) + ret = -EOPNOTSUPP; + break; + case DISABLE_KEY: + iwl_mld_set_key_remove(mld, vif, sta, key); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int +iwl_mld_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = + iwl_mld_link_dereference_check(mld_vif, chsw->link_id); + u8 primary; + int selected; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + IWL_DEBUG_MAC80211(mld, "pre CSA to freq %d\n", + chsw->chandef.center_freq1); + + if (!iwl_mld_emlsr_active(vif)) + return 0; + + primary = iwl_mld_get_primary_link(vif); + + /* stay on the primary link unless it is undergoing a CSA with quiet */ + if (chsw->link_id == primary && chsw->block_tx) + selected = iwl_mld_get_other_link(vif, primary); + else + selected = primary; + + /* Remember to tell the firmware that this link can't tx + * Note that this logic seems to be unrelated to emlsr, but it + * really is needed only when emlsr is active. When we have a + * single link, the firmware will handle all this on its own. + * In multi-link scenarios, we can learn about the CSA from + * another link and this logic is too complex for the firmware + * to track. + * Since we want to de-activate the link that got a CSA with mode=1, + * we need to tell the firmware not to send any frame on that link + * as the firmware may not be aware that link is under a CSA + * with mode=1 (no Tx allowed). + */ + mld_link->silent_deactivation = chsw->block_tx; + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CSA, selected); + + return 0; +} + +static void +iwl_mld_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* By implementing this operation, we prevent mac80211 from + * starting its own channel switch timer, so that we can call + * ieee80211_chswitch_done() ourselves at the right time + * (Upon receiving the channel_switch_start notification from the fw) + */ + IWL_DEBUG_MAC80211(mld, + "dummy channel switch op\n"); +} + +static int +iwl_mld_post_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(mld_link->silent_deactivation); + + return 0; +} + +static void +iwl_mld_abort_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + + IWL_DEBUG_MAC80211(mld, + "abort channel switch op\n"); + mld_link->silent_deactivation = false; +} + +static int +iwl_mld_switch_vif_chanctx_swap(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx); + iwl_mld_remove_chanctx(hw, vifs[0].old_ctx); + + ret = iwl_mld_add_chanctx(hw, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, "failed to add new_ctx during channel switch\n"); + goto out_reassign; + } + + ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, + "failed to assign new_ctx during channel switch\n"); + goto out_remove; + } + + return 0; + + out_remove: + iwl_mld_remove_chanctx(hw, vifs[0].new_ctx); + out_reassign: + if (iwl_mld_add_chanctx(hw, vifs[0].old_ctx)) { + IWL_ERR(mld, "failed to add old_ctx after failure\n"); + return ret; + } + + if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx)) + IWL_ERR(mld, "failed to reassign old_ctx after failure\n"); + + return ret; +} + +static int +iwl_mld_switch_vif_chanctx_reassign(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx); + ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, + "failed to assign new_ctx during channel switch\n"); + goto out_reassign; + } + + return 0; + +out_reassign: + if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx)) + IWL_ERR(mld, "failed to reassign old_ctx after failure\n"); + + return ret; +} + +static int +iwl_mld_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + int ret; + + /* we only support a single-vif right now */ + if (n_vifs > 1) + return -EOPNOTSUPP; + + switch (mode) { + case CHANCTX_SWMODE_SWAP_CONTEXTS: + ret = iwl_mld_switch_vif_chanctx_swap(hw, vifs); + break; + case CHANCTX_SWMODE_REASSIGN_VIF: + ret = iwl_mld_switch_vif_chanctx_reassign(hw, vifs); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void iwl_mld_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* This is called before mac80211 does RCU synchronisation, + * so here we already invalidate our internal RCU-protected + * station pointer. The rest of the code will thus no longer + * be able to find the station this way, and we don't rely + * on further RCU synchronisation after the sta_state() + * callback deleted the station. + */ + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], + NULL); + + if (sta == mld_vif->ap_sta) + mld_vif->ap_sta = NULL; +} + +static void +iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_bss_conf *link_conf; + u32 duration; + int ret; + + link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); + if (WARN_ON_ONCE(!link_conf)) + return; + + /* Protect the session to hear the TDLS setup response on the channel */ + + duration = 2 * link_conf->dtim_period * link_conf->beacon_int; + + ret = iwl_mld_start_session_protection(mld, vif, duration, duration, + link_id, HZ / 5); + if (ret) + IWL_ERR(mld, + "Failed to start session protection for TDLS: %d\n", + ret); +} + +static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 desired_links) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int n_links = hweight16(desired_links); + + /* Check if HW supports the wanted number of links */ + return n_links <= iwl_mld_max_active_links(mld, vif); +} + +static int +iwl_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_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_bss_conf *link_conf; + u16 removed = old_links & ~new_links; + u16 added = new_links & ~old_links; + int err; + + lockdep_assert_wiphy(mld->wiphy); + + /* + * No bits designate non-MLO mode. We can handle MLO exit/enter by + * simply mapping that to link ID zero internally. + * Note that mac80211 does such a non-MLO to MLO switch during restart + * if it was in MLO before. In that case, we do not have a link to + * remove. + */ + if (old_links == 0 && !mld->fw_status.in_hw_restart) + removed |= BIT(0); + + if (new_links == 0) + added |= BIT(0); + + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (removed & BIT(i)) { + link_conf = old[i]; + + err = iwl_mld_remove_link(mld, link_conf); + if (err) + return err; + } + } + + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (added & BIT(i)) { + link_conf = link_conf_dereference_protected(vif, i); + if (WARN_ON(!link_conf)) + return -EINVAL; + + err = iwl_mld_add_link(mld, link_conf); + if (err) + goto remove_added_links; + } + } + + /* + * Ensure we always have a valid primary_link. When using multiple + * links the proper value is set in assign_vif_chanctx. + */ + mld_vif->emlsr.primary = new_links ? __ffs(new_links) : 0; + + /* + * Special MLO restart case. We did not have a link when the interface + * was added, so do the power configuration now. + */ + if (old_links == 0 && mld->fw_status.in_hw_restart) + iwl_mld_update_mac_power(mld, vif, false); + + return 0; + +remove_added_links: + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (!(added & BIT(i))) + continue; + + link_conf = link_conf_dereference_protected(vif, i); + if (!link_conf || !iwl_mld_link_from_mac80211(link_conf)) + continue; + + iwl_mld_remove_link(mld, link_conf); + } + + return err; +} + +static int iwl_mld_change_sta_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_update_link_stas(mld, vif, sta, old_links, new_links); +} + +static int iwl_mld_mac80211_join_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + return iwl_mld_start_ap_ibss(hw, vif, &vif->bss_conf); +} + +static void iwl_mld_mac80211_leave_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + return iwl_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); +} + +static int iwl_mld_mac80211_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return mld->ibss_manager; +} + +#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (5 * HZ) + +static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS, + iwl_mld_get_primary_link(vif)); + if (ret) + return; + + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.tmp_non_bss_done_wk, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT); +} + +static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + IWL_DEBUG_MAC80211(mld, "prep_add_interface: type=%u\n", type); + + if (!(type == NL80211_IFTYPE_AP || + type == NL80211_IFTYPE_P2P_GO || + type == NL80211_IFTYPE_P2P_CLIENT)) + return; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_tmp_non_bss, + NULL); +} + +static int iwl_mld_set_hw_timestamp(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_set_hw_timestamp *hwts) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + u32 protocols = 0; + + /* HW timestamping is only supported for a specific station */ + if (!hwts->macaddr) + return -EOPNOTSUPP; + + if (hwts->enable) + protocols = + IWL_TIME_SYNC_PROTOCOL_TM | IWL_TIME_SYNC_PROTOCOL_FTM; + + return iwl_mld_time_sync_config(mld, hwts->macaddr, protocols); +} + +static int iwl_mld_start_pmsr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *request) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_ftm_start(mld, vif, request); +} + +const struct ieee80211_ops iwl_mld_hw_ops = { + .tx = iwl_mld_mac80211_tx, + .start = iwl_mld_mac80211_start, + .stop = iwl_mld_mac80211_stop, + .config = iwl_mld_mac80211_config, + .add_interface = iwl_mld_mac80211_add_interface, + .remove_interface = iwl_mld_mac80211_remove_interface, + .conf_tx = iwl_mld_mac80211_conf_tx, + .prepare_multicast = iwl_mld_mac80211_prepare_multicast, + .configure_filter = iwl_mld_mac80211_configure_filter, + .reconfig_complete = iwl_mld_mac80211_reconfig_complete, + .wake_tx_queue = iwl_mld_mac80211_wake_tx_queue, + .add_chanctx = iwl_mld_add_chanctx, + .remove_chanctx = iwl_mld_remove_chanctx, + .change_chanctx = iwl_mld_change_chanctx, + .assign_vif_chanctx = iwl_mld_assign_vif_chanctx, + .unassign_vif_chanctx = iwl_mld_unassign_vif_chanctx, + .set_rts_threshold = iwl_mld_mac80211_set_rts_threshold, + .link_info_changed = iwl_mld_mac80211_link_info_changed, + .vif_cfg_changed = iwl_mld_mac80211_vif_cfg_changed, + .set_key = iwl_mld_mac80211_set_key, + .hw_scan = iwl_mld_mac80211_hw_scan, + .cancel_hw_scan = iwl_mld_mac80211_cancel_hw_scan, + .sched_scan_start = iwl_mld_mac80211_sched_scan_start, + .sched_scan_stop = iwl_mld_mac80211_sched_scan_stop, + .mgd_prepare_tx = iwl_mld_mac80211_mgd_prepare_tx, + .mgd_complete_tx = iwl_mld_mac_mgd_complete_tx, + .sta_state = iwl_mld_mac80211_sta_state, + .sta_statistics = iwl_mld_mac80211_sta_statistics, + .flush = iwl_mld_mac80211_flush, + .flush_sta = iwl_mld_mac80211_flush_sta, + .ampdu_action = iwl_mld_mac80211_ampdu_action, + .can_aggregate_in_amsdu = iwl_mld_mac80211_can_aggregate, + .sync_rx_queues = iwl_mld_mac80211_sync_rx_queues, + .link_sta_rc_update = iwl_mld_sta_rc_update, + .start_ap = iwl_mld_start_ap_ibss, + .stop_ap = iwl_mld_stop_ap_ibss, + .pre_channel_switch = iwl_mld_pre_channel_switch, + .channel_switch = iwl_mld_channel_switch, + .post_channel_switch = iwl_mld_post_channel_switch, + .abort_channel_switch = iwl_mld_abort_channel_switch, + .switch_vif_chanctx = iwl_mld_switch_vif_chanctx, + .sta_pre_rcu_remove = iwl_mld_sta_pre_rcu_remove, + .remain_on_channel = iwl_mld_start_roc, + .cancel_remain_on_channel = iwl_mld_cancel_roc, + .can_activate_links = iwl_mld_can_activate_links, + .change_vif_links = iwl_mld_change_vif_links, + .change_sta_links = iwl_mld_change_sta_links, +#ifdef CONFIG_PM_SLEEP + .suspend = iwl_mld_suspend, + .resume = iwl_mld_resume, + .set_wakeup = iwl_mld_set_wakeup, + .set_rekey_data = iwl_mld_set_rekey_data, +#if IS_ENABLED(CONFIG_IPV6) + .ipv6_addr_change = iwl_mld_ipv6_addr_change, +#endif /* IS_ENABLED(CONFIG_IPV6) */ +#endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_IWLWIFI_DEBUGFS + .vif_add_debugfs = iwl_mld_add_vif_debugfs, + .link_add_debugfs = iwl_mld_add_link_debugfs, + .link_sta_add_debugfs = iwl_mld_add_link_sta_debugfs, +#endif + .mgd_protect_tdls_discover = iwl_mld_mac80211_mgd_protect_tdls_discover, + .join_ibss = iwl_mld_mac80211_join_ibss, + .leave_ibss = iwl_mld_mac80211_leave_ibss, + .tx_last_beacon = iwl_mld_mac80211_tx_last_beacon, + .prep_add_interface = iwl_mld_prep_add_interface, + .set_hw_timestamp = iwl_mld_set_hw_timestamp, + .start_pmsr = iwl_mld_start_pmsr, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h new file mode 100644 index 000000000000..aad04d7b2617 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_mac80211_h__ +#define __iwl_mld_mac80211_h__ + +#include "mld.h" + +int iwl_mld_register_hw(struct iwl_mld *mld); +void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld); + +#endif /* __iwl_mld_mac80211_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c new file mode 100644 index 000000000000..daca14e208bd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ + +#include +#include + +#include +#include + +#include "mld.h" +#include "hcmd.h" +#include "mcc.h" + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_parse_mcc_update_resp_v8(const struct iwl_rx_packet *pkt) +{ + const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data; + int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); + struct iwl_mcc_update_resp_v8 *resp_cp; + int notif_len = struct_size(resp_cp, channels, n_channels); + + if (iwl_rx_packet_payload_len(pkt) != notif_len) + return ERR_PTR(-EINVAL); + + resp_cp = kmemdup(mcc_resp_v8, notif_len, GFP_KERNEL); + if (!resp_cp) + return ERR_PTR(-ENOMEM); + + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_parse_mcc_update_resp_v5_v6(const struct iwl_rx_packet *pkt) +{ + const struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (const void *)pkt->data; + struct iwl_mcc_update_resp_v8 *resp_cp; + int n_channels = __le32_to_cpu(mcc_resp_v4->n_channels); + int resp_len; + + if (iwl_rx_packet_payload_len(pkt) != + struct_size(mcc_resp_v4, channels, n_channels)) + return ERR_PTR(-EINVAL); + + resp_len = struct_size(resp_cp, channels, n_channels); + resp_cp = kzalloc(resp_len, GFP_KERNEL); + if (!resp_cp) + return ERR_PTR(-ENOMEM); + + resp_cp->status = mcc_resp_v4->status; + resp_cp->mcc = mcc_resp_v4->mcc; + resp_cp->cap = cpu_to_le32(le16_to_cpu(mcc_resp_v4->cap)); + resp_cp->source_id = mcc_resp_v4->source_id; + resp_cp->geo_info = mcc_resp_v4->geo_info; + resp_cp->n_channels = mcc_resp_v4->n_channels; + memcpy(resp_cp->channels, mcc_resp_v4->channels, + n_channels * sizeof(__le32)); + + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2, + enum iwl_mcc_source src_id) +{ + int resp_ver = iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, + MCC_UPDATE_CMD, 0); + struct iwl_mcc_update_cmd mcc_update_cmd = { + .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), + .source_id = (u8)src_id, + }; + struct iwl_mcc_update_resp_v8 *resp_cp; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = MCC_UPDATE_CMD, + .flags = CMD_WANT_SKB, + .data = { &mcc_update_cmd }, + .len[0] = sizeof(mcc_update_cmd), + }; + int ret; + u16 mcc; + + IWL_DEBUG_LAR(mld, "send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], src_id); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + return ERR_PTR(ret); + + pkt = cmd.resp_pkt; + + /* For Wifi-7 radios, we get version 8 + * For Wifi-6E radios, we get version 6 + * For Wifi-6 radios, we get version 5, but 5, 6, and 4 are compatible. + */ + switch (resp_ver) { + case 5: + case 6: + resp_cp = iwl_mld_parse_mcc_update_resp_v5_v6(pkt); + break; + case 8: + resp_cp = iwl_mld_parse_mcc_update_resp_v8(pkt); + break; + default: + IWL_FW_CHECK_FAILED(mld, "Unknown MCC_UPDATE_CMD version %d\n", resp_ver); + resp_cp = ERR_PTR(-EINVAL); + } + + if (IS_ERR(resp_cp)) + goto exit; + + mcc = le16_to_cpu(resp_cp->mcc); + + IWL_FW_CHECK(mld, !mcc, "mcc can't be 0: %d\n", mcc); + + IWL_DEBUG_LAR(mld, + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c')\n", + le32_to_cpu(resp_cp->status), mcc, mcc >> 8, mcc & 0xff); + +exit: + iwl_free_resp(&cmd); + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +struct ieee80211_regdomain * +iwl_mld_get_regdomain(struct iwl_mld *mld, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed) +{ + struct ieee80211_regdomain *regd = NULL; + struct iwl_mcc_update_resp_v8 *resp; + u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP, + MCC_UPDATE_CMD, 0); + + IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2); + + lockdep_assert_wiphy(mld->wiphy); + + resp = iwl_mld_update_mcc(mld, alpha2, src_id); + if (IS_ERR(resp)) { + IWL_DEBUG_LAR(mld, "Could not get update from FW %ld\n", + PTR_ERR(resp)); + resp = NULL; + goto out; + } + + if (changed) { + u32 status = le32_to_cpu(resp->status); + + *changed = (status == MCC_RESP_NEW_CHAN_PROFILE || + status == MCC_RESP_ILLEGAL); + } + IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver); + + regd = iwl_parse_nvm_mcc_info(mld->trans->dev, mld->cfg, + __le32_to_cpu(resp->n_channels), + resp->channels, + __le16_to_cpu(resp->mcc), + __le16_to_cpu(resp->geo_info), + le32_to_cpu(resp->cap), resp_ver); + + if (IS_ERR(regd)) { + IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n", + PTR_ERR(regd)); + goto out; + } + + IWL_DEBUG_LAR(mld, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", + regd->alpha2, regd->alpha2[0], + regd->alpha2[1], resp->source_id); + + mld->mcc_src = resp->source_id; + + if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, + le16_to_cpu(resp->mcc))) + ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); + else + __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags); + +out: + kfree(resp); + return regd; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct ieee80211_regdomain * +iwl_mld_get_current_regdomain(struct iwl_mld *mld, + bool *changed) +{ + return iwl_mld_get_regdomain(mld, "ZZ", + MCC_SOURCE_GET_CURRENT, changed); +} + +void iwl_mld_update_changed_regdomain(struct iwl_mld *mld) +{ + struct ieee80211_regdomain *regd; + bool changed; + + regd = iwl_mld_get_current_regdomain(mld, &changed); + + if (IS_ERR_OR_NULL(regd)) + return; + + if (changed) + regulatory_set_wiphy_regd(mld->wiphy, regd); + kfree(regd); +} + +static int iwl_mld_apply_last_mcc(struct iwl_mld *mld, + const char *alpha2) +{ + struct ieee80211_regdomain *regd; + u32 used_src; + bool changed; + int ret; + + /* save the last source in case we overwrite it below */ + used_src = mld->mcc_src; + + /* Notify the firmware we support wifi location updates */ + regd = iwl_mld_get_current_regdomain(mld, NULL); + if (!IS_ERR_OR_NULL(regd)) + kfree(regd); + + /* Now set our last stored MCC and source */ + regd = iwl_mld_get_regdomain(mld, alpha2, used_src, + &changed); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + /* update cfg80211 if the regdomain was changed */ + if (changed) + ret = regulatory_set_wiphy_regd_sync(mld->wiphy, regd); + else + ret = 0; + + kfree(regd); + return ret; +} + +int iwl_mld_init_mcc(struct iwl_mld *mld) +{ + const struct ieee80211_regdomain *r; + struct ieee80211_regdomain *regd; + char mcc[3]; + int retval; + + /* try to replay the last set MCC to FW */ + r = wiphy_dereference(mld->wiphy, mld->wiphy->regd); + + if (r) + return iwl_mld_apply_last_mcc(mld, r->alpha2); + + regd = iwl_mld_get_current_regdomain(mld, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + if (!iwl_bios_get_mcc(&mld->fwrt, mcc)) { + kfree(regd); + regd = iwl_mld_get_regdomain(mld, mcc, MCC_SOURCE_BIOS, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + } + + retval = regulatory_set_wiphy_regd_sync(mld->wiphy, regd); + + kfree(regd); + return retval; +} + +static void iwl_mld_find_assoc_vif_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *assoc = data; + + if (vif->type == NL80211_IFTYPE_STATION && + vif->cfg.assoc) + *assoc = true; +} + +static bool iwl_mld_is_a_vif_assoc(struct iwl_mld *mld) +{ + bool assoc = false; + + ieee80211_iterate_active_interfaces_atomic(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_find_assoc_vif_iterator, + &assoc); + return assoc; +} + +void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt) +{ + struct iwl_mcc_chub_notif *notif = (void *)pkt->data; + enum iwl_mcc_source src; + char mcc[3]; + struct ieee80211_regdomain *regd; + bool changed; + + lockdep_assert_wiphy(mld->wiphy); + + if (iwl_mld_is_a_vif_assoc(mld) && + notif->source_id == MCC_SOURCE_WIFI) { + IWL_DEBUG_LAR(mld, "Ignore mcc update while associated\n"); + return; + } + + mcc[0] = le16_to_cpu(notif->mcc) >> 8; + mcc[1] = le16_to_cpu(notif->mcc) & 0xff; + mcc[2] = '\0'; + src = notif->source_id; + + IWL_DEBUG_LAR(mld, + "RX: received chub update mcc cmd (mcc '%s' src %d)\n", + mcc, src); + regd = iwl_mld_get_regdomain(mld, mcc, src, &changed); + if (IS_ERR_OR_NULL(regd)) + return; + + if (changed) + regulatory_set_wiphy_regd(mld->hw->wiphy, regd); + kfree(regd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.h b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h new file mode 100644 index 000000000000..2b31e5b5e2ed --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_mcc_h__ +#define __iwl_mld_mcc_h__ + +int iwl_mld_init_mcc(struct iwl_mld *mld); +void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_update_changed_regdomain(struct iwl_mld *mld); +struct ieee80211_regdomain * +iwl_mld_get_regdomain(struct iwl_mld *mld, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed); + +#endif /* __iwl_mld_mcc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c new file mode 100644 index 000000000000..d8499a05e724 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include + +#include "fw/api/rx.h" +#include "fw/api/datapath.h" +#include "fw/api/commands.h" +#include "fw/api/offload.h" +#include "fw/api/coex.h" +#include "fw/dbg.h" +#include "fw/uefi.h" + +#include "mld.h" +#include "mlo.h" +#include "mac80211.h" +#include "led.h" +#include "scan.h" +#include "tx.h" +#include "sta.h" +#include "regulatory.h" +#include "thermal.h" +#include "low_latency.h" +#include "hcmd.h" +#include "fw/api/location.h" + +#define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux" +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IWLWIFI); + +static const struct iwl_op_mode_ops iwl_mld_ops; + +static int __init iwl_mld_init(void) +{ + int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops); + + if (ret) + pr_err("Unable to register MLD op_mode: %d\n", ret); + + return ret; +} +module_init(iwl_mld_init); + +static void __exit iwl_mld_exit(void) +{ + iwl_opmode_deregister("iwlmld"); +} +module_exit(iwl_mld_exit); + +VISIBLE_IF_IWLWIFI_KUNIT +void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_cfg *cfg, const struct iwl_fw *fw, + struct ieee80211_hw *hw, struct dentry *dbgfs_dir) +{ + mld->dev = trans->dev; + mld->trans = trans; + mld->cfg = cfg; + mld->fw = fw; + mld->hw = hw; + mld->wiphy = hw->wiphy; + mld->debugfs_dir = dbgfs_dir; + + iwl_notification_wait_init(&mld->notif_wait); + + /* Setup async RX handling */ + spin_lock_init(&mld->async_handlers_lock); + INIT_LIST_HEAD(&mld->async_handlers_list); + wiphy_work_init(&mld->async_handlers_wk, + iwl_mld_async_handlers_wk); + + /* Dynamic Queue Allocation */ + spin_lock_init(&mld->add_txqs_lock); + INIT_LIST_HEAD(&mld->txqs_to_add); + wiphy_work_init(&mld->add_txqs_wk, iwl_mld_add_txqs_wk); + + /* Setup RX queues sync wait queue */ + init_waitqueue_head(&mld->rxq_sync.waitq); +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_construct_mld); + +static void __acquires(&mld->wiphy->mtx) +iwl_mld_fwrt_dump_start(void *ctx) +{ + struct iwl_mld *mld = ctx; + + wiphy_lock(mld->wiphy); +} + +static void __releases(&mld->wiphy->mtx) +iwl_mld_fwrt_dump_end(void *ctx) +{ + struct iwl_mld *mld = ctx; + + wiphy_unlock(mld->wiphy); +} + +static bool iwl_mld_d3_debug_enable(void *ctx) +{ + return IWL_MLD_D3_DEBUG; +} + +static int iwl_mld_fwrt_send_hcmd(void *ctx, struct iwl_host_cmd *host_cmd) +{ + struct iwl_mld *mld = (struct iwl_mld *)ctx; + int ret; + + wiphy_lock(mld->wiphy); + ret = iwl_mld_send_cmd(mld, host_cmd); + wiphy_unlock(mld->wiphy); + + return ret; +} + +static const struct iwl_fw_runtime_ops iwl_mld_fwrt_ops = { + .dump_start = iwl_mld_fwrt_dump_start, + .dump_end = iwl_mld_fwrt_dump_end, + .send_hcmd = iwl_mld_fwrt_send_hcmd, + .d3_debug_enable = iwl_mld_d3_debug_enable, +}; + +static void +iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_fw *fw, + struct dentry *debugfs_dir) +{ + iwl_fw_runtime_init(&mld->fwrt, trans, fw, &iwl_mld_fwrt_ops, mld, + NULL, NULL, debugfs_dir); + + iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR); +} + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { + HCMD_NAME(UCODE_ALIVE_NTFY), + HCMD_NAME(INIT_COMPLETE_NOTIF), + HCMD_NAME(PHY_CONTEXT_CMD), + HCMD_NAME(SCAN_CFG_CMD), + HCMD_NAME(SCAN_REQ_UMAC), + HCMD_NAME(SCAN_ABORT_UMAC), + HCMD_NAME(SCAN_COMPLETE_UMAC), + HCMD_NAME(TX_CMD), + HCMD_NAME(TXPATH_FLUSH), + HCMD_NAME(LEDS_CMD), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION), + HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), + HCMD_NAME(POWER_TABLE_CMD), + HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + HCMD_NAME(BEACON_NOTIFICATION), + HCMD_NAME(BEACON_TEMPLATE_CMD), + HCMD_NAME(TX_ANT_CONFIGURATION_CMD), + HCMD_NAME(REDUCE_TX_POWER_CMD), + HCMD_NAME(MISSED_BEACONS_NOTIFICATION), + HCMD_NAME(MAC_PM_POWER_TABLE), + HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(RSS_CONFIG_CMD), + HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), + HCMD_NAME(REPLY_RX_MPDU_CMD), + HCMD_NAME(BA_NOTIF), + HCMD_NAME(MCC_UPDATE_CMD), + HCMD_NAME(MCC_CHUB_UPDATE_CMD), + HCMD_NAME(MCAST_FILTER_CMD), + HCMD_NAME(REPLY_BEACON_FILTERING_CMD), + HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD), + HCMD_NAME(MATCH_FOUND_NOTIFICATION), + HCMD_NAME(WOWLAN_PATTERNS), + HCMD_NAME(WOWLAN_CONFIGURATION), + HCMD_NAME(WOWLAN_TSC_RSC_PARAM), + HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL), + HCMD_NAME(DEBUG_HOST_COMMAND), + HCMD_NAME(LDBG_CONFIG_CMD), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_system_names[] = { + HCMD_NAME(SHARED_MEM_CFG_CMD), + HCMD_NAME(SOC_CONFIGURATION_CMD), + HCMD_NAME(INIT_EXTENDED_CFG_CMD), + HCMD_NAME(FW_ERROR_RECOVERY_CMD), + HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), + HCMD_NAME(SYSTEM_STATISTICS_CMD), + HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = { + HCMD_NAME(LARI_CONFIG_CHANGE), + HCMD_NAME(NVM_GET_INFO), + HCMD_NAME(TAS_CONFIG), + HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD), + HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_debug_names[] = { + HCMD_NAME(HOST_EVENT_CFG), + HCMD_NAME(DBGC_SUSPEND_RESUME), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = { + HCMD_NAME(LOW_LATENCY_CMD), + HCMD_NAME(SESSION_PROTECTION_CMD), + HCMD_NAME(MAC_CONFIG_CMD), + HCMD_NAME(LINK_CONFIG_CMD), + HCMD_NAME(STA_CONFIG_CMD), + HCMD_NAME(AUX_STA_CMD), + HCMD_NAME(STA_REMOVE_CMD), + HCMD_NAME(ROC_CMD), + HCMD_NAME(MISSED_BEACONS_NOTIF), + HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF), + HCMD_NAME(ROC_NOTIF), + HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF), + HCMD_NAME(SESSION_PROTECTION_NOTIF), + HCMD_NAME(PROBE_RESPONSE_DATA_NOTIF), + HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_data_path_names[] = { + HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), + HCMD_NAME(WNM_PLATFORM_PTM_REQUEST_CMD), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), + HCMD_NAME(RFH_QUEUE_CONFIG_CMD), + HCMD_NAME(TLC_MNG_CONFIG_CMD), + HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD), + HCMD_NAME(SCD_QUEUE_CONFIG_CMD), + HCMD_NAME(OMI_SEND_STATUS_NOTIF), + HCMD_NAME(ESR_MODE_NOTIF), + HCMD_NAME(MONITOR_NOTIF), + HCMD_NAME(TLC_MNG_UPDATE_NOTIF), + HCMD_NAME(MU_GROUP_MGMT_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_location_names[] = { + HCMD_NAME(TOF_RANGE_REQ_CMD), + HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_phy_names[] = { + HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + HCMD_NAME(CTDP_CONFIG_CMD), + HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD), + HCMD_NAME(PER_CHAIN_LIMIT_OFFSET_CMD), + HCMD_NAME(CT_KILL_NOTIFICATION), + HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_statistics_names[] = { + HCMD_NAME(STATISTICS_OPER_NOTIF), + HCMD_NAME(STATISTICS_OPER_PART1_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = { + HCMD_NAME(STORED_BEACON_NTF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_coex_names[] = { + HCMD_NAME(PROFILE_NOTIF), +}; + +VISIBLE_IF_IWLWIFI_KUNIT +const struct iwl_hcmd_arr iwl_mld_groups[] = { + [LEGACY_GROUP] = HCMD_ARR(iwl_mld_legacy_names), + [LONG_GROUP] = HCMD_ARR(iwl_mld_legacy_names), + [SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names), + [MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names), + [DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names), + [LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names), + [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names), + [DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names), + [PHY_OPS_GROUP] = HCMD_ARR(iwl_mld_phy_names), + [STATISTICS_GROUP] = HCMD_ARR(iwl_mld_statistics_names), + [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mld_prot_offload_names), + [BT_COEX_GROUP] = HCMD_ARR(iwl_mld_coex_names), +}; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_groups); + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int global_iwl_mld_goups_size = ARRAY_SIZE(iwl_mld_groups); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size); +#endif + +static void +iwl_mld_configure_trans(struct iwl_op_mode *op_mode) +{ + const struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + static const u8 no_reclaim_cmds[] = {TX_CMD}; + struct iwl_trans_config trans_cfg = { + .op_mode = op_mode, + /* Rx is not supported yet, but add it to avoid warnings */ + .rx_buf_size = iwl_amsdu_size_to_rxb_size(), + .command_groups = iwl_mld_groups, + .command_groups_size = ARRAY_SIZE(iwl_mld_groups), + .fw_reset_handshake = true, + .queue_alloc_cmd_ver = + iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + 0), + .no_reclaim_cmds = no_reclaim_cmds, + .n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds), + .cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]), + }; + struct iwl_trans *trans = mld->trans; + + trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->iml = mld->fw->iml; + trans->iml_len = mld->fw->iml_len; + trans->wide_cmd_header = true; + + iwl_trans_configure(trans, &trans_cfg); +} + +/* + ***************************************************** + * op mode ops functions + ***************************************************** + */ + +#define NUM_FW_LOAD_RETRIES 3 +static struct iwl_op_mode * +iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, + const struct iwl_fw *fw, struct dentry *dbgfs_dir) +{ + struct ieee80211_hw *hw; + struct iwl_op_mode *op_mode; + struct iwl_mld *mld; + u32 eckv_value; + int ret; + + /* Allocate and initialize a new hardware device */ + hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + + sizeof(struct iwl_mld), + &iwl_mld_hw_ops); + if (!hw) + return ERR_PTR(-ENOMEM); + + op_mode = hw->priv; + + op_mode->ops = &iwl_mld_ops; + + mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); + + iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); + + iwl_mld_get_bios_tables(mld); + iwl_uefi_get_sgom_table(trans, &mld->fwrt); + iwl_uefi_get_step_table(trans); + if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) + IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); + else + trans->ext_32khz_clock_valid = !!eckv_value; + iwl_bios_setup_step(trans, &mld->fwrt); + mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); + + /* Configure transport layer with the opmode specific params */ + iwl_mld_configure_trans(op_mode); + + /* Needed for sending commands */ + wiphy_lock(mld->wiphy); + + for (int i = 0; i < NUM_FW_LOAD_RETRIES; i++) { + ret = iwl_mld_load_fw(mld); + if (!ret) + break; + } + + if (ret) { + wiphy_unlock(mld->wiphy); + iwl_fw_flush_dumps(&mld->fwrt); + goto free_hw; + } + + iwl_mld_stop_fw(mld); + + wiphy_unlock(mld->wiphy); + + ret = iwl_mld_leds_init(mld); + if (ret) + goto free_nvm; + + ret = iwl_mld_alloc_scan_cmd(mld); + if (ret) + goto leds_exit; + + ret = iwl_mld_low_latency_init(mld); + if (ret) + goto free_scan_cmd; + + ret = iwl_mld_register_hw(mld); + if (ret) + goto low_latency_free; + + iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant); + + iwl_mld_add_debugfs_files(mld, dbgfs_dir); + iwl_mld_thermal_initialize(mld); + + iwl_mld_ptp_init(mld); + + return op_mode; + +low_latency_free: + iwl_mld_low_latency_free(mld); +free_scan_cmd: + kfree(mld->scan.cmd); +leds_exit: + iwl_mld_leds_exit(mld); +free_nvm: + kfree(mld->nvm_data); +free_hw: + ieee80211_free_hw(mld->hw); + return ERR_PTR(ret); +} + +static void +iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_mld_ptp_remove(mld); + iwl_mld_leds_exit(mld); + + wiphy_lock(mld->wiphy); + iwl_mld_thermal_exit(mld); + iwl_mld_low_latency_stop(mld); + iwl_mld_deinit_time_sync(mld); + wiphy_unlock(mld->wiphy); + + ieee80211_unregister_hw(mld->hw); + + iwl_fw_runtime_free(&mld->fwrt); + iwl_mld_low_latency_free(mld); + + iwl_trans_op_mode_leave(mld->trans); + + kfree(mld->nvm_data); + kfree(mld->scan.cmd); + kfree(mld->error_recovery_buf); + kfree(mld->mcast_filter_cmd); + + ieee80211_free_hw(mld->hw); +} + +static void iwl_mld_queue_state_change(struct iwl_op_mode *op_mode, + int hw_queue, bool queue_full) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct ieee80211_txq *txq; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_txq *mld_txq; + + rcu_read_lock(); + + txq = rcu_dereference(mld->fw_id_to_txq[hw_queue]); + if (!txq) { + rcu_read_unlock(); + + if (queue_full) { + /* An internal queue is not expected to become full */ + IWL_WARN(mld, + "Internal hw_queue %d is full! stopping all queues\n", + hw_queue); + /* Stop all queues, as an internal queue is not + * mapped to a mac80211 one + */ + ieee80211_stop_queues(mld->hw); + } else { + ieee80211_wake_queues(mld->hw); + } + + return; + } + + mld_txq = iwl_mld_txq_from_mac80211(txq); + mld_sta = txq->sta ? iwl_mld_sta_from_mac80211(txq->sta) : NULL; + + mld_txq->status.stop_full = queue_full; + + if (!queue_full && mld_sta && + mld_sta->sta_state != IEEE80211_STA_NOTEXIST) { + local_bh_disable(); + iwl_mld_tx_from_txq(mld, txq); + local_bh_enable(); + } + + rcu_read_unlock(); +} + +static void +iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue) +{ + iwl_mld_queue_state_change(op_mode, hw_queue, true); +} + +static void +iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue) +{ + iwl_mld_queue_state_change(op_mode, hw_queue, false); +} + +static bool +iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_mld_set_hwkill(mld, state); + + return false; +} + +static void +iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + ieee80211_free_txskb(mld->hw, skb); +} + +static void iwl_mld_read_error_recovery_buffer(struct iwl_mld *mld) +{ + u32 src_size = mld->fw->ucode_capa.error_log_size; + u32 src_addr = mld->fw->ucode_capa.error_log_addr; + u8 *recovery_buf; + int ret; + + /* no recovery buffer size defined in a TLV */ + if (!src_size) + return; + + recovery_buf = kzalloc(src_size, GFP_ATOMIC); + if (!recovery_buf) + return; + + ret = iwl_trans_read_mem_bytes(mld->trans, src_addr, + recovery_buf, src_size); + if (ret) { + IWL_ERR(mld, "Failed to read error recovery buffer (%d)\n", + ret); + kfree(recovery_buf); + return; + } + + mld->error_recovery_buf = recovery_buf; +} + +static void iwl_mld_restart_nic(struct iwl_mld *mld) +{ + iwl_mld_read_error_recovery_buffer(mld); + + mld->fwrt.trans->dbg.restart_required = false; + + ieee80211_restart_hw(mld->hw); +} + +static void +iwl_mld_nic_error(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + bool trans_dead = test_bit(STATUS_TRANS_DEAD, &mld->trans->status); + + if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL) + IWL_ERR(mld, "Command queue full!\n"); + else if (!trans_dead && !mld->fw_status.do_not_dump_once) + iwl_fwrt_dump_error_logs(&mld->fwrt); + + mld->fw_status.do_not_dump_once = false; + + /* It is necessary to abort any os scan here because mac80211 requires + * having the scan cleared before restarting. + * We'll reset the scan_status to NONE in restart cleanup in + * the next drv_start() call from mac80211. If ieee80211_hw_restart + * isn't called scan status will stay busy. + */ + iwl_mld_report_scan_aborted(mld); + + /* + * This should be first thing before trying to collect any + * data to avoid endless loops if any HW error happens while + * collecting debug data. + * It might not actually be true that we'll restart, but the + * setting doesn't matter if we're going to be unbound either. + */ + if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) + mld->fw_status.in_hw_restart = true; +} + +static void iwl_mld_dump_error(struct iwl_op_mode *op_mode, + struct iwl_fw_error_dump_mode *mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + /* if we come in from opmode we have the mutex held */ + if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) { + lockdep_assert_wiphy(mld->wiphy); + iwl_fw_error_collect(&mld->fwrt); + } else { + wiphy_lock(mld->wiphy); + if (mode->context != IWL_ERR_CONTEXT_ABORT) + iwl_fw_error_collect(&mld->fwrt); + wiphy_unlock(mld->wiphy); + } +} + +static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + /* Do restart only in the following conditions are met: + * - we consider the FW as running + * - The trigger that brought us here is defined as one that requires + * a restart (in the debug TLVs) + */ + if (!mld->fw_status.running || !mld->fwrt.trans->dbg.restart_required) + return false; + + iwl_mld_restart_nic(mld); + return true; +} + +static void +iwl_mld_time_point(struct iwl_op_mode *op_mode, + enum iwl_fw_ini_time_point tp_id, + union iwl_dbg_tlv_tp_data *tp_data) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_dbg_tlv_time_point(&mld->fwrt, tp_id, tp_data); +} + +#ifdef CONFIG_PM_SLEEP +static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + wiphy_lock(mld->wiphy); + mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + iwl_mld_stop_fw(mld); + mld->fw_status.in_d3 = false; + wiphy_unlock(mld->wiphy); +} +#else +static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) +{} +#endif + +static const struct iwl_op_mode_ops iwl_mld_ops = { + .start = iwl_op_mode_mld_start, + .stop = iwl_op_mode_mld_stop, + .rx = iwl_mld_rx, + .rx_rss = iwl_mld_rx_rss, + .queue_full = iwl_mld_queue_full, + .queue_not_full = iwl_mld_queue_not_full, + .hw_rf_kill = iwl_mld_set_hw_rfkill_state, + .free_skb = iwl_mld_free_skb, + .nic_error = iwl_mld_nic_error, + .dump_error = iwl_mld_dump_error, + .sw_reset = iwl_mld_sw_reset, + .time_point = iwl_mld_time_point, + .device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off), +}; + +struct iwl_mld_mod_params iwlmld_mod_params = { + .power_scheme = IWL_POWER_SCHEME_BPS, +}; + +module_param_named(power_scheme, iwlmld_mod_params.power_scheme, int, 0444); +MODULE_PARM_DESC(power_scheme, + "power management scheme: 1-active, 2-balanced, default: 2"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h new file mode 100644 index 000000000000..6eda6081c8b4 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -0,0 +1,584 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_h__ +#define __iwl_mld_h__ + +#include +#include + +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "fw/runtime.h" +#include "fw/notif-wait.h" +#include "fw/api/commands.h" +#include "fw/api/scan.h" +#include "fw/api/mac-cfg.h" +#include "fw/api/mac.h" +#include "fw/api/phy-ctxt.h" +#include "fw/api/datapath.h" +#include "fw/api/rx.h" +#include "fw/api/rs.h" +#include "fw/api/context.h" +#include "fw/api/coex.h" +#include "fw/api/location.h" + +#include "fw/dbg.h" + +#include "notif.h" +#include "scan.h" +#include "rx.h" +#include "thermal.h" +#include "low_latency.h" +#include "constants.h" +#include "ptp.h" +#include "time_sync.h" + +/** + * DOC: Introduction + * + * iwlmld is an operation mode (a.k.a. op_mode) for Intel wireless devices. + * It is used for devices that ship after 2024 which typically support + * the WiFi-7 features. MLD stands for multi-link device. Note that there are + * devices that do not support WiFi-7 or even WiFi 6E and yet use iwlmld, but + * the firmware APIs used in this driver are WiFi-7 compatible. + * + * In the architecture of iwlwifi, an op_mode is a layer that translates + * mac80211's APIs into commands for the firmware and, of course, notifications + * from the firmware to mac80211's APIs. An op_mode must implement the + * interface defined in iwl-op-mode.h to interact with the transport layer + * which allows to send and receive data to the device, start the hardware, + * etc... + */ + +/** + * DOC: Locking policy + * + * iwlmld has a very simple locking policy: it doesn't have any mutexes. It + * relies on cfg80211's wiphy->mtx and takes the lock when needed. All the + * control flows originating from mac80211 already acquired the lock, so that + * part is trivial, but also notifications that are received from the firmware + * and handled asynchronously are handled only after having taken the lock. + * This is described in notif.c. + * There are spin_locks needed to synchronize with the data path, around the + * allocation of the queues, for example. + */ + +/** + * DOC: Debugfs + * + * iwlmld adds its share of debugfs hooks and its handlers are synchronized + * with the wiphy_lock using wiphy_locked_debugfs. This avoids races against + * resources deletion while the debugfs hook is being used. + */ + +/** + * DOC: Main resources + * + * iwlmld is designed with the life cycle of the resource in mind. The + * resources are: + * + * - struct iwl_mld (matches mac80211's struct ieee80211_hw) + * + * - struct iwl_mld_vif (matches macu80211's struct ieee80211_vif) + * iwl_mld_vif contains an array of pointers to struct iwl_mld_link + * which describe the links for this vif. + * + * - struct iwl_mld_sta (matches mac80211's struct ieee80211_sta) + * iwl_mld_sta contains an array of points to struct iwl_mld_link_sta + * which describes the link stations for this station + * + * Each object has properties that can survive a firmware reset or not. + * Asynchronous firmware notifications can declare themselves as dependent on a + * certain instance of those resources and that means that the notifications + * will be cancelled once the instance is destroyed. + */ + +#define IWL_MLD_MAX_ADDRESSES 5 + +/** + * struct iwl_mld - MLD op mode + * + * @fw_id_to_bss_conf: maps a fw id of a link to the corresponding + * ieee80211_bss_conf. + * @fw_id_to_vif: maps a fw id of a MAC context to the corresponding + * ieee80211_vif. Mapping is valid only when the MAC exists in the fw. + * @fw_id_to_txq: maps a fw id of a txq to the corresponding + * ieee80211_txq. + * @used_phy_ids: a bitmap of the phy IDs used. If a bit is set, it means + * that the index of this bit is already used as a PHY id. + * @num_igtks: the number if iGTKs that were sent to the FW. + * @monitor: monitor related data + * @monitor.on: does a monitor vif exist (singleton hence bool) + * @monitor.ampdu_ref: the id of the A-MPDU for sniffer + * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU + * @monitor.cur_aid: current association id tracked by the sniffer + * @monitor.cur_bssid: current bssid tracked by the sniffer + * @monitor.p80: primary channel position relative to he whole bandwidth, in + * steps of 80 MHz + * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding + * ieee80211_link_sta. This is not cleaned up on restart since we want to + * preserve the fw sta ids during a restart (for SN/PN restoring). + * FW ids of internal stations will be mapped to ERR_PTR, and will be + * re-allocated during a restart, so make sure to free it in restart + * cleanup using iwl_mld_free_internal_sta + * @netdetect: indicates the FW is in suspend mode with netdetect configured + * @p2p_device_vif: points to the p2p device vif if exists + * @bt_is_active: indicates that BT is active + * @dev: pointer to device struct. For printing purposes + * @trans: pointer to the transport layer + * @cfg: pointer to the device configuration + * @fw: a pointer to the fw object + * @hw: pointer to the hw object. + * @wiphy: a pointer to the wiphy struct, for easier access to it. + * @nvm_data: pointer to the nvm_data that includes all our capabilities + * @fwrt: fw runtime data + * @debugfs_dir: debugfs directory + * @notif_wait: notification wait related data. + * @async_handlers_list: a list of all async RX handlers. When a notifciation + * with an async handler is received, it is added to this list. + * When &async_handlers_wk runs - it runs these handlers one by one. + * @async_handlers_lock: a lock for &async_handlers_list. Sync + * &async_handlers_wk and RX notifcation path. + * @async_handlers_wk: A work to run all async RX handlers from + * &async_handlers_list. + * @ct_kill_exit_wk: worker to exit thermal kill + * @fw_status: bitmap of fw status bits + * @running: true if the firmware is running + * @do_not_dump_once: true if firmware dump must be prevented once + * @in_d3: indicates FW is in suspend mode and should be resumed + * @in_hw_restart: indicates that we are currently in restart flow. + * rather than restarted. Should be unset upon restart. + * @radio_kill: bitmap of radio kill status + * @radio_kill.hw: radio is killed by hw switch + * @radio_kill.ct: radio is killed because the device it too hot + * @addresses: device MAC addresses. + * @scan: instance of the scan object + * @wowlan: WoWLAN support data. + * @led: the led device + * @mcc_src: the source id of the MCC, comes from the firmware + * @bios_enable_puncturing: is puncturing enabled by bios + * @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data. + * @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions. + * the driver ensures that new BA sessions are blocked once the maximum + * supported by the firmware is reached, preventing firmware asserts. + * @rxq_sync: manages RX queue sync state + * @txqs_to_add: a list of &ieee80211_txq's to allocate in &add_txqs_wk + * @add_txqs_wk: a worker to allocate txqs. + * @add_txqs_lock: to lock the &txqs_to_add list. + * @error_recovery_buf: pointer to the recovery buffer that will be read + * from firmware upon fw/hw error and sent back to the firmware in + * reconfig flow (after NIC reset). + * @mcast_filter_cmd: pointer to the multicast filter command. + * @mgmt_tx_ant: stores the last TX antenna index; used for setting + * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure). + * @low_latency: low-latency manager. + * @tzone: thermal zone device's data + * @cooling_dev: cooling device's related data + * @ibss_manager: in IBSS mode (only one vif can be active), indicates what + * firmware indicated about having transmitted the last beacon, i.e. + * being IBSS manager for that time and needing to respond to probe + * requests + * @ptp_data: data of the PTP clock + * @time_sync: time sync data. + * @ftm_initiator: FTM initiator data + */ +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_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; + u8 num_igtks; + struct { + bool on; + u32 ampdu_ref; + bool ampdu_toggle; + u8 p80; +#ifdef CONFIG_IWLWIFI_DEBUGFS + __le16 cur_aid; + u8 cur_bssid[ETH_ALEN]; +#endif + } monitor; +#ifdef CONFIG_PM_SLEEP + bool netdetect; +#endif /* CONFIG_PM_SLEEP */ + struct ieee80211_vif *p2p_device_vif; + bool bt_is_active; + ); + struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX]; + /* And here fields that survive a fw restart */ + struct device *dev; + struct iwl_trans *trans; + const struct iwl_cfg *cfg; + const struct iwl_fw *fw; + struct ieee80211_hw *hw; + struct wiphy *wiphy; + struct iwl_nvm_data *nvm_data; + struct iwl_fw_runtime fwrt; + struct dentry *debugfs_dir; + struct iwl_notif_wait_data notif_wait; + struct list_head async_handlers_list; + spinlock_t async_handlers_lock; + struct wiphy_work async_handlers_wk; + struct wiphy_delayed_work ct_kill_exit_wk; + + struct { + u32 running:1, + do_not_dump_once:1, +#ifdef CONFIG_PM_SLEEP + in_d3:1, +#endif + in_hw_restart:1; + + } fw_status; + + struct { + u32 hw:1, + ct:1; + } radio_kill; + + struct mac_address addresses[IWL_MLD_MAX_ADDRESSES]; + struct iwl_mld_scan scan; +#ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan; +#endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_IWLWIFI_LEDS + struct led_classdev led; +#endif + enum iwl_mcc_source mcc_src; + bool bios_enable_puncturing; + + struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID]; + u8 num_rx_ba_sessions; + + struct iwl_mld_rx_queues_sync rxq_sync; + + struct list_head txqs_to_add; + struct wiphy_work add_txqs_wk; + spinlock_t add_txqs_lock; + + u8 *error_recovery_buf; + struct iwl_mcast_filter_cmd *mcast_filter_cmd; + + u8 mgmt_tx_ant; + + struct iwl_mld_low_latency low_latency; + + bool ibss_manager; +#ifdef CONFIG_THERMAL + struct thermal_zone_device *tzone; + struct iwl_mld_cooling_device cooling_dev; +#endif + + struct ptp_data ptp_data; + + struct iwl_mld_time_sync_data __rcu *time_sync; + + struct { + struct cfg80211_pmsr_request *req; + struct wireless_dev *req_wdev; + int responses[IWL_TOF_MAX_APS]; + } ftm_initiator; +}; + +/* memset the part of the struct that requires cleanup on restart */ +#define CLEANUP_STRUCT(_ptr) \ + memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \ + sizeof((_ptr)->zeroed_on_hw_restart)) + +/* Cleanup function for struct iwl_mld_vif, will be called in restart */ +static inline void +iwl_cleanup_mld(struct iwl_mld *mld) +{ + CLEANUP_STRUCT(mld); + CLEANUP_STRUCT(&mld->scan); + + mld->fw_status.in_d3 = false; + + iwl_mld_low_latency_restart_cleanup(mld); + + /* Empty the list of async notification handlers so we won't process + * notifications from the dead fw after the reconfig flow. + */ + iwl_mld_purge_async_handlers_list(mld); +} + +enum iwl_power_scheme { + IWL_POWER_SCHEME_CAM = 1, + IWL_POWER_SCHEME_BPS, +}; + +/** + * struct iwl_mld_mod_params - module parameters for iwlmld + * @power_scheme: one of enum iwl_power_scheme + */ +struct iwl_mld_mod_params { + int power_scheme; +}; + +extern struct iwl_mld_mod_params iwlmld_mod_params; + +/* Extract MLD priv from op_mode */ +#define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \ + ((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific) + +#define IWL_MAC80211_GET_MLD(_hw) \ + IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv)) + +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir); +#else +static inline void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) +{} +#endif + +int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld); +int iwl_mld_load_fw(struct iwl_mld *mld); +void iwl_mld_stop_fw(struct iwl_mld *mld); +int iwl_mld_start_fw(struct iwl_mld *mld); +void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags); + +static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state) +{ + mld->radio_kill.ct = state; + + wiphy_rfkill_set_hw_state(mld->wiphy, + mld->radio_kill.hw || mld->radio_kill.ct); +} + +static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state) +{ + mld->radio_kill.hw = state; + + wiphy_rfkill_set_hw_state(mld->wiphy, + mld->radio_kill.hw || mld->radio_kill.ct); +} + +static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld) +{ + u8 tx_ant = mld->fw->valid_tx_ant; + + if (mld->nvm_data && mld->nvm_data->valid_tx_ant) + tx_ant &= mld->nvm_data->valid_tx_ant; + + return tx_ant; +} + +static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld) +{ + u8 rx_ant = mld->fw->valid_rx_ant; + + if (mld->nvm_data && mld->nvm_data->valid_rx_ant) + rx_ant &= mld->nvm_data->valid_rx_ant; + + return rx_ant; +} + +static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + return PHY_BAND_24; + case NL80211_BAND_5GHZ: + return PHY_BAND_5; + case NL80211_BAND_6GHZ: + return PHY_BAND_6; + default: + WARN_ONCE(1, "Unsupported band (%u)\n", band); + return PHY_BAND_5; + } +} + +static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band) +{ + switch (phy_band) { + case PHY_BAND_24: + return NL80211_BAND_2GHZ; + case PHY_BAND_5: + return NL80211_BAND_5GHZ; + case PHY_BAND_6: + return NL80211_BAND_6GHZ; + default: + WARN_ONCE(1, "Unsupported phy band (%u)\n", phy_band); + return NL80211_BAND_5GHZ; + } +} + +static inline int +iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, + enum nl80211_band band) +{ + int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; + bool is_lb = band == NL80211_BAND_2GHZ; + + if (format == RATE_MCS_LEGACY_OFDM_MSK) + return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate; + + /* CCK is not allowed in 5 GHz */ + return is_lb ? rate : -1; +} + +extern const struct ieee80211_ops iwl_mld_hw_ops; + +/** + * enum iwl_rx_handler_context: context for Rx handler + * @RX_HANDLER_SYNC: this means that it will be called in the Rx path + * which can't acquire the wiphy->mutex. + * @RX_HANDLER_ASYNC: If the handler needs to hold wiphy->mutex + * (and only in this case!), it should be set as ASYNC. In that case, + * it will be called from a worker with wiphy->mutex held. + */ +enum iwl_rx_handler_context { + RX_HANDLER_SYNC, + RX_HANDLER_ASYNC, +}; + +/** + * struct iwl_rx_handler: handler for FW notification + * @val_fn: input validation function. + * @sizes: an array that mapps a version to the expected size. + * @fn: the function is called when notification is handled + * @cmd_id: command id + * @n_sizes: number of elements in &sizes. + * @context: see &iwl_rx_handler_context + * @obj_type: the type of the object that this handler is related to. + * See &iwl_mld_object_type. Use IWL_MLD_OBJECT_TYPE_NONE if not related. + * @cancel: function to cancel the notification. valid only if obj_type is not + * IWL_MLD_OBJECT_TYPE_NONE. + */ +struct iwl_rx_handler { + union { + bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt); + const struct iwl_notif_struct_size *sizes; + }; + void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt); + u16 cmd_id; + u8 n_sizes; + u8 context; + enum iwl_mld_object_type obj_type; + bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt, + u32 obj_id); +}; + +/** + * struct iwl_notif_struct_size: map a notif ver to the expected size + * + * @size: the size to expect + * @ver: the version of the notification + */ +struct iwl_notif_struct_size { + u32 size:24, ver:8; +}; + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +extern const struct iwl_hcmd_arr iwl_mld_groups[]; +extern const unsigned int global_iwl_mld_goups_size; +extern const struct iwl_rx_handler iwl_mld_rx_handlers[]; +extern const unsigned int iwl_mld_rx_handlers_num; + +bool +iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + const struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_rx_status *rx_status, int queue); + +void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_cfg *cfg, const struct iwl_fw *fw, + struct ieee80211_hw *hw, struct dentry *dbgfs_dir); +#endif + +#define IWL_MLD_INVALID_FW_ID 0xff + +#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \ +static int \ +iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \ + u8 *fw_id, \ + struct ieee80211_##_mac80211_type *mac80211_ptr) \ +{ \ + u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \ + u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_##_mac80211_type); \ + if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \ + struct ieee80211_link_sta)) \ + arr_sz = mld->fw->ucode_capa.num_stations; \ + if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \ + struct ieee80211_bss_conf)) \ + arr_sz = mld->fw->ucode_capa.num_links; \ + for (int i = 0; i < arr_sz; i++) { \ + u8 idx = (i + rand) % arr_sz; \ + if (rcu_access_pointer(mld->fw_id_to_##_mac80211_type[idx])) \ + continue; \ + IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n", idx, arr_sz); \ + *fw_id = idx; \ + rcu_assign_pointer(mld->fw_id_to_##_mac80211_type[idx], mac80211_ptr); \ + return 0; \ + } \ + return -ENOSPC; \ +} + +static inline struct ieee80211_bss_conf * +iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id) +{ + if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links, + "Invalid fw_link_id: %d\n", fw_link_id)) + return NULL; + + return wiphy_dereference(mld->wiphy, + mld->fw_id_to_bss_conf[fw_link_id]); +} + +#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024) + +void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir); +void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); + +/* Utilities */ + +static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac) +{ + static const u8 mac80211_ac_to_fw_tx_fifo[] = { + IWL_BZ_EDCA_TX_FIFO_VO, + IWL_BZ_EDCA_TX_FIFO_VI, + IWL_BZ_EDCA_TX_FIFO_BE, + IWL_BZ_EDCA_TX_FIFO_BK, + IWL_BZ_TRIG_TX_FIFO_VO, + IWL_BZ_TRIG_TX_FIFO_VI, + IWL_BZ_TRIG_TX_FIFO_BE, + IWL_BZ_TRIG_TX_FIFO_BK, + }; + return mac80211_ac_to_fw_tx_fifo[ac]; +} + +static inline u32 +iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band) +{ + if (!fw_has_capa(&mld->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT) || + band == NL80211_BAND_2GHZ) + return IWL_LMAC_24G_INDEX; + return IWL_LMAC_5G_INDEX; +} + +/* Check if we had an error, but reconfig flow didn't start yet */ +static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld) +{ + return mld->fw_status.in_hw_restart && + !iwl_trans_fw_running(mld->trans); +} + +int iwl_mld_tdls_sta_count(struct iwl_mld *mld); + +#endif /* __iwl_mld_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c new file mode 100644 index 000000000000..99c8501129b8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -0,0 +1,994 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mlo.h" + +/* Block reasons helper */ +#define HANDLE_EMLSR_BLOCKED_REASONS(HOW) \ + HOW(PREVENTION) \ + HOW(WOWLAN) \ + HOW(FW) \ + HOW(ROC) \ + HOW(NON_BSS) \ + HOW(TMP_NON_BSS) \ + HOW(TPT) + +static const char * +iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked) +{ + /* Using switch without "default" will warn about missing entries */ + switch (blocked) { +#define REASON_CASE(x) case IWL_MLD_EMLSR_BLOCKED_##x: return #x; + HANDLE_EMLSR_BLOCKED_REASONS(REASON_CASE) +#undef REASON_CASE + } + + return "ERROR"; +} + +static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) +{ +#define NAME_FMT(x) "%s" +#define NAME_PR(x) (mask & IWL_MLD_EMLSR_BLOCKED_##x) ? "[" #x "]" : "", + IWL_DEBUG_INFO(mld, + "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT) + " (0x%x)\n", + HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR) + mask); +#undef NAME_FMT +#undef NAME_PR +} + +/* Exit reasons helper */ +#define HANDLE_EMLSR_EXIT_REASONS(HOW) \ + HOW(BLOCK) \ + HOW(MISSED_BEACON) \ + HOW(FAIL_ENTRY) \ + HOW(CSA) \ + HOW(EQUAL_BAND) \ + HOW(BANDWIDTH) \ + HOW(LOW_RSSI) \ + HOW(LINK_USAGE) \ + HOW(BT_COEX) \ + HOW(CHAN_LOAD) \ + HOW(RFI) + +static const char * +iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit) +{ + /* Using switch without "default" will warn about missing entries */ + switch (exit) { +#define REASON_CASE(x) case IWL_MLD_EMLSR_EXIT_##x: return #x; + HANDLE_EMLSR_EXIT_REASONS(REASON_CASE) +#undef REASON_CASE + } + + return "ERROR"; +} + +static void iwl_mld_print_emlsr_exit(struct iwl_mld *mld, u32 mask) +{ +#define NAME_FMT(x) "%s" +#define NAME_PR(x) (mask & IWL_MLD_EMLSR_EXIT_##x) ? "[" #x "]" : "", + IWL_DEBUG_INFO(mld, + "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT) + " (0x%x)\n", + HANDLE_EMLSR_EXIT_REASONS(NAME_PR) + mask); +#undef NAME_FMT +#undef NAME_PR +} + +void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.prevent_done_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + if (WARN_ON(!(mld_vif->emlsr.blocked_reasons & + IWL_MLD_EMLSR_BLOCKED_PREVENTION))) + return; + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_PREVENTION); +} + +void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, + struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.prevent_done_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + if (WARN_ON(!(mld_vif->emlsr.blocked_reasons & + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS))) + return; + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS); +} + +#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 | \ + IWL_MLD_EMLSR_EXIT_LINK_USAGE) +#define IWL_MLD_PREVENT_EMLSR_TIMEOUT (HZ * 400) + +#define IWL_MLD_EMLSR_PREVENT_SHORT (HZ * 300) +#define IWL_MLD_EMLSR_PREVENT_LONG (HZ * 600) + +static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif, + enum iwl_mld_emlsr_exit reason) +{ + unsigned long delay; + + /* + * Reset the counter if more than 400 seconds have passed between one + * exit and the other, or if we exited due to a different reason. + * Will also reset the counter after the long prevention is done. + */ + if (time_after(jiffies, mld_vif->emlsr.last_exit_ts + + IWL_MLD_PREVENT_EMLSR_TIMEOUT) || + mld_vif->emlsr.last_exit_reason != reason) + mld_vif->emlsr.exit_repeat_count = 0; + + mld_vif->emlsr.last_exit_reason = reason; + mld_vif->emlsr.last_exit_ts = jiffies; + mld_vif->emlsr.exit_repeat_count++; + + /* + * Do not add a prevention when the reason was a block. For a block, + * EMLSR will be enabled again on unblock. + */ + if (reason == IWL_MLD_EMLSR_EXIT_BLOCK) + return; + + /* Set prevention for a minimum of 30 seconds */ + mld_vif->emlsr.blocked_reasons |= IWL_MLD_EMLSR_BLOCKED_PREVENTION; + delay = IWL_MLD_TRIGGER_LINK_SEL_TIME; + + /* Handle repeats for reasons that can cause long prevention */ + if (mld_vif->emlsr.exit_repeat_count > 1 && + reason & IWL_MLD_PREVENT_EMLSR_REASONS) { + if (mld_vif->emlsr.exit_repeat_count == 2) + delay = IWL_MLD_EMLSR_PREVENT_SHORT; + else + delay = IWL_MLD_EMLSR_PREVENT_LONG; + + /* + * The timeouts are chosen so that this will not happen, i.e. + * IWL_MLD_EMLSR_PREVENT_LONG > IWL_MLD_PREVENT_EMLSR_TIMEOUT + */ + WARN_ON(mld_vif->emlsr.exit_repeat_count > 3); + } + + IWL_DEBUG_INFO(mld, + "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n", + delay / HZ, mld_vif->emlsr.exit_repeat_count, + iwl_mld_get_emlsr_exit_string(reason), reason); + + wiphy_delayed_work_queue(mld->wiphy, + &mld_vif->emlsr.prevent_done_wk, delay); +} + +static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep, + bool sync) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + u16 new_active_links; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + + /* On entry failure need to exit anyway, even if entered from debugfs */ + if (exit != IWL_MLD_EMLSR_EXIT_FAIL_ENTRY && !IWL_MLD_AUTO_EML_ENABLE) + return 0; + + /* Ignore exit request if EMLSR is not active */ + if (!iwl_mld_emlsr_active(vif)) + return 0; + + if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mld_vif->authorized)) + return 0; + + if (WARN_ON(!(vif->active_links & BIT(link_to_keep)))) + link_to_keep = __ffs(vif->active_links); + + new_active_links = BIT(link_to_keep); + IWL_DEBUG_INFO(mld, + "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n", + iwl_mld_get_emlsr_exit_string(exit), exit, + vif->active_links, new_active_links); + + if (sync) + ret = ieee80211_set_active_links(vif, new_active_links); + else + ieee80211_set_active_links_async(vif, new_active_links); + + /* Update latest exit reason and check EMLSR prevention */ + iwl_mld_check_emlsr_prevention(mld, mld_vif, exit); + + return ret; +} + +void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep) +{ + _iwl_mld_exit_emlsr(mld, vif, exit, link_to_keep, false); +} + +static int _iwl_mld_emlsr_block(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, + u8 link_to_keep, bool sync) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif)) + return 0; + + if (mld_vif->emlsr.blocked_reasons & reason) + return 0; + + mld_vif->emlsr.blocked_reasons |= reason; + + IWL_DEBUG_INFO(mld, + "Blocking EMLSR mode. reason = %s (0x%x)\n", + iwl_mld_get_emlsr_blocked_string(reason), reason); + iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons); + + if (reason == IWL_MLD_EMLSR_BLOCKED_TPT) + wiphy_delayed_work_cancel(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + + return _iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BLOCK, + link_to_keep, sync); +} + +void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep) +{ + _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, false); +} + +int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep) +{ + return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true); +} + +static void _iwl_mld_select_links(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +void iwl_mld_trigger_link_selection(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + bool last_scan_was_recent = + time_before(jiffies, mld->scan.last_mlo_scan_jiffies + + IWL_MLD_SCAN_EXPIRE_TIME); + + if (last_scan_was_recent) { + IWL_DEBUG_EHT(mld, "MLO scan was recent, skip.\n"); + _iwl_mld_select_links(mld, vif); + } else { + IWL_DEBUG_EHT(mld, "Doing link selection after MLO scan\n"); + iwl_mld_int_mlo_scan(mld, vif); + } +} + +void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif)) + return; + + if (!(mld_vif->emlsr.blocked_reasons & reason)) + return; + + mld_vif->emlsr.blocked_reasons &= ~reason; + + IWL_DEBUG_INFO(mld, + "Unblocking EMLSR mode. reason = %s (0x%x)\n", + iwl_mld_get_emlsr_blocked_string(reason), reason); + iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons); + + if (reason == IWL_MLD_EMLSR_BLOCKED_TPT) + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); + + if (mld_vif->emlsr.blocked_reasons) + return; + + IWL_DEBUG_INFO(mld, "EMLSR is unblocked\n"); + iwl_mld_trigger_link_selection(mld, vif); +} + +static void +iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_esr_mode_notif *notif = (void *)data; + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + switch (le32_to_cpu(notif->action)) { + case ESR_RECOMMEND_ENTER: + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_FW); + break; + case ESR_RECOMMEND_LEAVE: + iwl_mld_block_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_FW, + iwl_mld_get_primary_link(vif)); + break; + case ESR_FORCE_LEAVE: + default: + /* ESR_FORCE_LEAVE should not happen at this point */ + IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n", + le32_to_cpu(notif->action)); + } +} + +void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_mode_notif, + pkt->data); +} + +static void +iwl_mld_vif_iter_disconnect_emlsr(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + ieee80211_connection_loss(vif); +} + +void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_esr_trans_fail_notif *notif = (const void *)pkt->data; + u32 fw_link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *bss_conf = + iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + + IWL_DEBUG_INFO(mld, "Failed to %s EMLSR on link %d (FW: %d), reason %d\n", + le32_to_cpu(notif->activation) ? "enter" : "exit", + bss_conf ? bss_conf->link_id : -1, + le32_to_cpu(notif->link_id), + le32_to_cpu(notif->err_code)); + + if (IWL_FW_CHECK(mld, !bss_conf, + "FW reported failure to %sactivate EMLSR on a non-existing link: %d\n", + le32_to_cpu(notif->activation) ? "" : "de", + fw_link_id)) { + ieee80211_iterate_active_interfaces_mtx( + mld->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_disconnect_emlsr, NULL); + return; + } + + /* Disconnect if we failed to deactivate a link */ + if (!le32_to_cpu(notif->activation)) { + ieee80211_connection_loss(bss_conf->vif); + return; + } + + /* + * We failed to activate the second link, go back to the link specified + * by the firmware as that is the one that is still valid now. + */ + iwl_mld_exit_emlsr(mld, bss_conf->vif, IWL_MLD_EMLSR_EXIT_FAIL_ENTRY, + bss_conf->link_id); +} + +/* Active non-station link tracking */ +static void iwl_mld_count_non_bss_links(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int *count = _data; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + return; + + *count += iwl_mld_count_active_links(mld_vif->mld, vif); +} + +struct iwl_mld_update_emlsr_block_data { + bool block; + int result; +}; + +static void +iwl_mld_vif_iter_update_emlsr_non_bss_block(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_update_emlsr_block_data *data = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (data->block) { + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_NON_BSS, + iwl_mld_get_primary_link(vif)); + if (ret) + data->result = ret; + } else { + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_NON_BSS); + } +} + +int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, + int pending_link_changes) +{ + /* An active link of a non-station vif blocks EMLSR. Upon activation + * block EMLSR on the bss vif. Upon deactivation, check if this link + * was the last non-station link active, and if so unblock the bss vif + */ + struct iwl_mld_update_emlsr_block_data block_data = {}; + int count = pending_link_changes; + + /* No need to count if we are activating a non-BSS link */ + if (count <= 0) + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_count_non_bss_links, + &count); + + /* + * We could skip updating it if the block change did not change (and + * pending_link_changes is non-zero). + */ + block_data.block = !!count; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_update_emlsr_non_bss_block, + &block_data); + + return block_data.result; +} + +#define EMLSR_SEC_LINK_MIN_PERC 10 +#define EMLSR_MIN_TX 3000 +#define EMLSR_MIN_RX 400 + +void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.check_tpt_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link *sec_link; + unsigned long total_tx = 0, total_rx = 0; + unsigned long sec_link_tx = 0, sec_link_rx = 0; + u8 sec_link_tx_perc, sec_link_rx_perc; + s8 sec_link_id; + + if (!iwl_mld_vif_has_emlsr_cap(vif) || !mld_vif->ap_sta) + return; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* We only count for the AP sta in a MLO connection */ + if (!mld_sta->mpdu_counters) + return; + + /* This wk should only run when the TPT blocker isn't set. + * When the blocker is set, the decision to remove it, as well as + * clearing the counters is done in DP (to avoid having a wk every + * 5 seconds when idle. When the blocker is unset, we are not idle anyway) + */ + if (WARN_ON(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) + return; + /* + * TPT is unblocked, need to check if the TPT criteria is still met. + * + * If EMLSR is active, then we also need to check the secondar link + * requirements. + */ + if (iwl_mld_emlsr_active(vif)) { + sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif)); + sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id); + if (WARN_ON_ONCE(!sec_link)) + return; + /* We need the FW ID here */ + sec_link_id = sec_link->fw_id; + } else { + sec_link_id = -1; + } + + /* Sum up RX and TX MPDUs from the different queues/links */ + for (int q = 0; q < mld->trans->num_rx_queues; q++) { + struct iwl_mld_per_q_mpdu_counter *queue_counter = + &mld_sta->mpdu_counters[q]; + + spin_lock_bh(&queue_counter->lock); + + /* The link IDs that doesn't exist will contain 0 */ + for (int link = 0; + link < ARRAY_SIZE(queue_counter->per_link); + link++) { + total_tx += queue_counter->per_link[link].tx; + total_rx += queue_counter->per_link[link].rx; + } + + if (sec_link_id != -1) { + sec_link_tx += queue_counter->per_link[sec_link_id].tx; + sec_link_rx += queue_counter->per_link[sec_link_id].rx; + } + + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + + spin_unlock_bh(&queue_counter->lock); + } + + IWL_DEBUG_INFO(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n", + total_tx, total_rx); + + /* If we don't have enough MPDUs - exit EMLSR */ + if (total_tx < IWL_MLD_ENTER_EMLSR_TPT_THRESH && + total_rx < IWL_MLD_ENTER_EMLSR_TPT_THRESH) { + iwl_mld_block_emlsr(mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT, + iwl_mld_get_primary_link(vif)); + return; + } + + /* EMLSR is not active */ + if (sec_link_id == -1) + return; + + IWL_DEBUG_INFO(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n", + sec_link_id, sec_link_tx, sec_link_rx); + + /* Calculate the percentage of the secondary link TX/RX */ + sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0; + sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0; + + /* + * The TX/RX percentage is checked only if it exceeds the required + * minimum. In addition, RX is checked only if the TX check failed. + */ + if ((total_tx > EMLSR_MIN_TX && + sec_link_tx_perc < EMLSR_SEC_LINK_MIN_PERC) || + (total_rx > EMLSR_MIN_RX && + sec_link_rx_perc < EMLSR_SEC_LINK_MIN_PERC)) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LINK_USAGE, + iwl_mld_get_primary_link(vif)); + return; + } + + /* Check again when the next window ends */ + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); +} + +void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.unblock_tpt_wk); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT); +} + +/* + * Link selection + */ +struct iwl_mld_link_sel_data { + u8 link_id; + const struct cfg80211_chan_def *chandef; + s32 signal; + u16 grade; +}; + +s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, + const struct cfg80211_chan_def *chandef, + bool low) +{ + if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ && + chandef->chan->band != NL80211_BAND_5GHZ && + chandef->chan->band != NL80211_BAND_6GHZ)) + return S8_MAX; + +#define RSSI_THRESHOLD(_low, _bw) \ + (_low) ? IWL_MLD_LOW_RSSI_THRESH_##_bw##MHZ \ + : IWL_MLD_HIGH_RSSI_THRESH_##_bw##MHZ + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + /* 320 MHz has the same thresholds as 20 MHz */ + case NL80211_CHAN_WIDTH_320: + return RSSI_THRESHOLD(low, 20); + case NL80211_CHAN_WIDTH_40: + return RSSI_THRESHOLD(low, 40); + case NL80211_CHAN_WIDTH_80: + return RSSI_THRESHOLD(low, 80); + case NL80211_CHAN_WIDTH_160: + return RSSI_THRESHOLD(low, 160); + default: + WARN_ON(1); + return S8_MAX; + } +#undef RSSI_THRESHOLD +} + +static u32 +iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *link, + bool primary) +{ + struct wiphy *wiphy = mld->wiphy; + struct ieee80211_bss_conf *conf; + enum iwl_mld_emlsr_exit ret = 0; + + conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); + if (WARN_ON_ONCE(!conf)) + return false; + + if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active) + ret |= IWL_MLD_EMLSR_EXIT_BT_COEX; + + if (link->signal < + iwl_mld_get_emlsr_rssi_thresh(mld, link->chandef, false)) + ret |= IWL_MLD_EMLSR_EXIT_LOW_RSSI; + + if (conf->csa_active) + ret |= IWL_MLD_EMLSR_EXIT_CSA; + + if (ret) { + IWL_DEBUG_INFO(mld, + "Link %d is not allowed for EMLSR as %s\n", + link->link_id, + primary ? "primary" : "secondary"); + iwl_mld_print_emlsr_exit(mld, ret); + } + + return ret; +} + +static u8 +iwl_mld_set_link_sel_data(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *data, + unsigned long usable_links, + u8 *best_link_idx) +{ + u8 n_data = 0; + u16 max_grade = 0; + unsigned long link_id; + + /* + * TODO: don't select links that weren't discovered in the last scan + * This requires mac80211 (or cfg80211) changes to forward/track when + * a BSS was last updated. cfg80211 already tracks this information but + * it is not exposed within the kernel. + */ + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + /* Ignore any BSS that was not seen in the last 5 seconds */ + if (ktime_before(link_conf->bss->ts_boottime, + ktime_sub_ns(ktime_get_boottime_ns(), + (u64)5 * NSEC_PER_SEC))) + continue; + + data[n_data].link_id = link_id; + data[n_data].chandef = &link_conf->chanreq.oper; + data[n_data].signal = MBM_TO_DBM(link_conf->bss->signal); + data[n_data].grade = iwl_mld_get_link_grade(mld, link_conf); + + if (n_data == 0 || data[n_data].grade > max_grade) { + max_grade = data[n_data].grade; + *best_link_idx = n_data; + } + n_data++; + } + + return n_data; +} + +static bool +iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + enum iwl_mld_emlsr_exit ret = 0; + + /* Per-link considerations */ + if (iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true) || + iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false)) + return false; + + if (a->chandef->chan->band == b->chandef->chan->band) { + ret |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + } else if (a->chandef->width != b->chandef->width) { + /* TODO: task=EMLSR task=statistics + * replace BANDWIDTH exit reason with channel load criteria + */ + ret |= IWL_MLD_EMLSR_EXIT_BANDWIDTH; + } + + if (ret) { + IWL_DEBUG_INFO(mld, + "Links %d and %d are not a valid pair for EMLSR\n", + a->link_id, b->link_id); + IWL_DEBUG_INFO(mld, + "Links bandwidth are: %d and %d\n", + nl80211_chan_width_to_mhz(a->chandef->width), + nl80211_chan_width_to_mhz(b->chandef->width)); + iwl_mld_print_emlsr_exit(mld, ret); + return false; + } + + return true; +} + +/* Calculation is done with fixed-point with a scaling factor of 1/256 */ +#define SCALE_FACTOR 256 + +/* + * Returns the combined grade of two given links. + * Returns 0 if EMLSR is not allowed with these 2 links. + */ +static +unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b, + u8 *primary_id) +{ + struct ieee80211_bss_conf *primary_conf; + struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy; + unsigned int primary_load; + + lockdep_assert_wiphy(wiphy); + + /* a is always primary, b is always secondary */ + if (b->grade > a->grade) + swap(a, b); + + *primary_id = a->link_id; + + if (!iwl_mld_valid_emlsr_pair(vif, a, b)) + return 0; + + primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]); + + if (WARN_ON_ONCE(!primary_conf)) + return 0; + + primary_load = iwl_mld_get_chan_load(mld, primary_conf); + + /* The more the primary link is loaded, the more worthwhile EMLSR becomes */ + return a->grade + ((b->grade * primary_load) / SCALE_FACTOR); +} + +static void _iwl_mld_select_links(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; + struct iwl_mld_link_sel_data *best_link; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int max_active_links = iwl_mld_max_active_links(mld, vif); + u16 new_active, usable_links = ieee80211_vif_usable_links(vif); + u8 best_idx, new_primary, n_data; + u16 max_grade; + + lockdep_assert_wiphy(mld->wiphy); + + if (!mld_vif->authorized || hweight16(usable_links) <= 1) + return; + + if (WARN(time_before(mld->scan.last_mlo_scan_jiffies, + jiffies - IWL_MLD_SCAN_EXPIRE_TIME), + "Last MLO scan was too long ago, can't select links\n")) + return; + + /* The logic below is simple and not suited for more than 2 links */ + WARN_ON_ONCE(max_active_links > 2); + + n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links, + &best_idx); + + if (WARN(!n_data, "Couldn't find a valid grade for any link!\n")) + return; + + /* Default to selecting the single best link */ + best_link = &data[best_idx]; + new_primary = best_link->link_id; + new_active = BIT(best_link->link_id); + max_grade = best_link->grade; + + /* If EMLSR is not possible, activate the best link */ + if (max_active_links == 1 || n_data == 1 || + !iwl_mld_vif_has_emlsr_cap(vif) || !IWL_MLD_AUTO_EML_ENABLE || + mld_vif->emlsr.blocked_reasons) + goto set_active; + + /* Try to find the best link combination */ + for (u8 a = 0; a < n_data; a++) { + for (u8 b = a + 1; b < n_data; b++) { + u8 best_in_pair; + u16 emlsr_grade = + iwl_mld_get_emlsr_grade(mld, vif, + &data[a], &data[b], + &best_in_pair); + + /* + * Prefer (new) EMLSR combination to prefer EMLSR over + * a single link. + */ + if (emlsr_grade < max_grade) + continue; + + max_grade = emlsr_grade; + new_primary = best_in_pair; + new_active = BIT(data[a].link_id) | + BIT(data[b].link_id); + } + } + +set_active: + IWL_DEBUG_INFO(mld, "Link selection result: 0x%x. Primary = %d\n", + new_active, new_primary); + + mld_vif->emlsr.selected_primary = new_primary; + mld_vif->emlsr.selected_links = new_active; + + ieee80211_set_active_links_async(vif, new_active); +} + +static void iwl_mld_vif_iter_select_links(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + + _iwl_mld_select_links(mld, vif); +} + +void iwl_mld_select_links(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_select_links, + NULL); +} + +void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + u8 other_link_id = iwl_mld_get_other_link(vif, link->link_id); + struct ieee80211_bss_conf *other_link = + link_conf_dereference_check(vif, other_link_id); + + if (!ieee80211_vif_link_active(vif, link->link_id) || + !iwl_mld_emlsr_active(vif) || + WARN_ON(link->link_id == other_link_id || !other_link)) + return; + + if (link->chanreq.oper.width != other_link->chanreq.oper.width) + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BANDWIDTH, + iwl_mld_get_primary_link(vif)); +} + +static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct ieee80211_bss_conf *link; + unsigned int link_id; + + if (!mld->bt_is_active) { + iwl_mld_retry_emlsr(mld, vif); + return; + } + + /* BT is turned ON but we are not in EMLSR, nothing to do */ + if (!iwl_mld_emlsr_active(vif)) + return; + + /* In EMLSR and BT is turned ON */ + + for_each_vif_active_link(vif, link, link_id) { + if (WARN_ON(!link->chanreq.oper.chan)) + continue; + + if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX, + iwl_mld_get_primary_link(vif)); + return; + } + } +} + +void iwl_mld_emlsr_check_bt(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_emlsr_check_bt_iter, + NULL); +} + +static void iwl_mld_emlsr_check_chan_load_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = (struct iwl_mld *)_data; + struct ieee80211_bss_conf *prim_link; + unsigned int prim_link_id; + int chan_load; + + if (!iwl_mld_emlsr_active(vif)) + return; + + prim_link_id = iwl_mld_get_primary_link(vif); + prim_link = link_conf_dereference_protected(vif, prim_link_id); + if (WARN_ON(!prim_link)) + return; + + chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link, true); + + if (chan_load < 0) + return; + + /* chan_load is in range [0,255] */ + if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_CHAN_LOAD_THRESH)) + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + prim_link_id); +} + +void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_emlsr_check_chan_load_iter, + (void *)(uintptr_t)mld); +} + +void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (!iwl_mld_vif_has_emlsr_cap(vif) || iwl_mld_emlsr_active(vif) || + mld_vif->emlsr.blocked_reasons) + return; + + iwl_mld_trigger_link_selection(mld, vif); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h new file mode 100644 index 000000000000..fd1abe8e6084 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_mlo_h__ +#define __iwl_mld_mlo_h__ + +#include +#include +#include +#include "iwl-config.h" +#include "iwl-trans.h" +#include "iface.h" + +struct iwl_mld; + +void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, + struct wiphy_work *wk); + +static inline bool iwl_mld_emlsr_active(struct ieee80211_vif *vif) +{ + /* Set on phy context activation, so should be a good proxy */ + return !!(vif->driver_flags & IEEE80211_VIF_EML_ACTIVE); +} + +static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* We only track/permit EMLSR state once authorized */ + if (!mld_vif->authorized) + return false; + + /* No EMLSR on dual radio devices */ + return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION && + ieee80211_vif_is_mld(vif) && + vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP && + !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->hw_rf_id); +} + +static inline int +iwl_mld_max_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_AP) + return mld->fw->ucode_capa.num_beacons; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + return IWL_FW_MAX_ACTIVE_LINKS_NUM; + + /* For now, do not accept more links on other interface types */ + return 1; +} + +static inline int +iwl_mld_count_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link; + int n_active = 0; + + for_each_mld_vif_valid_link(mld_vif, mld_link) { + if (rcu_access_pointer(mld_link->chan_ctx)) + n_active++; + } + + return n_active; +} + +static inline u8 iwl_mld_get_primary_link(struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld_vif->mld->wiphy); + + if (!ieee80211_vif_is_mld(vif) || WARN_ON(!vif->active_links)) + return 0; + + /* In AP mode, there is no primary link */ + if (vif->type == NL80211_IFTYPE_AP) + return __ffs(vif->active_links); + + if (iwl_mld_emlsr_active(vif) && + !WARN_ON(!(BIT(mld_vif->emlsr.primary) & vif->active_links))) + return mld_vif->emlsr.primary; + + return __ffs(vif->active_links); +} + +/* + * For non-MLO/single link, this will return the deflink/single active link, + * respectively + */ +static inline u8 iwl_mld_get_other_link(struct ieee80211_vif *vif, u8 link_id) +{ + switch (hweight16(vif->active_links)) { + case 0: + return 0; + default: + WARN_ON(1); + fallthrough; + case 1: + return __ffs(vif->active_links); + case 2: + return __ffs(vif->active_links & ~BIT(link_id)); + } +} + +s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, + const struct cfg80211_chan_def *chandef, + bool low); + +/* EMLSR block/unblock and exit */ +void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); +int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); +void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason); +void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep); + +int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, + int pending_link_changes); + +void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk); + +void iwl_mld_select_links(struct iwl_mld *mld); + +void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); + +void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld); + +void iwl_mld_trigger_link_selection(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +/** + * iwl_mld_retry_emlsr - Retry entering EMLSR + * @mld: MLD context + * @vif: VIF to retry EMLSR on + * + * Retry entering EMLSR on the given VIF. + * Use this if one of the parameters that can prevent EMLSR has changed. + */ +void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif); + +#endif /* __iwl_mld_mlo_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c new file mode 100644 index 000000000000..06caedf9b0ba --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -0,0 +1,758 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "notif.h" +#include "scan.h" +#include "iface.h" +#include "mlo.h" +#include "iwl-trans.h" +#include "fw/file.h" +#include "fw/dbg.h" +#include "fw/api/cmdhdr.h" +#include "fw/api/mac-cfg.h" +#include "session-protect.h" +#include "fw/api/time-event.h" +#include "fw/api/tx.h" +#include "fw/api/rs.h" +#include "fw/api/offload.h" +#include "fw/api/stats.h" +#include "fw/api/rfi.h" +#include "fw/api/coex.h" + +#include "mcc.h" +#include "link.h" +#include "tx.h" +#include "rx.h" +#include "tlc.h" +#include "agg.h" +#include "mac80211.h" +#include "thermal.h" +#include "roc.h" +#include "stats.h" +#include "coex.h" +#include "time_sync.h" +#include "ftm-initiator.h" + +/* Please use this in an increasing order of the versions */ +#define CMD_VER_ENTRY(_ver, _struct) \ + { .size = sizeof(struct _struct), .ver = _ver }, +#define CMD_VERSIONS(name, ...) \ + static const struct iwl_notif_struct_size \ + iwl_notif_struct_sizes_##name[] = { __VA_ARGS__ }; + +#define RX_HANDLER_NO_OBJECT(_grp, _cmd, _name, _context) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + .sizes = iwl_notif_struct_sizes_##_name, \ + .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \ + }, + +/* Use this for Rx handlers that do not need notification validation */ +#define RX_HANDLER_NO_VAL(_grp, _cmd, _name, _context) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + }, + +#define RX_HANDLER_VAL_FN(_grp, _cmd, _name, _context) \ + { .cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + .val_fn = iwl_mld_validate_##_name, \ + }, + +#define DEFINE_SIMPLE_CANCELLATION(name, notif_struct, id_member) \ +static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld, \ + struct iwl_rx_packet *pkt, \ + u32 obj_id) \ +{ \ + const struct notif_struct *notif = (const void *)pkt->data; \ + \ + return obj_id == _Generic((notif)->id_member, \ + __le32: le32_to_cpu((notif)->id_member), \ + __le16: le16_to_cpu((notif)->id_member), \ + u8: (notif)->id_member); \ +} + +static bool iwl_mld_always_cancel(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id) +{ + return true; +} + +/* Currently only defined for the RX_HANDLER_SIZES options. Use this for + * notifications that belong to a specific object, and that should be + * canceled when the object is removed + */ +#define RX_HANDLER_OF_OBJ(_grp, _cmd, _name, _obj_type) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + /* Only async handlers can be canceled */ \ + .context = RX_HANDLER_ASYNC, \ + .fn = iwl_mld_handle_##_name, \ + .sizes = iwl_notif_struct_sizes_##_name, \ + .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \ + .obj_type = IWL_MLD_OBJECT_TYPE_##_obj_type, \ + .cancel = iwl_mld_cancel_##_name, \ + }, + +#define RX_HANDLER_OF_LINK(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, LINK) \ + +#define RX_HANDLER_OF_VIF(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, VIF) \ + +#define RX_HANDLER_OF_STA(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, STA) \ + +#define RX_HANDLER_OF_ROC(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, ROC) + +#define RX_HANDLER_OF_SCAN(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, SCAN) + +#define RX_HANDLER_OF_FTM_REQ(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, FTM_REQ) + +static void iwl_mld_handle_mfuart_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data; + + IWL_DEBUG_INFO(mld, + "MFUART: installed ver: 0x%08x, external ver: 0x%08x\n", + le32_to_cpu(mfuart_notif->installed_ver), + le32_to_cpu(mfuart_notif->external_ver)); + IWL_DEBUG_INFO(mld, + "MFUART: status: 0x%08x, duration: 0x%08x image size: 0x%08x\n", + le32_to_cpu(mfuart_notif->status), + le32_to_cpu(mfuart_notif->duration), + le32_to_cpu(mfuart_notif->image_size)); +} + +static void iwl_mld_mu_mimo_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + unsigned int link_id = 0; + + if (WARN(hweight16(vif->active_links) > 1, + "no support for this notif while in EMLSR 0x%x\n", + vif->active_links)) + return; + + if (ieee80211_vif_is_mld(vif)) { + link_id = __ffs(vif->active_links); + bss_conf = link_conf_dereference_check(vif, link_id); + } + + if (!WARN_ON(!bss_conf) && bss_conf->mu_mimo_owner) { + const struct iwl_mu_group_mgmt_notif *notif = _data; + + BUILD_BUG_ON(sizeof(notif->membership_status) != + WLAN_MEMBERSHIP_LEN); + BUILD_BUG_ON(sizeof(notif->user_position) != + WLAN_USER_POSITION_LEN); + + /* MU-MIMO Group Id action frame is little endian. We treat + * the data received from firmware as if it came from the + * action frame, so no conversion is needed. + */ + ieee80211_update_mu_groups(vif, link_id, + (u8 *)¬if->membership_status, + (u8 *)¬if->user_position); + } +} + +/* This handler is called in SYNC mode because it needs to be serialized with + * Rx as specified in ieee80211_update_mu_groups()'s documentation. + */ +static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data; + + ieee80211_iterate_active_interfaces_atomic(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_mu_mimo_iface_iterator, + notif); +} + +static void +iwl_mld_handle_stored_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + struct iwl_stored_beacon_notif *sb = (void *)pkt->data; + struct ieee80211_rx_status rx_status = {}; + struct sk_buff *skb; + u32 size = le32_to_cpu(sb->common.byte_count); + + if (size == 0) + return; + + if (pkt_len < struct_size(sb, data, size)) + return; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mld, "alloc_skb failed\n"); + return; + } + + /* update rx_status according to the notification's metadata */ + rx_status.mactime = le64_to_cpu(sb->common.tsf); + /* TSF as indicated by the firmware is at INA time */ + rx_status.flag |= RX_FLAG_MACTIME_PLCP_START; + rx_status.device_timestamp = le32_to_cpu(sb->common.system_time); + rx_status.band = + iwl_mld_phy_band_to_nl80211(le16_to_cpu(sb->common.band)); + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(sb->common.channel), + rx_status.band); + + /* copy the data */ + skb_put_data(skb, sb->data, size); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + + /* pass it as regular rx to mac80211 */ + ieee80211_rx_napi(mld->hw, NULL, skb, NULL); +} + +static void +iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_channel_switch_start_notif *notif = (void *)pkt->data; + u32 link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, link_id); + struct ieee80211_vif *vif; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + + IWL_DEBUG_INFO(mld, + "CSA Start Notification with vif type: %d, link_id: %d\n", + vif->type, + link_conf->link_id); + + switch (vif->type) { + case NL80211_IFTYPE_AP: + /* We don't support canceling a CSA as it was advertised + * by the AP itself + */ + if (!link_conf->csa_active) + return; + + ieee80211_csa_finish(vif, link_conf->link_id); + break; + case NL80211_IFTYPE_STATION: + if (!link_conf->csa_active) { + /* Either unexpected cs notif or mac80211 chose to + * ignore, for example in channel switch to same channel + */ + struct iwl_cancel_channel_switch_cmd cmd = { + .id = cpu_to_le32(link_id), + }; + + if (iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, + CANCEL_CHANNEL_SWITCH_CMD), + &cmd)) + IWL_ERR(mld, + "Failed to cancel the channel switch\n"); + return; + } + + ieee80211_chswitch_done(vif, true, link_conf->link_id); + break; + + default: + WARN(1, "CSA on invalid vif type: %d", vif->type); + } +} + +static void +iwl_mld_handle_channel_switch_error_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_channel_switch_error_notif *notif = (void *)pkt->data; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_vif *vif; + u32 link_id = le32_to_cpu(notif->link_id); + u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask); + + link_conf = iwl_mld_fw_id_to_link_conf(mld, link_id); + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + + IWL_DEBUG_INFO(mld, "FW reports CSA error: id=%u, csa_err_mask=%u\n", + link_id, csa_err_mask); + + if (csa_err_mask & (CS_ERR_COUNT_ERROR | + CS_ERR_LONG_DELAY_AFTER_CS | + CS_ERR_TX_BLOCK_TIMER_EXPIRED)) + ieee80211_channel_switch_disconnect(vif); +} + +static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; + + mld->ibss_manager = !!beacon->ibss_mgr_status; +} + +/** + * DOC: Notification versioning + * + * The firmware's notifications change from time to time. In order to + * differentiate between different versions of the same notification, the + * firmware advertises the version of each notification. + * Here are listed all the notifications that are supported. Several versions + * of the same notification can be allowed at the same time: + * + * CMD_VERSION(my_multi_version_notif, + * CMD_VER_ENTRY(1, iwl_my_multi_version_notif_ver1) + * CMD_VER_ENTRY(2, iwl_my_multi_version_notif_ver2) + * + * etc... + * + * The driver will enforce that the notification coming from the firmware + * has its version listed here and it'll also enforce that the firmware sent + * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY. + */ + +CMD_VERSIONS(scan_complete_notif, + CMD_VER_ENTRY(1, iwl_umac_scan_complete)) +CMD_VERSIONS(scan_iter_complete_notif, + CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif)) +CMD_VERSIONS(mfuart_notif, + CMD_VER_ENTRY(2, iwl_mfuart_load_notif)) +CMD_VERSIONS(update_mcc, + CMD_VER_ENTRY(1, iwl_mcc_chub_notif)) +CMD_VERSIONS(session_prot_notif, + CMD_VER_ENTRY(3, iwl_session_prot_notif)) +CMD_VERSIONS(missed_beacon_notif, + CMD_VER_ENTRY(5, iwl_missed_beacons_notif)) +CMD_VERSIONS(tx_resp_notif, + CMD_VER_ENTRY(8, iwl_tx_resp)) +CMD_VERSIONS(compressed_ba_notif, + CMD_VER_ENTRY(5, iwl_compressed_ba_notif)) +CMD_VERSIONS(tlc_notif, + CMD_VER_ENTRY(3, iwl_tlc_update_notif)) +CMD_VERSIONS(mu_mimo_grp_notif, + CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif)) +CMD_VERSIONS(channel_switch_start_notif, + CMD_VER_ENTRY(3, iwl_channel_switch_start_notif)) +CMD_VERSIONS(channel_switch_error_notif, + CMD_VER_ENTRY(2, iwl_channel_switch_error_notif)) +CMD_VERSIONS(ct_kill_notif, + CMD_VER_ENTRY(2, ct_kill_notif)) +CMD_VERSIONS(temp_notif, + CMD_VER_ENTRY(2, iwl_dts_measurement_notif)) +CMD_VERSIONS(stored_beacon_notif, + CMD_VER_ENTRY(4, iwl_stored_beacon_notif)) +CMD_VERSIONS(roc_notif, + CMD_VER_ENTRY(1, iwl_roc_notif)) +CMD_VERSIONS(probe_resp_data_notif, + CMD_VER_ENTRY(1, iwl_probe_resp_data_notif)) +CMD_VERSIONS(datapath_monitor_notif, + CMD_VER_ENTRY(1, iwl_datapath_monitor_notif)) +CMD_VERSIONS(stats_oper_notif, + CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper)) +CMD_VERSIONS(stats_oper_part1_notif, + CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper)) +CMD_VERSIONS(bt_coex_notif, + CMD_VER_ENTRY(1, iwl_bt_coex_profile_notif)) +CMD_VERSIONS(beacon_notification, + CMD_VER_ENTRY(6, iwl_extended_beacon_notif)) +CMD_VERSIONS(emlsr_mode_notif, + CMD_VER_ENTRY(1, iwl_esr_mode_notif)) +CMD_VERSIONS(emlsr_trans_fail_notif, + CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif)) +CMD_VERSIONS(uapsd_misbehaving_ap_notif, + CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif)) +CMD_VERSIONS(time_msmt_notif, + CMD_VER_ENTRY(1, iwl_time_msmt_notify)) +CMD_VERSIONS(time_sync_confirm_notif, + CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify)) +CMD_VERSIONS(omi_status_notif, + CMD_VER_ENTRY(1, iwl_omi_send_status_notif)) +CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(9, iwl_tof_range_rsp_ntfy)) + +DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id) +DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id) +DEFINE_SIMPLE_CANCELLATION(channel_switch_start, + iwl_channel_switch_start_notif, link_id) +DEFINE_SIMPLE_CANCELLATION(channel_switch_error, + iwl_channel_switch_error_notif, link_id) +DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif, + link_id) +DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity) +DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid) +DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, + mac_id) +DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, + mac_id) +#define iwl_mld_cancel_omi_status_notif iwl_mld_always_cancel +DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id) + +/** + * DOC: Handlers for fw notifications + * + * Here are listed the notifications IDs (including the group ID), the handler + * of the notification and how it should be called: + * + * - RX_HANDLER_SYNC: will be called as part of the Rx path + * - RX_HANDLER_ASYNC: will be handled in a working with the wiphy_lock held + * + * This means that if the firmware sends two notifications A and B in that + * order and notification A is RX_HANDLER_ASYNC and notification is + * RX_HANDLER_SYNC, the handler of B will likely be called before the handler + * of A. + * + * This list should be in order of frequency for performance purposes. + * The handler can be one from two contexts, see &iwl_rx_handler_context + * + * A handler can declare that it relies on a specific object in which case it + * can be cancelled in case the object is deleted. In order to use this + * mechanism, a cancellation function is needed. The cancellation function must + * receive an object id (the index of that object in the firmware) and a + * notification payload. It'll return true if that specific notification should + * be cancelled upon the obliteration of the specific instance of the object. + * + * DEFINE_SIMPLE_CANCELLATION allows to easily create a cancellation function + * that wills simply return true if a given object id matches the object id in + * the firmware notification. + */ + +VISIBLE_IF_IWLWIFI_KUNIT +const struct iwl_rx_handler iwl_mld_rx_handlers[] = { + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, TX_CMD, tx_resp_notif, + RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif, + RX_HANDLER_SYNC) + RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC, + scan_complete_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC, + scan_iter_complete_notif, + RX_HANDLER_SYNC) + RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION, + match_found_notif, RX_HANDLER_SYNC) + + RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, + stats_oper_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, + stats_oper_part1_notif, RX_HANDLER_ASYNC) + + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MFUART_LOAD_NOTIFICATION, + mfuart_notif, RX_HANDLER_SYNC) + + RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, + temp_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF, + session_prot_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF, + missed_beacon_notif) + RX_HANDLER_OF_STA(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, tlc_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF, + channel_switch_start_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF, + channel_switch_error_notif) + RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif) + RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, + mu_mimo_grp_notif, RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF, + stored_beacon_notif, RX_HANDLER_SYNC) + RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF, + probe_resp_data_notif) + RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION, + ct_kill_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(DATA_PATH_GROUP, MONITOR_NOTIF, + datapath_monitor_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MCC_CHUB_UPDATE_CMD, update_mcc, + RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(BT_COEX_GROUP, PROFILE_NOTIF, + bt_coex_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BEACON_NOTIFICATION, + beacon_notification, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, ESR_MODE_NOTIF, + emlsr_mode_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF, + emlsr_trans_fail_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, + uapsd_misbehaving_ap_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, + WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION, + time_msmt_notif, RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, + WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION, + time_sync_confirm_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(DATA_PATH_GROUP, OMI_SEND_STATUS_NOTIF, + omi_status_notif) + RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF, + ftm_resp_notif) +}; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers); + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int iwl_mld_rx_handlers_num = ARRAY_SIZE(iwl_mld_rx_handlers); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers_num); +#endif + +static bool +iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt, + const struct iwl_rx_handler *handler) +{ + unsigned int size = iwl_rx_packet_payload_len(pkt); + size_t notif_ver; + + /* If n_sizes == 0, it indicates that a validation function may be used + * or that no validation is required. + */ + if (!handler->n_sizes) { + if (handler->val_fn) + return handler->val_fn(mld, pkt); + return true; + } + + notif_ver = iwl_fw_lookup_notif_ver(mld->fw, + iwl_cmd_groupid(handler->cmd_id), + iwl_cmd_opcode(handler->cmd_id), + IWL_FW_CMD_VER_UNKNOWN); + + for (int i = 0; i < handler->n_sizes; i++) { + if (handler->sizes[i].ver != notif_ver) + continue; + + if (IWL_FW_CHECK(mld, size < handler->sizes[i].size, + "unexpected notification 0x%04x size %d, need %d\n", + handler->cmd_id, size, handler->sizes[i].size)) + return false; + return true; + } + + IWL_FW_CHECK_FAILED(mld, + "notif 0x%04x ver %zu missing expected size, use version %u size\n", + handler->cmd_id, notif_ver, + handler->sizes[handler->n_sizes - 1].ver); + + return size < handler->sizes[handler->n_sizes - 1].size; +} + +struct iwl_async_handler_entry { + struct list_head list; + struct iwl_rx_cmd_buffer rxb; + const struct iwl_rx_handler *rx_h; +}; + +static void +iwl_mld_log_async_handler_op(struct iwl_mld *mld, const char *op, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + IWL_DEBUG_HC(mld, + "%s async handler for notif %s (%.2x.%2x, seq 0x%x)\n", + op, iwl_get_cmd_string(mld->trans, + WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)), + pkt->hdr.group_id, pkt->hdr.cmd, + le16_to_cpu(pkt->hdr.sequence)); +} + +static void iwl_mld_rx_notif(struct iwl_mld *mld, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_rx_packet *pkt) +{ + for (int i = 0; i < ARRAY_SIZE(iwl_mld_rx_handlers); i++) { + const struct iwl_rx_handler *rx_h = &iwl_mld_rx_handlers[i]; + struct iwl_async_handler_entry *entry; + + if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) + continue; + + if (!iwl_mld_notif_is_valid(mld, pkt, rx_h)) + return; + + if (rx_h->context == RX_HANDLER_SYNC) { + rx_h->fn(mld, pkt); + break; + } + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + /* we can't do much... */ + if (!entry) + return; + + /* Set the async handler entry */ + entry->rxb._page = rxb_steal_page(rxb); + entry->rxb._offset = rxb->_offset; + entry->rxb._rx_page_order = rxb->_rx_page_order; + + entry->rx_h = rx_h; + + /* Add it to the list and queue the work */ + spin_lock(&mld->async_handlers_lock); + list_add_tail(&entry->list, &mld->async_handlers_list); + spin_unlock(&mld->async_handlers_lock); + + wiphy_work_queue(mld->hw->wiphy, + &mld->async_handlers_wk); + + iwl_mld_log_async_handler_op(mld, "Queued", rxb); + break; + } + + iwl_notification_wait_notify(&mld->notif_wait, pkt); +} + +void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) + iwl_mld_rx_mpdu(mld, napi, rxb, 0); + else if (cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)) + iwl_mld_handle_frame_release_notif(mld, napi, pkt, 0); + else if (cmd_id == WIDE_ID(LEGACY_GROUP, BAR_FRAME_RELEASE)) + iwl_mld_handle_bar_frame_release_notif(mld, napi, pkt, 0); + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RX_QUEUES_NOTIFICATION))) + iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0); + else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF)) + iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0); + else + iwl_mld_rx_notif(mld, rxb, pkt); +} + +void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + if (unlikely(queue >= mld->trans->num_rx_queues)) + return; + + if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) + iwl_mld_rx_mpdu(mld, napi, rxb, queue); + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RX_QUEUES_NOTIFICATION))) + iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue); + else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))) + iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue); +} + +void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds) +{ + struct iwl_async_handler_entry *entry, *tmp; + + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + bool match = false; + + for (int i = 0; i < n_cmds; i++) { + if (entry->rx_h->cmd_id == cmds[i]) { + match = true; + break; + } + } + + if (!match) + continue; + + iwl_mld_log_async_handler_op(mld, "Delete", &entry->rxb); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_bh(&mld->async_handlers_lock); +} + +void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = + container_of(wk, struct iwl_mld, async_handlers_wk); + struct iwl_async_handler_entry *entry, *tmp; + LIST_HEAD(local_list); + + /* Sync with Rx path with a lock. Remove all the entries from this + * list, add them to a local one (lock free), and then handle them. + */ + spin_lock_bh(&mld->async_handlers_lock); + list_splice_init(&mld->async_handlers_list, &local_list); + spin_unlock_bh(&mld->async_handlers_lock); + + list_for_each_entry_safe(entry, tmp, &local_list, list) { + iwl_mld_log_async_handler_op(mld, "Handle", &entry->rxb); + entry->rx_h->fn(mld, rxb_addr(&entry->rxb)); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } +} + +void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld) +{ + struct iwl_async_handler_entry *entry, *tmp; + + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_bh(&mld->async_handlers_lock); +} + +void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld, + enum iwl_mld_object_type obj_type, + u32 obj_id) +{ + struct iwl_async_handler_entry *entry, *tmp; + LIST_HEAD(cancel_list); + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(obj_type == IWL_MLD_OBJECT_TYPE_NONE)) + return; + + /* Sync with RX path and remove matching entries from the async list */ + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + const struct iwl_rx_handler *rx_h = entry->rx_h; + + if (rx_h->obj_type != obj_type || WARN_ON(!rx_h->cancel)) + continue; + + if (rx_h->cancel(mld, rxb_addr(&entry->rxb), obj_id)) { + iwl_mld_log_async_handler_op(mld, "Cancel", &entry->rxb); + list_del(&entry->list); + list_add_tail(&entry->list, &cancel_list); + } + } + + spin_unlock_bh(&mld->async_handlers_lock); + + /* Free the matching entries outside of the spinlock */ + list_for_each_entry_safe(entry, tmp, &cancel_list, list) { + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.h b/drivers/net/wireless/intel/iwlwifi/mld/notif.h new file mode 100644 index 000000000000..2eaa1d4e138e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_notif_h__ +#define __iwl_mld_notif_h__ + +struct iwl_mld; + +void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb); + +void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue); + +void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk); + +void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld); + +enum iwl_mld_object_type { + IWL_MLD_OBJECT_TYPE_NONE, + IWL_MLD_OBJECT_TYPE_LINK, + IWL_MLD_OBJECT_TYPE_STA, + IWL_MLD_OBJECT_TYPE_VIF, + IWL_MLD_OBJECT_TYPE_ROC, + IWL_MLD_OBJECT_TYPE_SCAN, + IWL_MLD_OBJECT_TYPE_FTM_REQ, +}; + +void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld, + enum iwl_mld_object_type obj_type, + u32 obj_id); +void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds); + +#endif /* __iwl_mld_notif_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c new file mode 100644 index 000000000000..c38f101628de --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include + +#include "phy.h" +#include "hcmd.h" +#include "fw/api/phy-ctxt.h" + +int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld) +{ + int id; + unsigned long used = mld->used_phy_ids; + + for_each_clear_bit(id, &used, NUM_PHY_CTX) { + mld->used_phy_ids |= BIT(id); + return id; + } + + return -ENOSPC; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id); + +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct ieee80211_chanctx_conf *ctx) +{ + bool use_def = cfg80211_channel_is_psc(ctx->def.chan) || + (ctx->def.chan->band == NL80211_BAND_6GHZ && + ctx->def.width >= NL80211_CHAN_WIDTH_80); + + return use_def ? &ctx->def : &ctx->min_def; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_chandef_from_chanctx); + +static u8 +iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return IWL_PHY_CHANNEL_MODE20; + case NL80211_CHAN_WIDTH_40: + return IWL_PHY_CHANNEL_MODE40; + case NL80211_CHAN_WIDTH_80: + return IWL_PHY_CHANNEL_MODE80; + case NL80211_CHAN_WIDTH_160: + return IWL_PHY_CHANNEL_MODE160; + case NL80211_CHAN_WIDTH_320: + return IWL_PHY_CHANNEL_MODE320; + default: + WARN(1, "Invalid channel width=%u", width); + return IWL_PHY_CHANNEL_MODE20; + } +} + +/* Maps the driver specific control channel position (relative to the center + * freq) definitions to the fw values + */ +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +{ + int offs = chandef->chan->center_freq - chandef->center_freq1; + int abs_offs = abs(offs); + u8 ret; + + if (offs == 0) { + /* The FW is expected to check the control channel position only + * when in HT/VHT and the channel width is not 20MHz. Return + * this value as the default one. + */ + return 0; + } + + /* this results in a value 0-7, i.e. fitting into 0b0111 */ + ret = (abs_offs - 10) / 20; + /* But we need the value to be in 0b1011 because 0b0100 is + * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in + * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000) + */ + ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) | + ((ret & BIT(2)) << 1); + /* and add the above bit */ + ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE; + + return ret; +} + +int iwl_mld_phy_fw_action(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx, u32 action) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = &phy->chandef; + struct iwl_phy_context_cmd cmd = { + .id_and_color = cpu_to_le32(phy->fw_id), + .action = cpu_to_le32(action), + .puncture_mask = cpu_to_le16(chandef->punctured), + /* Channel info */ + .ci.channel = cpu_to_le32(chandef->chan->hw_value), + .ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band), + .ci.width = iwl_mld_nl80211_width_to_fw(chandef->width), + .ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef), + }; + int ret; + + if (ctx->ap.chan) { + cmd.sbb_bandwidth = + iwl_mld_nl80211_width_to_fw(ctx->ap.width); + cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap); + } + + ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret); + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h new file mode 100644 index 000000000000..3dfb8ca994e2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_phy_h__ +#define __iwl_mld_phy_h__ + +#include "mld.h" + +/** + * struct iwl_mld_phy - PHY configuration parameters + * + * @fw_id: fw id of the phy. + * @chandef: the last chandef that mac80211 configured the driver + * with. Used to detect a no-op when the chanctx changes. + * @channel_load_by_us: channel load on this channel caused by + * the NIC itself, as indicated by firmware + */ +struct iwl_mld_phy { + /* Add here fields that need clean up on hw restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + struct cfg80211_chan_def chandef; + ); + /* And here fields that survive a hw restart */ + u32 channel_load_by_us; +}; + +static inline struct iwl_mld_phy * +iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel) +{ + return (void *)channel->drv_priv; +} + +/* Cleanup function for struct iwl_mld_phy, will be called in restart */ +static inline void +iwl_mld_cleanup_phy(struct iwl_mld *mld, struct iwl_mld_phy *phy) +{ + CLEANUP_STRUCT(phy); +} + +int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld); +int iwl_mld_phy_fw_action(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx, u32 action); +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct ieee80211_chanctx_conf *ctx); +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef); + +#endif /* __iwl_mld_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c new file mode 100644 index 000000000000..ed7c0319f239 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include + +#include "mld.h" +#include "hcmd.h" +#include "power.h" +#include "iface.h" +#include "link.h" +#include "constants.h" + +static void iwl_mld_vif_ps_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *ps_enable = (bool *)data; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + *ps_enable &= vif->cfg.ps; +} + +int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3) +{ + struct iwl_device_power_cmd cmd = {}; + bool enable_ps = false; + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) { + enable_ps = true; + + /* Disable power save if any STA interface has + * power save turned off + */ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_ps_iterator, + &enable_ps); + } + + if (enable_ps) + cmd.flags |= + cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (d3) + cmd.flags |= + cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK); + + IWL_DEBUG_POWER(mld, + "Sending device power command with flags = 0x%X\n", + cmd.flags); + + return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd); +} + +int iwl_mld_enable_beacon_filter(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + bool d3) +{ + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + .ba_enable_beacon_abort = cpu_to_le32(1), + }; + + if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION) + return 0; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf) + return 0; +#endif + + if (link_conf->cqm_rssi_thold) { + cmd.bf_energy_delta = + cpu_to_le32(link_conf->cqm_rssi_hyst); + /* fw uses an absolute value for this */ + cmd.bf_roaming_state = + cpu_to_le32(-link_conf->cqm_rssi_thold); + } + + if (d3) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD, + &cmd); +} + +int iwl_mld_disable_beacon_filter(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_beacon_filter_cmd cmd = {}; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + return 0; + + return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD, + &cmd); +} + +static bool iwl_mld_power_is_radar(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf) +{ + const struct ieee80211_chanctx_conf *chanctx_conf; + + chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + + if (WARN_ON(!chanctx_conf)) + return false; + + return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR; +} + +static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld, + struct iwl_mld_link *link, + struct iwl_mac_power_cmd *cmd, + bool ps_poll) +{ + bool tid_found = false; + + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT); + + /* set advanced pm flag with no uapsd ACs to enable ps-poll */ + if (ps_poll) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + return; + } + + for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO; + ac <= IEEE80211_AC_BK; + ac++) { + if (!link->queue_params[ac].uapsd) + continue; + + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK | + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); + + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !link->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL); + cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len; +} + +static void +iwl_mld_power_config_skip_dtim(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + struct iwl_mac_power_cmd *cmd) +{ + unsigned int dtimper_tu; + unsigned int dtimper; + unsigned int skip; + + dtimper = link_conf->dtim_period ?: 1; + dtimper_tu = dtimper * link_conf->beacon_int; + + if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf)) + return; + + if (WARN_ON(!dtimper_tu)) + return; + + /* configure skip over dtim up to 900 TU DTIM interval */ + skip = max_t(int, 1, 900 / dtimper_tu); + + cmd->skip_dtim_periods = skip; + cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); +} + +#define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static void iwl_mld_power_build_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool d3) +{ + int dtimper, bi; + int keep_alive; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct iwl_mld_link *link = &mld_vif->deflink; + bool ps_poll = false; + + cmd->id_and_color = cpu_to_le32(mld_vif->fw_id); + + if (ieee80211_vif_is_mld(vif)) { + int link_id; + + if (WARN_ON(!vif->active_links)) + return; + + /* The firmware consumes one single configuration for the vif + * and can't differentiate between links, just pick the lowest + * link_id's configuration and use that. + */ + link_id = __ffs(vif->active_links); + link_conf = link_conf_dereference_check(vif, link_id); + link = iwl_mld_link_dereference_check(mld_vif, link_id); + + if (WARN_ON(!link_conf || !link)) + return; + } + dtimper = link_conf->dtim_period; + bi = link_conf->beacon_int; + + /* Regardless of power management state the driver must set + * keep alive period. FW will use it for sending keep alive NDPs + * immediately after association. Check that keep alive period + * is at least 3 * DTIM + */ + keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), + USEC_PER_SEC); + keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); + cmd->keep_alive_seconds = cpu_to_le16(keep_alive); + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + + /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */ + if (link_conf->beacon_rate && + (link_conf->beacon_rate->bitrate == 10 || + link_conf->beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; + } + + if (d3) { + iwl_mld_power_config_skip_dtim(mld, link_conf, cmd); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT); + } else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) { + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT); + } + + /* uAPSD is only enabled for specific certifications. For those cases, + * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd + * which will look at what mac80211 is saying. + */ +#ifdef CONFIG_IWLWIFI_DEBUGFS + ps_poll = mld_vif->use_ps_poll; +#endif + iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll); +} + +int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool d3) +{ + struct iwl_mac_power_cmd cmd = {}; + + iwl_mld_power_build_cmd(mld, vif, &cmd, d3); + + return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd); +} + +static void +iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd, + const struct ieee80211_bss_conf *link) +{ + u8 i; + + /* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT, + * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE + */ + + BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) != + ARRAY_SIZE(link->tpe.psd_local[0].power)); + + /* if not valid, mac80211 puts default (max value) */ + for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++) + cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i], + link->tpe.psd_reg_client[0].power[i]); + + BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) != + ARRAY_SIZE(link->tpe.max_local[0].power)); + + for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++) + cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i], + link->tpe.max_reg_client[0].power[i]); +} + +void +iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_txpower_constraints_cmd cmd = {}; + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (!mld_link->active) + return; + + if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ) + return; + + cmd.link_id = cpu_to_le16(mld_link->fw_id); + memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr)); + memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr)); + + if (vif->type == NL80211_IFTYPE_AP) { + cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP); + } else if (link->power_type == IEEE80211_REG_UNSET_AP) { + return; + } else { + cmd.ap_type = cpu_to_le16(link->power_type - 1); + iwl_mld_tpe_sta_cmd_data(&cmd, link); + } + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(PHY_OPS_GROUP, + AP_TX_POWER_CONSTRAINTS_CMD), + &cmd); + if (ret) + IWL_ERR(mld, + "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n", + ret); +} + +int iwl_mld_set_tx_power(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + s16 tx_power) +{ + u32 cmd_id = REDUCE_TX_POWER_CMD; + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ? + IWL_DEV_MAX_TX_POWER : 8 * tx_power; + struct iwl_dev_tx_power_cmd cmd = { + /* Those fields sit on the same place for v9 and v10 */ + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK), + .common.pwr_restriction = cpu_to_le16(u_tx_power), + }; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + int len = sizeof(cmd.common); + + if (WARN_ON(!mld_link)) + return -ENODEV; + + cmd.common.link_id = cpu_to_le32(mld_link->fw_id); + + if (cmd_ver == 10) + len += sizeof(cmd.v10); + else if (cmd_ver == 9) + len += sizeof(cmd.v9); + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.h b/drivers/net/wireless/intel/iwlwifi/mld/power.h new file mode 100644 index 000000000000..05ce27bef106 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_power_h__ +#define __iwl_mld_power_h__ + +#include + +#include "mld.h" + +int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3); + +int iwl_mld_enable_beacon_filter(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + bool d3); + +int iwl_mld_disable_beacon_filter(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool d3); + +void +iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_set_tx_power(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + s16 tx_power); + +#endif /* __iwl_mld_power_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c new file mode 100644 index 000000000000..d5c3f853d96c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ + +#include "mld.h" +#include "iwl-debug.h" +#include "hcmd.h" +#include "ptp.h" +#include + +/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional + * part, which means that a value of 1 in one of those fields actually means + * 2^-16 ppm, and 2^16=65536 is 1 ppm. + */ +#define PTP_SCALE_FACTOR 65536000000ULL + +#define IWL_PTP_GP2_WRAP 0x100000000ULL +#define IWL_PTP_WRAP_TIME (3600 * HZ) +#define IWL_PTP_WRAP_THRESHOLD_USEC (5000) + +static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2) +{ + *gp2 = iwl_read_prph(mld->trans, mld->trans->cfg->gp2_reg_addr); + + if (*gp2 == 0x5a5a5a5a) + return -EINVAL; + + return 0; +} + +static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2) +{ + IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n", + mld->ptp_data.last_gp2, gp2); + + /* If the difference is above the threshold, assume it's a wraparound. + * Otherwise assume it's an old read and ignore it. + */ + if (gp2 < mld->ptp_data.last_gp2) { + if (mld->ptp_data.last_gp2 - gp2 < + IWL_PTP_WRAP_THRESHOLD_USEC) { + IWL_DEBUG_PTP(mld, + "PTP: ignore old read (gp2=%u, last_gp2=%u)\n", + gp2, mld->ptp_data.last_gp2); + return; + } + + mld->ptp_data.wrap_counter++; + IWL_DEBUG_PTP(mld, + "PTP: wraparound detected (new counter=%u)\n", + mld->ptp_data.wrap_counter); + } + + mld->ptp_data.last_gp2 = gp2; + schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME); +} + +u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns) +{ + struct ptp_data *data = &mld->ptp_data; + u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC; + u64 res; + u64 diff; + s64 scaled_diff; + + lockdep_assert_held(&data->lock); + + iwl_mld_ptp_update_new_read(mld, + div64_u64(base_time_ns, NSEC_PER_USEC)); + + base_time_ns = base_time_ns + + (data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC); + + /* It is possible that a GP2 timestamp was received from fw before the + * last scale update. + */ + if (base_time_ns < scale_time_gp2_ns) { + diff = scale_time_gp2_ns - base_time_ns; + scaled_diff = -mul_u64_u64_div_u64(diff, + data->scaled_freq, + PTP_SCALE_FACTOR); + } else { + diff = base_time_ns - scale_time_gp2_ns; + scaled_diff = mul_u64_u64_div_u64(diff, + data->scaled_freq, + PTP_SCALE_FACTOR); + } + + IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n", + (unsigned long long)base_time_ns, + (unsigned long long)diff, (long long)scaled_diff); + + res = data->scale_update_adj_time_ns + data->delta + scaled_diff; + + IWL_DEBUG_PTP(mld, "scale_update_ns=%llu delta=%lld adj=%llu\n", + (unsigned long long)data->scale_update_adj_time_ns, + (long long)data->delta, (unsigned long long)res); + return res; +} + +static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + u64 ns; + + if (iwl_mld_get_systime(mld, &gp2)) { + IWL_DEBUG_PTP(mld, "PTP: gettime: failed to read systime\n"); + return -EIO; + } + + spin_lock_bh(&data->lock); + ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); + spin_unlock_bh(&data->lock); + + *ts = ns_to_timespec64(ns); + return 0; +} + +static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + + spin_lock_bh(&data->lock); + data->delta += delta; + IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta, + (long long)data->delta); + spin_unlock_bh(&data->lock); + return 0; +} + +static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + + /* Must call iwl_mld_ptp_get_adj_time() before updating + * data->scale_update_gp2 or data->scaled_freq since + * scale_update_adj_time_ns should reflect the previous scaled_freq. + */ + if (iwl_mld_get_systime(mld, &gp2)) { + IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n"); + return -EBUSY; + } + + spin_lock_bh(&data->lock); + data->scale_update_adj_time_ns = + iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC); + data->scale_update_gp2 = gp2; + + /* scale_update_adj_time_ns now relects the configured delta, the + * wrap_counter and the previous scaled frequency. Thus delta and + * wrap_counter should be reset, and the scale frequency is updated + * to the new frequency. + */ + data->delta = 0; + data->wrap_counter = 0; + data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm; + IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n", + scaled_ppm, (unsigned long long)data->scaled_freq); + spin_unlock_bh(&data->lock); + return 0; +} + +static void iwl_mld_ptp_work(struct work_struct *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + ptp_data.dwork.work); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + + spin_lock_bh(&data->lock); + if (!iwl_mld_get_systime(mld, &gp2)) + iwl_mld_ptp_update_new_read(mld, gp2); + else + IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n"); + spin_unlock_bh(&data->lock); +} + +static int +iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time) +{ + struct iwl_synced_time_cmd synced_time_cmd = { + .operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH) + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD), + .flags = CMD_WANT_SKB, + .data[0] = &synced_time_cmd, + .len[0] = sizeof(synced_time_cmd), + }; + struct iwl_synced_time_rsp *resp; + struct iwl_rx_packet *pkt; + int ret; + u64 gp2_10ns; + + wiphy_lock(mld->wiphy); + ret = iwl_mld_send_cmd(mld, &cmd); + wiphy_unlock(mld->wiphy); + if (ret) + return ret; + + pkt = cmd.resp_pkt; + + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) { + IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n"); + iwl_free_resp(&cmd); + return -EIO; + } + + resp = (void *)pkt->data; + + gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 | + le32_to_cpu(resp->gp2_timestamp_lo); + *gp2 = div_u64(gp2_10ns, 100); + + *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | + le32_to_cpu(resp->platform_timestamp_lo); + + iwl_free_resp(&cmd); + return ret; +} + +static int +iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp, + struct system_device_crosststamp *xtstamp) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + int ret = 0; + /* Raw value read from GP2 register in usec */ + u32 gp2; + /* GP2 value in ns*/ + s64 gp2_ns; + /* System (wall) time */ + ktime_t sys_time; + + memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); + + ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time); + if (ret) { + IWL_DEBUG_PTP(mld, + "PTP: fw get_crosstimestamp failed (ret=%d)\n", + ret); + return ret; + } + + spin_lock_bh(&data->lock); + gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_PTP(mld, + "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n", + gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time); + + /* System monotonic raw time is not used */ + xtstamp->device = ns_to_ktime(gp2_ns); + xtstamp->sys_realtime = sys_time; + + return ret; +} + +void iwl_mld_ptp_init(struct iwl_mld *mld) +{ + if (WARN_ON(mld->ptp_data.ptp_clock)) + return; + + spin_lock_init(&mld->ptp_data.lock); + INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work); + + mld->ptp_data.ptp_clock_info.owner = THIS_MODULE; + mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime; + mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; + mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime; + mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine; + mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR; + mld->ptp_data.ptp_clock_info.getcrosststamp = + iwl_mld_phc_get_crosstimestamp; + + /* Give a short 'friendly name' to identify the PHC clock */ + snprintf(mld->ptp_data.ptp_clock_info.name, + sizeof(mld->ptp_data.ptp_clock_info.name), + "%s", "iwlwifi-PTP"); + + 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)) { + IWL_ERR(mld, "Failed to register PHC clock (%ld)\n", + PTR_ERR(mld->ptp_data.ptp_clock)); + mld->ptp_data.ptp_clock = NULL; + } else { + IWL_INFO(mld, "Registered PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); + } +} + +void iwl_mld_ptp_remove(struct iwl_mld *mld) +{ + if (mld->ptp_data.ptp_clock) { + IWL_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); + + ptp_clock_unregister(mld->ptp_data.ptp_clock); + mld->ptp_data.ptp_clock = NULL; + mld->ptp_data.last_gp2 = 0; + mld->ptp_data.wrap_counter = 0; + cancel_delayed_work_sync(&mld->ptp_data.dwork); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.h b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h new file mode 100644 index 000000000000..f3d18dd304e5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_ptp_h__ +#define __iwl_mld_ptp_h__ + +#include + +/** + * struct ptp_data - PTP hardware clock data + * + * @ptp_clock: struct ptp_clock pointer returned by the ptp_clock_register() + * function. + * @ptp_clock_info: struct ptp_clock_info that describes a PTP hardware clock + * @lock: protects the time adjustments data + * @delta: delta between hardware clock and ptp clock in nanoseconds + * @scale_update_gp2: GP2 time when the scale was last updated + * @scale_update_adj_time_ns: adjusted time when the scale was last updated, + * in nanoseconds + * @scaled_freq: clock frequency offset, scaled to 65536000000 + * @last_gp2: the last GP2 reading from the hardware, used for tracking GP2 + * wraparounds + * @wrap_counter: number of wraparounds since scale_update_adj_time_ns + * @dwork: worker scheduled every 1 hour to detect workarounds + */ +struct ptp_data { + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + + spinlock_t lock; + s64 delta; + u32 scale_update_gp2; + u64 scale_update_adj_time_ns; + u64 scaled_freq; + u32 last_gp2; + u32 wrap_counter; + struct delayed_work dwork; +}; + +void iwl_mld_ptp_init(struct iwl_mld *mld); +void iwl_mld_ptp_remove(struct iwl_mld *mld); +u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns); + +#endif /* __iwl_mld_ptp_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c new file mode 100644 index 000000000000..a75af8c1e8ab --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include + +#include "fw/regulatory.h" +#include "fw/acpi.h" +#include "fw/uefi.h" + +#include "regulatory.h" +#include "mld.h" +#include "hcmd.h" + +void iwl_mld_get_bios_tables(struct iwl_mld *mld) +{ + int ret; + + iwl_acpi_get_guid_lock_status(&mld->fwrt); + + ret = iwl_bios_get_ppag_table(&mld->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "PPAG BIOS table invalid or unavailable. (%d)\n", + ret); + } + + ret = iwl_bios_get_wrds_table(&mld->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "WRDS SAR BIOS table invalid or unavailable. (%d)\n", + ret); + + /* If not available, don't fail and don't bother with EWRD and + * WGDS + */ + + if (!iwl_bios_get_wgds_table(&mld->fwrt)) { + /* If basic SAR is not available, we check for WGDS, + * which should *not* be available either. If it is + * available, issue an error, because we can't use SAR + * Geo without basic SAR. + */ + IWL_ERR(mld, "BIOS contains WGDS but no WRDS\n"); + } + + } else { + ret = iwl_bios_get_ewrd_table(&mld->fwrt); + /* If EWRD is not available, we can still use + * WRDS, so don't fail. + */ + if (ret < 0) + IWL_DEBUG_RADIO(mld, + "EWRD SAR BIOS table invalid or unavailable. (%d)\n", + ret); + + ret = iwl_bios_get_wgds_table(&mld->fwrt); + if (ret < 0) + IWL_DEBUG_RADIO(mld, + "Geo SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + } + + ret = iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); + if (ret) + IWL_DEBUG_RADIO(mld, "failed to read UATS table (%d)\n", ret); +} + +static int iwl_mld_geo_sar_init(struct iwl_mld *mld) +{ + u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); + union iwl_geo_tx_power_profiles_cmd cmd; + u16 len; + u32 n_bands; + __le32 sk = cpu_to_le32(0); + int ret; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops)); + + cmd.v4.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES); + + /* Only set to South Korea if the table revision is 1 */ + if (mld->fwrt.geo_rev == 1) + sk = cpu_to_le32(1); + + if (cmd_ver == 5) { + len = sizeof(cmd.v5); + n_bands = ARRAY_SIZE(cmd.v5.table[0]); + cmd.v5.table_revision = sk; + } else if (cmd_ver == 4) { + len = sizeof(cmd.v4); + n_bands = ARRAY_SIZE(cmd.v4.table[0]); + cmd.v4.table_revision = sk; + } else { + return -EOPNOTSUPP; + } + + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table)); + /* the table is at the same position for all versions, so set use v4 */ + ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v4.table[0][0], + n_bands, 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. + */ + if (ret) + return 0; + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); +} + +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), + }; + __le16 *per_chain; + int ret; + u16 len = sizeof(cmd.common); + u32 n_subbands; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + if (cmd_ver == 10) { + len += sizeof(cmd.v10); + n_subbands = IWL_NUM_SUB_BANDS_V2; + per_chain = &cmd.v10.per_chain[0][0][0]; + cmd.v10.flags = + cpu_to_le32(mld->fwrt.reduced_power_flags); + } else if (cmd_ver == 9) { + len += sizeof(cmd.v9); + n_subbands = IWL_NUM_SUB_BANDS_V1; + per_chain = &cmd.v9.per_chain[0][0]; + } else { + return -EOPNOTSUPP; + } + + /* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */ + ret = iwl_sar_fill_profile(&mld->fwrt, per_chain, + IWL_NUM_CHAIN_TABLES, + n_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, len); +} + +int iwl_mld_init_sar(struct iwl_mld *mld) +{ + int chain_a_prof = 1; + int chain_b_prof = 1; + int ret; + + /* If no profile was chosen by the user yet, choose profile 1 (WRDS) as + * default for both chains + */ + if (mld->fwrt.sar_chain_a_profile && mld->fwrt.sar_chain_b_profile) { + chain_a_prof = mld->fwrt.sar_chain_a_profile; + chain_b_prof = mld->fwrt.sar_chain_b_profile; + } + + ret = iwl_mld_config_sar_profile(mld, chain_a_prof, chain_b_prof); + if (ret < 0) + return ret; + + if (ret) + return 0; + + return iwl_mld_geo_sar_init(mld); +} + +int iwl_mld_init_sgom(struct iwl_mld *mld) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + SAR_OFFSET_MAPPING_TABLE_CMD), + .data[0] = &mld->fwrt.sgom_table, + .len[0] = sizeof(mld->fwrt.sgom_table), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!mld->fwrt.sgom_enabled) { + IWL_DEBUG_RADIO(mld, "SGOM table is disabled\n"); + return 0; + } + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + IWL_ERR(mld, + "failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret); + + return ret; +} + +static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld) +{ + union iwl_ppag_table_cmd cmd = {}; + int ret, len; + + ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len); + /* Not supporting PPAG table is a valid scenario */ + if (ret < 0) + 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, len); + if (ret < 0) + IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", + ret); + + return ret; +} + +int iwl_mld_init_ppag(struct iwl_mld *mld) +{ + /* no need to read the table, done in INIT stage */ + + if (!(iwl_is_ppag_approved(&mld->fwrt))) + return 0; + + return iwl_mld_ppag_send_cmd(mld); +} + +void iwl_mld_configure_lari(struct iwl_mld *mld) +{ + struct iwl_fw_runtime *fwrt = &mld->fwrt; + struct iwl_lari_config_change_cmd cmd = { + .config_bitmap = iwl_get_lari_config_bitmap(fwrt), + }; + int ret; + u32 value; + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); + if (!ret) + cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); + if (!ret) + cmd.oem_unii4_allow_bitmap = + cpu_to_le32(value &= DSM_UNII4_ALLOW_BITMAP); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); + if (!ret) + cmd.chan_state_active_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value); + if (!ret) + cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value); + if (!ret) + cmd.force_disable_channels_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, + &value); + if (!ret) + cmd.edt_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_wbem(fwrt, &value); + if (!ret) + cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value); + if (!ret) + cmd.oem_11be_allow_bitmap = cpu_to_le32(value); + + if (!cmd.config_bitmap && + !cmd.oem_uhb_allow_bitmap && + !cmd.oem_11ax_allow_bitmap && + !cmd.oem_unii4_allow_bitmap && + !cmd.chan_state_active_bitmap && + !cmd.force_disable_channels_bitmap && + !cmd.edt_bitmap && + !cmd.oem_320mhz_allow_bitmap && + !cmd.oem_11be_allow_bitmap) + return; + + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.config_bitmap), + le32_to_cpu(cmd.oem_11ax_allow_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_unii4_allow_bitmap), + le32_to_cpu(cmd.chan_state_active_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_uhb_allow_bitmap), + le32_to_cpu(cmd.force_disable_channels_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.edt_bitmap), + le32_to_cpu(cmd.oem_320mhz_allow_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_11be_allow_bitmap)); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), &cmd); + if (ret) + IWL_DEBUG_RADIO(mld, + "Failed to send LARI_CONFIG_CHANGE (%d)\n", + ret); +} + +void iwl_mld_init_uats(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), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!mld->fwrt.uats_valid) + return; + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", + ret); +} + +void iwl_mld_init_tas(struct iwl_mld *mld) +{ + int ret; + struct iwl_tas_data data = {}; + struct iwl_tas_config_cmd cmd = {}; + u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); + + BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + + if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { + IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n"); + return; + } + + ret = iwl_bios_get_tas_table(&mld->fwrt, &data); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "TAS table invalid or unavailable. (%d)\n", + ret); + return; + } + + if (!iwl_is_tas_approved()) { + IWL_DEBUG_RADIO(mld, + "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); + if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_US)) || + (!iwl_add_mcc_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_CANADA))) { + IWL_DEBUG_RADIO(mld, + "Unable to add US/Canada to TAS block list, disabling TAS\n"); + return; + } + } else { + IWL_DEBUG_RADIO(mld, + "System vendor '%s' is in the approved list.\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); + } + + cmd.block_list_size = cpu_to_le16(data.block_list_size); + for (u8 i = 0; i < data.block_list_size; i++) + cmd.block_list_array[i] = + cpu_to_le16(data.block_list_array[i]); + cmd.tas_config_info.table_source = data.table_source; + cmd.tas_config_info.table_revision = data.table_revision; + cmd.tas_config_info.value = cpu_to_le32(data.tas_selection); + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h new file mode 100644 index 000000000000..3b01c645adda --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_regulatory_h__ +#define __iwl_mld_regulatory_h__ + +#include "mld.h" + +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_tas(struct iwl_mld *mld); + +int iwl_mld_init_ppag(struct iwl_mld *mld); + +int iwl_mld_init_sgom(struct iwl_mld *mld); + +int iwl_mld_init_sar(struct iwl_mld *mld); + +int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b); + +#endif /* __iwl_mld_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c new file mode 100644 index 000000000000..b87faca23ceb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 - 2025 Intel Corporation + */ +#include +#include + +#include "mld.h" +#include "roc.h" +#include "hcmd.h" +#include "iface.h" +#include "sta.h" +#include "mlo.h" + +#include "fw/api/context.h" +#include "fw/api/time-event.h" + +#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200) + +static void +iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int *result = data; + int ret; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_ROC, + iwl_mld_get_primary_link(vif)); + if (ret) + *result = ret; +} + +int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *channel, int duration, + enum ieee80211_roc_type type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_int_sta *aux_sta; + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + enum iwl_roc_activity activity; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_roc, + &ret); + if (ret) + return ret; + + /* TODO: task=Hotspot 2.0 */ + if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { + IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", + vif->type); + + return -EOPNOTSUPP; + } + + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + activity = ROC_ACTIVITY_P2P_DISC; + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + activity = ROC_ACTIVITY_P2P_NEG; + break; + default: + WARN_ONCE(1, "Got an invalid P2P ROC type\n"); + return -EINVAL; + } + + if (WARN_ON(mld_vif->roc_activity != ROC_NUM_ACTIVITIES)) + return -EBUSY; + + /* No MLO on P2P device */ + aux_sta = &mld_vif->deflink.aux_sta; + + ret = iwl_mld_add_aux_sta(mld, aux_sta); + if (ret) + return ret; + + cmd.activity = cpu_to_le32(activity); + cmd.sta_id = cpu_to_le32(aux_sta->sta_id); + cmd.channel_info.channel = cpu_to_le32(channel->hw_value); + cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); + cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; + /* TODO: task=Hotspot 2.0, revisit those parameters when we add an ROC + * on the BSS vif + */ + cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); + cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); + + memcpy(cmd.node_addr, vif->addr, ETH_ALEN); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) { + IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); + return ret; + } + mld_vif->roc_activity = activity; + + return 0; +} + +static void +iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC); +} + +static void iwl_mld_destroy_roc(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_vif *mld_vif) +{ + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_unblock_roc, + NULL); + + /* wait until every tx has seen that roc_activity has been reset */ + synchronize_net(); + /* from here, no new tx will be added + * we can flush the Tx on the queues + */ + + iwl_mld_flush_link_sta_txqs(mld, mld_vif->deflink.aux_sta.sta_id); + + iwl_mld_remove_aux_sta(mld, vif, &vif->bss_conf); +} + +int iwl_mld_cancel_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* TODO: task=Hotspot 2.0 */ + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + return -EOPNOTSUPP; + + /* No roc activity running it's probably already done */ + if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) + return 0; + + cmd.activity = cpu_to_le32(mld_vif->roc_activity); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) + IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n"); + + /* We may have raced with the firmware expiring the ROC instance at + * this very moment. In that case, we can have a notification in the + * async processing queue. However, none can arrive _after_ this as + * ROC_CMD was sent synchronously, i.e. we waited for a response and + * the firmware cannot refer to this ROC after the response. Thus, + * if we just cancel the notification (if there's one) we'll be at a + * clean state for any possible next ROC. + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC, + mld_vif->roc_activity); + + iwl_mld_destroy_roc(mld, vif, mld_vif); + + return 0; +} + +void iwl_mld_handle_roc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_roc_notif *notif = (void *)pkt->data; + u32 activity = le32_to_cpu(notif->activity); + /* TODO: task=Hotspot 2.0 - roc can run on BSS */ + struct ieee80211_vif *vif = mld->p2p_device_vif; + struct iwl_mld_vif *mld_vif; + + if (WARN_ON(!vif)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + /* It is possible that the ROC was canceled + * but the notification was already fired. + */ + if (mld_vif->roc_activity != activity) + return; + + if (le32_to_cpu(notif->success) && + le32_to_cpu(notif->started)) { + /* We had a successful start */ + ieee80211_ready_on_channel(mld->hw); + } else { + /* ROC was not successful, tell the firmware to remove it */ + if (le32_to_cpu(notif->started)) + iwl_mld_cancel_roc(mld->hw, vif); + else + iwl_mld_destroy_roc(mld, vif, mld_vif); + /* we need to let know mac80211 about end OR + * an unsuccessful start + */ + ieee80211_remain_on_channel_expired(mld->hw); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.h b/drivers/net/wireless/intel/iwlwifi/mld/roc.h new file mode 100644 index 000000000000..985d7f20a3d7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_roc_h__ +#define __iwl_mld_roc_h__ + +#include + +int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *channel, int duration, + enum ieee80211_roc_type type); + +int iwl_mld_cancel_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +void iwl_mld_handle_roc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_roc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c new file mode 100644 index 000000000000..c4f189bcece2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -0,0 +1,2060 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include +#include + +#include "mld.h" +#include "sta.h" +#include "agg.h" +#include "rx.h" +#include "hcmd.h" +#include "iface.h" +#include "time_sync.h" +#include "fw/dbg.h" +#include "fw/api/rx.h" + +/* stores relevant PHY data fields extracted from iwl_rx_mpdu_desc */ +struct iwl_mld_rx_phy_data { + enum iwl_rx_phy_info_type info_type; + __le32 data0; + __le32 data1; + __le32 data2; + __le32 data3; + __le32 eht_data4; + __le32 data5; + __le16 data4; + bool first_subframe; + bool with_data; + __le32 rx_vec[4]; + u32 rate_n_flags; + u32 gp2_on_air_rise; + u16 phy_info; + u8 energy_a, energy_b; + u8 channel; +}; + +static void +iwl_mld_fill_phy_data(struct iwl_rx_mpdu_desc *desc, + struct iwl_mld_rx_phy_data *phy_data) +{ + phy_data->phy_info = le16_to_cpu(desc->phy_info); + phy_data->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags); + phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); + phy_data->channel = desc->v3.channel; + phy_data->energy_a = desc->v3.energy_a; + phy_data->energy_b = desc->v3.energy_b; + phy_data->data0 = desc->v3.phy_data0; + phy_data->data1 = desc->v3.phy_data1; + phy_data->data2 = desc->v3.phy_data2; + phy_data->data3 = desc->v3.phy_data3; + phy_data->data4 = desc->phy_data4; + phy_data->eht_data4 = desc->phy_eht_data4; + phy_data->data5 = desc->v3.phy_data5; + phy_data->with_data = true; +} + +static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb, + int queue, struct ieee80211_sta *sta) +{ + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_ptk_pn *ptk_pn; + int res; + u8 tid, keyidx; + u8 pn[IEEE80211_CCMP_PN_LEN]; + u8 *extiv; + + /* multicast and non-data only arrives on default queue; avoid checking + * for default queue - we don't want to replicate all the logic that's + * necessary for checking the PN on fragmented frames, leave that + * to mac80211 + */ + if (queue == 0 || !ieee80211_is_data(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return 0; + + if (!(stats->flag & RX_FLAG_DECRYPTED)) + return 0; + + /* if we are here - this for sure is either CCMP or GCMP */ + if (!sta) { + IWL_DEBUG_DROP(mld, + "expected hw-decrypted unicast frame for station\n"); + return -1; + } + + mld_sta = iwl_mld_sta_from_mac80211(sta); + + extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + keyidx = extiv[3] >> 6; + + ptk_pn = rcu_dereference(mld_sta->ptk_pn[keyidx]); + if (!ptk_pn) + return -1; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = ieee80211_get_tid(hdr); + else + tid = 0; + + /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */ + if (tid >= IWL_MAX_TID_COUNT) + return -1; + + /* load pn */ + pn[0] = extiv[7]; + pn[1] = extiv[6]; + pn[2] = extiv[5]; + pn[3] = extiv[4]; + pn[4] = extiv[1]; + pn[5] = extiv[0]; + + res = memcmp(pn, ptk_pn->q[queue].pn[tid], IEEE80211_CCMP_PN_LEN); + if (res < 0) + return -1; + if (!res && !(stats->flag & RX_FLAG_ALLOW_SAME_PN)) + return -1; + + memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN); + stats->flag |= RX_FLAG_PN_VALIDATED; + + return 0; +} + +/* iwl_mld_pass_packet_to_mac80211 - passes the packet for mac80211 */ +void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211, + mld, napi, skb, queue, sta); + + if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) { + kfree_skb(skb); + return; + } + + ieee80211_rx_napi(mld->hw, sta, skb, napi); +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211); + +static void iwl_mld_fill_signal(struct iwl_mld *mld, + struct ieee80211_rx_status *rx_status, + struct iwl_mld_rx_phy_data *phy_data) +{ + u32 rate_n_flags = phy_data->rate_n_flags; + int energy_a = phy_data->energy_a; + int energy_b = phy_data->energy_b; + int max_energy; + + energy_a = energy_a ? -energy_a : S8_MIN; + energy_b = energy_b ? -energy_b : S8_MIN; + max_energy = max(energy_a, energy_b); + + IWL_DEBUG_STATS(mld, "energy in A %d B %d, and max %d\n", + energy_a, energy_b, max_energy); + + rx_status->signal = max_energy; + rx_status->chains = + (rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS; + rx_status->chain_signal[0] = energy_a; + rx_status->chain_signal[1] = energy_b; +} + +static void +iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + struct ieee80211_rx_status *rx_status) +{ + /* Unfortunately, we have to leave the mac80211 data + * incorrect for the case that we receive an HE-MU + * transmission and *don't* have the HE phy data (due + * to the bits being used for TSF). This shouldn't + * happen though as management frames where we need + * the TSF/timers are not be transmitted in HE-MU. + */ + u8 ru = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK); + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + u8 offs = 0; + + rx_status->bw = RATE_INFO_BW_HE_RU; + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + + switch (ru) { + case 0 ... 36: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + he->data2 |= le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN); + if (phy_data->data1 & cpu_to_le32(IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80)) + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC); + +#define CHECK_BW(bw) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS); \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS) + CHECK_BW(20); + CHECK_BW(40); + CHECK_BW(80); + CHECK_BW(160); + + if (he_mu) + he_mu->flags2 |= + le16_encode_bits(u32_get_bits(rate_n_flags, + RATE_MCS_CHAN_WIDTH_MSK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + else if (he_type == RATE_MCS_HE_TYPE_TRIG) + he->data6 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) | + le16_encode_bits(u32_get_bits(rate_n_flags, + RATE_MCS_CHAN_WIDTH_MSK), + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW); +} + +static void +iwl_mld_decode_he_mu_ext(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he_mu *he_mu) +{ + u32 phy_data2 = le32_to_cpu(phy_data->data2); + u32 phy_data3 = le32_to_cpu(phy_data->data3); + u16 phy_data4 = le16_to_cpu(phy_data->data4); + u32 rate_n_flags = phy_data->rate_n_flags; + + if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK)) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN); + + he_mu->flags1 |= + le16_encode_bits(u32_get_bits(phy_data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + he_mu->ru_ch1[0] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0); + he_mu->ru_ch1[1] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1); + he_mu->ru_ch1[2] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2); + he_mu->ru_ch1[3] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3); + } + + if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK) && + (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + he_mu->flags2 |= + le16_encode_bits(u32_get_bits(phy_data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + + he_mu->ru_ch2[0] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0); + he_mu->ru_ch2[1] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1); + he_mu->ru_ch2[2] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2); + he_mu->ru_ch2[3] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3); + } +} + +static void +iwl_mld_decode_he_phy_data(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + struct ieee80211_rx_status *rx_status, + int queue) +{ + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_NONE: + case IWL_RX_PHY_INFO_TYPE_CCK: + case IWL_RX_PHY_INFO_TYPE_OFDM_LGCY: + case IWL_RX_PHY_INFO_TYPE_HT: + case IWL_RX_PHY_INFO_TYPE_VHT_SU: + case IWL_RX_PHY_INFO_TYPE_VHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT: + return; + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_SU: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_TB: + /* HE common */ + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK), + IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + if (phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB && + phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB_EXT) { + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_UPLINK), + IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + } + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM), + IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK), + IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_PE_DISAMBIG), + IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK), + IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS); + he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK), + IEEE80211_RADIOTAP_HE_DATA6_TXOP); + he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_DOPPLER), + IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); + break; + } + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_SU: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK), + IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + break; + default: + /* nothing here */ + break; + } + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + he_mu->flags1 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + he_mu->flags1 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + he_mu->flags2 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + iwl_mld_decode_he_mu_ext(phy_data, he_mu); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_MU: + he_mu->flags2 |= + le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + he_mu->flags2 |= + le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_TB: + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + iwl_mld_decode_he_phy_ru_alloc(phy_data, he, he_mu, rx_status); + break; + case IWL_RX_PHY_INFO_TYPE_HE_SU: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_BEAM_CHNG), + IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE); + break; + default: + /* nothing */ + break; + } +} + +static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_he *he = NULL; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + u8 ltf; + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN), + }; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN), + .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + u16 phy_info = phy_data->phy_info; + + he = skb_put_data(skb, &known, sizeof(known)); + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + + if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU || + phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) { + he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known)); + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + } + + /* report the AMPDU-EOF bit on single frames */ + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status, + queue); + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && + (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (he_type == RATE_MCS_HE_TYPE_EXT_SU && + rate_n_flags & RATE_MCS_HE_106T_MSK) { + rx_status->bw = RATE_INFO_BW_HE_RU; + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + } + + /* actually data is filled in mac80211 */ + if (he_type == RATE_MCS_HE_TYPE_SU || + he_type == RATE_MCS_HE_TYPE_EXT_SU) + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + he->data1 |= cpu_to_le16(he_type >> RATE_MCS_HE_TYPE_POS); + + if (rate_n_flags & RATE_MCS_BF_MSK) + he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >> + RATE_MCS_HE_GI_LTF_POS) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + if (he_type == RATE_MCS_HE_TYPE_MU) + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + else + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + break; + case 1: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + } else { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 3: + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + break; + case 4: + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + break; + default: + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; + } + + he->data5 |= le16_encode_bits(ltf, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); +} + +static void iwl_mld_decode_lsig(struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_lsig *lsig; + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HT: + case IWL_RX_PHY_INFO_TYPE_VHT_SU: + case IWL_RX_PHY_INFO_TYPE_VHT_MU: + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_SU: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT: + lsig = skb_put(skb, sizeof(*lsig)); + lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN); + lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_LSIG_LEN_MASK), + IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH); + rx_status->flag |= RX_FLAG_RADIOTAP_LSIG; + break; + default: + break; + } +} + +/* Put a TLV on the skb and return data pointer + * + * Also pad the len to 4 and zero out all data part + */ +static void * +iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len) +{ + struct ieee80211_radiotap_tlv *tlv; + + tlv = skb_put(skb, sizeof(*tlv)); + tlv->type = cpu_to_le16(type); + tlv->len = cpu_to_le16(len); + return skb_put_zero(skb, ALIGN(len, 4)); +} + +#define LE32_DEC_ENC(value, dec_bits, enc_bits) \ + le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +#define IWL_MLD_ENC_USIG_VALUE_MASK(usig, in_value, dec_bits, enc_bits) do { \ + typeof(enc_bits) _enc_bits = enc_bits; \ + typeof(usig) _usig = usig; \ + (_usig)->mask |= cpu_to_le32(_enc_bits); \ + (_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \ +} while (0) + +#define __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + eht->data[(rt_data)] |= \ + (cpu_to_le32 \ + (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \ + LE32_DEC_ENC(data ## fw_data, \ + IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \ + IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru)) + +#define _IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) + +#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4 + +#define IWL_RX_RU_DATA_A1 2 +#define IWL_RX_RU_DATA_A2 2 +#define IWL_RX_RU_DATA_B1 2 +#define IWL_RX_RU_DATA_B2 4 +#define IWL_RX_RU_DATA_C1 3 +#define IWL_RX_RU_DATA_C2 3 +#define IWL_RX_RU_DATA_D1 4 +#define IWL_RX_RU_DATA_D2 4 + +#define IWL_MLD_ENC_EHT_RU(rt_ru, fw_ru) \ + _IWL_MLD_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \ + rt_ru, \ + IWL_RX_RU_DATA_ ## fw_ru, \ + fw_ru) + +static void iwl_mld_decode_eht_ext_mu(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + if (phy_data->with_data) { + __le32 data1 = phy_data->data1; + __le32 data2 = phy_data->data2; + __le32 data3 = phy_data->data3; + __le32 data4 = phy_data->eht_data4; + __le32 data5 = phy_data->data5; + u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data4, + IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MLD_ENC_USIG_VALUE_MASK + (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | + LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M); + eht->data[7] |= LE32_DEC_ENC + (data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + + /* + * Hardware labels the content channels/RU allocation values + * as follows: + * Content Channel 1 Content Channel 2 + * 20 MHz: A1 + * 40 MHz: A1 B1 + * 80 MHz: A1 C1 B1 D1 + * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2 + * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4 + * + * However firmware can only give us A1-D2, so the higher + * frequencies are missing. + */ + + switch (phy_bw) { + case RATE_MCS_CHAN_WIDTH_320: + /* additional values are missing in RX metadata */ + fallthrough; + case RATE_MCS_CHAN_WIDTH_160: + /* content channel 1 */ + IWL_MLD_ENC_EHT_RU(1_2_1, A2); + IWL_MLD_ENC_EHT_RU(1_2_2, C2); + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_2_1, B2); + IWL_MLD_ENC_EHT_RU(2_2_2, D2); + fallthrough; + case RATE_MCS_CHAN_WIDTH_80: + /* content channel 1 */ + IWL_MLD_ENC_EHT_RU(1_1_2, C1); + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_1_2, D1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_40: + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_1_1, B1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_20: + IWL_MLD_ENC_EHT_RU(1_1_1, A1); + break; + } + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_VALIDATE, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PUNC_CHANNEL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_SIG_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MLD_ENC_USIG_VALUE_MASK + (usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC); + } +} + +static void iwl_mld_decode_eht_ext_tb(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + if (phy_data->with_data) { + __le32 data5 = phy_data->data5; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC); + } +} + +static void iwl_mld_decode_eht_ru(struct iwl_mld *mld, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht) +{ + u32 ru = le32_get_bits(eht->data[8], + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + enum nl80211_eht_ru_alloc nl_ru; + + /* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields + * in an EHT variant User Info field + */ + + switch (ru) { + case 0 ... 36: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_26; + break; + case 37 ... 52: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52; + break; + case 53 ... 60: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106; + break; + case 61 ... 64: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_242; + break; + case 65 ... 66: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484; + break; + case 67: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996; + break; + case 68: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + break; + case 69: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + break; + case 70 ... 81: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + break; + case 82 ... 89: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + break; + case 90 ... 93: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + break; + case 94 ... 95: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + break; + case 96 ... 99: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + break; + case 100 ... 103: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + break; + case 104: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + break; + case 105 ... 106: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + break; + default: + return; + } + + rx_status->bw = RATE_INFO_BW_EHT_RU; + rx_status->eht.ru = nl_ru; +} + +static void iwl_mld_decode_eht_phy_data(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) + +{ + __le32 data0 = phy_data->data0; + __le32 data1 = phy_data->data1; + __le32 usig_a1 = phy_data->rx_vec[0]; + u8 info_type = phy_data->info_type; + + /* Not in EHT range */ + if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU || + info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT) + return; + + usig->common |= cpu_to_le32 + (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); + if (phy_data->with_data) { + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_UPLINK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } else { + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_UL_FLAG, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } + + usig->common |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_CHECKED); + usig->common |= + LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_VALIDATE, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_OK); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE); + eht->data[0] |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* All RU allocating size/index is in TB format */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT); + eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B0, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B1_B7, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + + iwl_mld_decode_eht_ru(mld, rx_status, eht); + + /* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set + * which is on only in case of monitor mode so no need to check monitor + * mode + */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80); + eht->data[1] |= + le32_encode_bits(mld->monitor.p80, + IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); + if (phy_data->with_data) + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + else + usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + /* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */ + + if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK)) + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN); + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER); + + /* + * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE, + * IWL_RX_PHY_DATA1_EHT_TB_LOW_SS + */ + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF); + eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB) + iwl_mld_decode_eht_ext_tb(mld, phy_data, rx_status, eht, usig); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU) + iwl_mld_decode_eht_ext_mu(mld, phy_data, rx_status, eht, usig); +} + +static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_eht *eht; + struct ieee80211_radiotap_eht_usig *usig; + size_t eht_len = sizeof(*eht); + + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + /* EHT and HE have the same values for LTF */ + u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; + u16 phy_info = phy_data->phy_info; + u32 bw; + + /* u32 for 1 user_info */ + if (phy_data->with_data) + eht_len += sizeof(u32); + + eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len); + + usig = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, + sizeof(*usig)); + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + usig->common |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN); + + /* specific handling for 320MHz */ + bw = u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK); + if (bw == RATE_MCS_CHAN_WIDTH_320_VAL) + bw += le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_EHT_BW320_SLOT); + + usig->common |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw)); + + /* report the AMPDU-EOF bit on single frames */ + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & + cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig); + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + } else { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 1: + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + else + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + break; + case 3: + if (he_type != RATE_MCS_HE_TYPE_TRIG) { + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + } + break; + default: + /* nothing here */ + break; + } + + if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI); + eht->data[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF, + ltf) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI, + rx_status->eht.gi)); + } + + if (!phy_data->with_data) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S); + eht->data[7] |= + le32_encode_bits(le32_get_bits(phy_data->rx_vec[2], + RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK), + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->data[7] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + } else { + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); + + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); + + eht->user_info[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, + u32_get_bits(rate_n_flags, + RATE_VHT_MCS_RATE_CODE_MSK)) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, + u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK))); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +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); + + if (!mld->monitor.cur_aid) + return; + + radiotap = + iwl_mld_radiotap_put_tlv(skb, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE, + sizeof(*radiotap) + vendor_data_len); + + /* Intel OUI */ + radiotap->oui[0] = 0xf6; + radiotap->oui[1] = 0x54; + radiotap->oui[2] = 0x25; + /* radiotap sniffer config sub-namespace */ + radiotap->oui_subtype = 1; + radiotap->vendor_type = 0; + + /* 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 + +static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_hdr *hdr, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + u32 rate_n_flags = phy_data->rate_n_flags; + u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK); + bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK; + + if (WARN_ON_ONCE(phy_data->with_data && (!mpdu_desc || !hdr))) + return; + + /* Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (phy_data->with_data && + (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || + !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK)))) { + IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(mpdu_desc->status)); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE; + + if (phy_data->with_data && + likely(!(phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { + rx_status->mactime = + le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); + + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + } else { + phy_data->info_type = + le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_INFO_TYPE_MASK); + } + + /* management stuff on default queue */ + if (!queue && phy_data->with_data && + unlikely(ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) { + rx_status->boottime_ns = ktime_get_boottime_ns(); + + if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_FOUND; + } + + /* set the preamble flag if appropriate */ + if (format == RATE_MCS_CCK_MSK && + phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; + + iwl_mld_fill_signal(mld, rx_status, phy_data); + + /* This may be overridden by iwl_mld_rx_he() to HE_RU */ + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + rx_status->bw = RATE_INFO_BW_40; + break; + case RATE_MCS_CHAN_WIDTH_80: + rx_status->bw = RATE_INFO_BW_80; + break; + case RATE_MCS_CHAN_WIDTH_160: + rx_status->bw = RATE_INFO_BW_160; + break; + case RATE_MCS_CHAN_WIDTH_320: + rx_status->bw = RATE_INFO_BW_320; + break; + } + + /* must be before L-SIG data */ + if (format == RATE_MCS_HE_MSK) + iwl_mld_rx_he(mld, skb, phy_data, queue); + + iwl_mld_decode_lsig(skb, phy_data); + + rx_status->device_timestamp = phy_data->gp2_on_air_rise; + + /* using TLV format and must be after all fixed len fields */ + if (format == RATE_MCS_EHT_MSK) + iwl_mld_rx_eht(mld, skb, phy_data, queue); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (unlikely(mld->monitor.on)) + iwl_mld_add_rtap_sniffer_config(mld, skb); +#endif + + if (format != RATE_MCS_CCK_MSK && is_sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; + + switch (format) { + case RATE_MCS_HT_MSK: + rx_status->encoding = RX_ENC_HT; + rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags); + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; + break; + case RATE_MCS_VHT_MSK: + case RATE_MCS_HE_MSK: + case RATE_MCS_EHT_MSK: + if (format == RATE_MCS_VHT_MSK) { + rx_status->encoding = RX_ENC_VHT; + } else if (format == RATE_MCS_HE_MSK) { + rx_status->encoding = RX_ENC_HE; + rx_status->he_dcm = + !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); + } else if (format == RATE_MCS_EHT_MSK) { + rx_status->encoding = RX_ENC_EHT; + } + + rx_status->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; + rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; + break; + default: { + int rate = + iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags, + rx_status->band); + + /* valid rate */ + if (rate >= 0 && rate <= 0xFF) { + rx_status->rate_idx = rate; + break; + } + + /* invalid rate */ + rx_status->rate_idx = 0; + + if (net_ratelimit()) + IWL_ERR(mld, "invalid rate_n_flags=0x%x, band=%d\n", + rate_n_flags, rx_status->band); + break; + } + } +} + +/* iwl_mld_create_skb adds the rxb to a new skb */ +static int iwl_mld_build_rx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_hdr *hdr, u16 len, + u8 crypt_len, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; + unsigned int headlen, fraglen, pad_len = 0; + unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + u8 mic_crc_len = u8_get_bits(desc->mac_flags1, + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK) << 1; + + if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + len -= 2; + pad_len = 2; + } + + /* For non monitor interface strip the bytes the RADA might not have + * removed (it might be disabled, e.g. for mgmt frames). As a monitor + * interface cannot exist with other interfaces, this removal is safe + * and sufficient, in monitor mode there's no decryption being done. + */ + if (len > mic_crc_len && !ieee80211_hw_check(mld->hw, RX_INCLUDES_FCS)) + len -= mic_crc_len; + + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr (including crypto if present, and + * an additional 8 bytes for SNAP/ethertype, see below) so that + * splice() or TCP coalesce are more efficient. + * + * Since, in addition, ieee80211_data_to_8023() always pull in at + * least 8 bytes (possibly more for mesh) we can do the same here + * to save the cost of doing it later. That still doesn't pull in + * the actual IP header since the typical case has a SNAP header. + * If the latter changes (there are efforts in the standards group + * to do so) we should revisit this and ieee80211_data_to_8023(). + */ + headlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8; + + /* The firmware may align the packet to DWORD. + * The padding is inserted after the IV. + * After copying the header + IV skip the padding if + * present before copying packet data. + */ + hdrlen += crypt_len; + + if (unlikely(headlen < hdrlen)) + return -EINVAL; + + /* Since data doesn't move data while putting data on skb and that is + * the only way we use, data + len is the next place that hdr would + * be put + */ + skb_set_mac_header(skb, skb->len); + skb_put_data(skb, hdr, hdrlen); + skb_put_data(skb, (u8 *)hdr + hdrlen + pad_len, headlen - hdrlen); + + if (skb->ip_summed == CHECKSUM_COMPLETE) { + struct { + u8 hdr[6]; + __be16 type; + } __packed *shdr = (void *)((u8 *)hdr + hdrlen + pad_len); + + if (unlikely(headlen - hdrlen < sizeof(*shdr) || + !ether_addr_equal(shdr->hdr, rfc1042_header) || + (shdr->type != htons(ETH_P_IP) && + shdr->type != htons(ETH_P_ARP) && + shdr->type != htons(ETH_P_IPV6) && + shdr->type != htons(ETH_P_8021Q) && + shdr->type != htons(ETH_P_PAE) && + shdr->type != htons(ETH_P_TDLS)))) + skb->ip_summed = CHECKSUM_NONE; + } + + fraglen = len - headlen; + + if (fraglen) { + int offset = (u8 *)hdr + headlen + pad_len - + (u8 *)rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + return 0; +} + +/* returns true if a packet is a duplicate or invalid tid and + * should be dropped. Updates AMSDU PN tracking info + */ +VISIBLE_IF_IWLWIFI_KUNIT +bool +iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + const struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_rx_status *rx_status, int queue) +{ + struct iwl_mld_sta *mld_sta; + struct iwl_mld_rxq_dup_data *dup_data; + u8 tid, sub_frame_idx; + + if (WARN_ON(!sta)) + return false; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + + if (WARN_ON_ONCE(!mld_sta->dup_data)) + return false; + + dup_data = &mld_sta->dup_data[queue]; + + /* Drop duplicate 802.11 retransmissions + * (IEEE 802.11-2020: 10.3.2.14 "Duplicate detection and recovery") + */ + if (ieee80211_is_ctl(hdr->frame_control) || + ieee80211_is_any_nullfunc(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return false; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + /* frame has qos control */ + tid = ieee80211_get_tid(hdr); + if (tid >= IWL_MAX_TID_COUNT) + return true; + } else { + tid = IWL_MAX_TID_COUNT; + } + + /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */ + sub_frame_idx = mpdu_desc->amsdu_info & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + + if (IWL_FW_CHECK(mld, + sub_frame_idx > 0 && + !(mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU), + "got sub_frame_idx=%d but A-MSDU flag is not set\n", + sub_frame_idx)) + return true; + + if (unlikely(ieee80211_has_retry(hdr->frame_control) && + dup_data->last_seq[tid] == hdr->seq_ctrl && + dup_data->last_sub_frame_idx[tid] >= sub_frame_idx)) + return true; + + /* Allow same PN as the first subframe for following sub frames */ + if (dup_data->last_seq[tid] == hdr->seq_ctrl && + sub_frame_idx > dup_data->last_sub_frame_idx[tid]) + rx_status->flag |= RX_FLAG_ALLOW_SAME_PN; + + dup_data->last_seq[tid] = hdr->seq_ctrl; + dup_data->last_sub_frame_idx[tid] = sub_frame_idx; + + rx_status->flag |= RX_FLAG_DUP_VALIDATED; + + return false; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_is_dup); + +static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid) +{ + unsigned long now = jiffies; + unsigned long timeout; + struct iwl_mld_baid_data *ba_data; + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!ba_data) { + IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); + return; + } + + if (!ba_data->timeout) + return; + + /* To minimize cache bouncing between RX queues, avoid frequent updates + * to last_rx_timestamp. update it only when the timeout period has + * passed. The worst-case scenario is the session expiring after + * approximately 2 * timeout, which is negligible (the update is + * atomic). + */ + timeout = TU_TO_JIFFIES(ba_data->timeout); + if (time_is_before_jiffies(ba_data->last_rx_timestamp + timeout)) + ba_data->last_rx_timestamp = now; +} + +/* Processes received packets for a station. + * Sets *drop to true if the packet should be dropped. + * Returns the station if found, or NULL otherwise. + */ +static struct ieee80211_sta * +iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, + struct sk_buff *skb, + const struct iwl_rx_mpdu_desc *mpdu_desc, + const struct iwl_rx_packet *pkt, int queue, bool *drop) +{ + struct ieee80211_sta *sta = NULL; + struct ieee80211_link_sta *link_sta = NULL; + struct ieee80211_rx_status *rx_status; + u8 baid; + + if (mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) { + u8 sta_id = le32_get_bits(mpdu_desc->status, + IWL_RX_MPDU_STATUS_STA_ID); + + if (IWL_FW_CHECK(mld, + sta_id >= mld->fw->ucode_capa.num_stations, + "rx_mpdu: invalid sta_id %d\n", sta_id)) + return NULL; + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (!IS_ERR_OR_NULL(link_sta)) + sta = link_sta->sta; + } else if (!is_multicast_ether_addr(hdr->addr2)) { + /* Passing NULL is fine since we prevent two stations with the + * same address from being added. + */ + sta = ieee80211_find_sta_by_ifaddr(mld->hw, hdr->addr2, NULL); + } + + /* we may not have any station yet */ + if (!sta) + return NULL; + + rx_status = IEEE80211_SKB_RXCB(skb); + + if (link_sta && sta->valid_links) { + rx_status->link_valid = true; + rx_status->link_id = link_sta->link_id; + } + + /* fill checksum */ + if (ieee80211_is_data(hdr->frame_control) && + pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) { + u16 hwsum = be16_to_cpu(mpdu_desc->v3.raw_xsum); + + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum_unfold(~(__force __sum16)hwsum); + } + + if (iwl_mld_is_dup(mld, sta, hdr, mpdu_desc, rx_status, queue)) { + IWL_DEBUG_DROP(mld, "Dropping duplicate packet 0x%x\n", + le16_to_cpu(hdr->seq_ctrl)); + *drop = true; + return NULL; + } + + baid = le32_get_bits(mpdu_desc->reorder_data, + IWL_RX_MPDU_REORDER_BAID_MASK); + if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) + iwl_mld_update_last_rx_timestamp(mld, baid); + + if (link_sta && ieee80211_is_data(hdr->frame_control)) { + u8 sub_frame_idx = mpdu_desc->amsdu_info & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + + /* 0 means not an A-MSDU, and 1 means a new A-MSDU */ + if (!sub_frame_idx || sub_frame_idx == 1) + iwl_mld_count_mpdu_rx(link_sta, queue, 1); + + if (!is_multicast_ether_addr(hdr->addr1)) + iwl_mld_low_latency_update_counters(mld, hdr, sta, + queue); + } + + return sta; +} + +#define KEY_IDX_LEN 2 + +static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rx_status, + u32 mpdu_status, + u32 mpdu_len) +{ + struct wireless_dev *wdev; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_vif *mld_vif; + u8 keyidx; + struct ieee80211_key_conf *key; + const u8 *frame = (void *)hdr; + + if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_NONE) + return 0; + + /* For non-beacon, we don't really care. But beacons may + * be filtered out, and we thus need the firmware's replay + * detection, otherwise beacons the firmware previously + * filtered could be replayed, or something like that, and + * it can filter a lot - though usually only if nothing has + * changed. + */ + if (!ieee80211_is_beacon(hdr->frame_control)) + return 0; + + if (!sta) + return -1; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + + /* key mismatch - will also report !MIC_OK but we shouldn't count it */ + if (!(mpdu_status & IWL_RX_MPDU_STATUS_KEY_VALID)) + goto report; + + /* good cases */ + if (likely(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK && + !(mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) { + rx_status->flag |= RX_FLAG_DECRYPTED; + return 0; + } + + /* both keys will have the same cipher and MIC length, use + * whichever one is available + */ + key = rcu_dereference(mld_vif->bigtks[0]); + if (!key) { + key = rcu_dereference(mld_vif->bigtks[1]); + if (!key) + goto report; + } + + if (mpdu_len < key->icv_len + IEEE80211_GMAC_PN_LEN + KEY_IDX_LEN) + goto report; + + /* get the real key ID */ + keyidx = frame[mpdu_len - key->icv_len - IEEE80211_GMAC_PN_LEN - KEY_IDX_LEN]; + /* and if that's the other key, look it up */ + if (keyidx != key->keyidx) { + /* shouldn't happen since firmware checked, but be safe + * in case the MIC length is wrong too, for example + */ + if (keyidx != 6 && keyidx != 7) + return -1; + + key = rcu_dereference(mld_vif->bigtks[keyidx - 6]); + if (!key) + goto report; + } + + /* Report status to mac80211 */ + if (!(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK)) + ieee80211_key_mic_failure(key); + else if (mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR) + ieee80211_key_replay(key); +report: + wdev = ieee80211_vif_to_wdev(mld_sta->vif); + if (wdev->netdev) + cfg80211_rx_unprot_mlme_mgmt(wdev->netdev, (void *)hdr, + mpdu_len); + + return -1; +} + +static int iwl_mld_rx_crypto(struct iwl_mld *mld, + struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rx_status, + struct iwl_rx_mpdu_desc *desc, int queue, + u32 pkt_flags, u8 *crypto_len) +{ + u32 status = le32_to_cpu(desc->status); + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_has_protected(hdr->frame_control))) + return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status, + le16_to_cpu(desc->mpdu_len)); + + if (!ieee80211_has_protected(hdr->frame_control) || + (status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_NONE) + return 0; + + switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) { + case IWL_RX_MPDU_STATUS_SEC_CCM: + case IWL_RX_MPDU_STATUS_SEC_GCM: + BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); + if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) { + IWL_DEBUG_DROP(mld, + "Dropping packet, bad MIC (CCM/GCM)\n"); + return -1; + } + + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; + *crypto_len = IEEE80211_CCMP_HDR_LEN; + return 0; + case IWL_RX_MPDU_STATUS_SEC_TKIP: + if (!(status & IWL_RX_MPDU_STATUS_ICV_OK)) + return -1; + + if (!(status & RX_MPDU_RES_STATUS_MIC_OK)) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + if (pkt_flags & FH_RSCSR_RADA_EN) { + rx_status->flag |= RX_FLAG_ICV_STRIPPED; + rx_status->flag |= RX_FLAG_MMIC_STRIPPED; + } + + *crypto_len = IEEE80211_TKIP_IV_LEN; + rx_status->flag |= RX_FLAG_DECRYPTED; + return 0; + default: + break; + } + + return 0; +} + +static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status) +{ + bool toggle_bit = + phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; + + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + /* Toggle is switched whenever new aggregation starts. Make + * sure ampdu_reference is never 0 so we can later use it to + * see if the frame was really part of an A-MPDU or not. + */ + if (toggle_bit != mld->monitor.ampdu_toggle) { + mld->monitor.ampdu_ref++; + if (mld->monitor.ampdu_ref == 0) + mld->monitor.ampdu_ref++; + mld->monitor.ampdu_toggle = toggle_bit; + phy_data->first_subframe = true; + } + rx_status->ampdu_reference = mld->monitor.ampdu_ref; +} + +static void +iwl_mld_fill_rx_status_band_freq(struct iwl_mld_rx_phy_data *phy_data, + struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_rx_status *rx_status) +{ + enum nl80211_band band; + + band = BAND_IN_RX_STATUS(mpdu_desc->mac_phy_idx); + rx_status->band = iwl_mld_phy_band_to_nl80211(band); + rx_status->freq = ieee80211_channel_to_frequency(phy_data->channel, + rx_status->band); +} + +void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld_rx_phy_data phy_data = {}; + struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + size_t mpdu_desc_size = sizeof(*mpdu_desc); + bool drop = false; + u8 crypto_len = 0; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u32 mpdu_len; + enum iwl_mld_reorder_result reorder_res; + struct ieee80211_rx_status *rx_status; + + if (unlikely(mld->fw_status.in_hw_restart)) + return; + + if (IWL_FW_CHECK(mld, pkt_len < mpdu_desc_size, + "Bad REPLY_RX_MPDU_CMD size (%d)\n", pkt_len)) + return; + + mpdu_len = le16_to_cpu(mpdu_desc->mpdu_len); + + if (IWL_FW_CHECK(mld, mpdu_len + mpdu_desc_size > pkt_len, + "FW lied about packet len (%d)\n", pkt_len)) + return; + + /* Don't use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mld, "alloc_skb failed\n"); + return; + } + + hdr = (void *)(pkt->data + mpdu_desc_size); + + iwl_mld_fill_phy_data(mpdu_desc, &phy_data); + + if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + /* If the device inserted padding it means that (it thought) + * the 802.11 header wasn't a multiple of 4 bytes long. In + * this case, reserve two bytes at the start of the SKB to + * align the payload properly in case we end up copying it. + */ + skb_reserve(skb, 2); + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + /* this is needed early */ + iwl_mld_fill_rx_status_band_freq(&phy_data, mpdu_desc, rx_status); + + rcu_read_lock(); + + sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop); + if (drop) + goto drop; + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU)) + iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status); + + iwl_mld_rx_fill_status(mld, skb, &phy_data, mpdu_desc, hdr, queue); + + if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue, + le32_to_cpu(pkt->len_n_flags), &crypto_len)) + goto drop; + + if (iwl_mld_build_rx_skb(mld, skb, hdr, mpdu_len, crypto_len, rxb)) + goto drop; + + /* time sync frame is saved and will be released later when the + * notification with the timestamps arrives. + */ + if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2)) + goto out; + + reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc); + switch (reorder_res) { + case IWL_MLD_PASS_SKB: + break; + case IWL_MLD_DROP_SKB: + goto drop; + case IWL_MLD_BUFFERED_SKB: + goto out; + default: + WARN_ON(1); + goto drop; + } + + iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta); + + goto out; + +drop: + kfree_skb(skb); +out: + rcu_read_unlock(); +} + +#define SYNC_RX_QUEUE_TIMEOUT (HZ) +void iwl_mld_sync_rx_queues(struct iwl_mld *mld, + enum iwl_mld_internal_rxq_notif_type type, + const void *notif_payload, u32 notif_payload_size) +{ + u8 num_rx_queues = mld->trans->num_rx_queues; + struct { + struct iwl_rxq_sync_cmd sync_cmd; + struct iwl_mld_internal_rxq_notif notif; + } __packed cmd = { + .sync_cmd.rxq_mask = cpu_to_le32(BIT(num_rx_queues) - 1), + .sync_cmd.count = + cpu_to_le32(sizeof(struct iwl_mld_internal_rxq_notif) + + notif_payload_size), + .notif.type = type, + .notif.cookie = mld->rxq_sync.cookie, + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), + .data[0] = &cmd, + .len[0] = sizeof(cmd), + .data[1] = notif_payload, + .len[1] = notif_payload_size, + }; + int ret; + + /* size must be a multiple of DWORD */ + if (WARN_ON(cmd.sync_cmd.count & cpu_to_le32(3))) + return; + + mld->rxq_sync.state = (1 << num_rx_queues) - 1; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) { + IWL_ERR(mld, "Failed to trigger RX queues sync (%d)\n", ret); + goto out; + } + + 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); + +out: + mld->rxq_sync.state = 0; + mld->rxq_sync.cookie++; +} + +void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_rxq_sync_notification *notif; + struct iwl_mld_internal_rxq_notif *internal_notif; + u32 len = iwl_rx_packet_payload_len(pkt); + size_t combined_notif_len = sizeof(*notif) + sizeof(*internal_notif); + + notif = (void *)pkt->data; + internal_notif = (void *)notif->payload; + + if (IWL_FW_CHECK(mld, len < combined_notif_len, + "invalid notification size %u (%zu)\n", + len, combined_notif_len)) + return; + + len -= combined_notif_len; + + if (IWL_FW_CHECK(mld, mld->rxq_sync.cookie != internal_notif->cookie, + "received expired RX queue sync message (cookie=%d expected=%d q[%d])\n", + internal_notif->cookie, mld->rxq_sync.cookie, queue)) + return; + + switch (internal_notif->type) { + case IWL_MLD_RXQ_EMPTY: + IWL_FW_CHECK(mld, len, + "invalid empty notification size %d\n", len); + break; + case IWL_MLD_RXQ_NOTIF_DEL_BA: + if (IWL_FW_CHECK(mld, len != sizeof(struct iwl_mld_delba_data), + "invalid delba notification size %u (%zu)\n", + len, sizeof(struct iwl_mld_delba_data))) + break; + iwl_mld_del_ba(mld, queue, (void *)internal_notif->payload); + break; + default: + WARN_ON_ONCE(1); + } + + IWL_FW_CHECK(mld, !test_and_clear_bit(queue, &mld->rxq_sync.state), + "RXQ sync: queue %d responded a second time!\n", queue); + + if (READ_ONCE(mld->rxq_sync.state) == 0) + wake_up(&mld->rxq_sync.waitq); +} + +void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_rx_no_data_ver_3 *desc; + struct iwl_mld_rx_phy_data phy_data; + struct ieee80211_rx_status *rx_status; + struct sk_buff *skb; + u32 format, rssi; + + if (unlikely(mld->fw_status.in_hw_restart)) + return; + + if (IWL_FW_CHECK(mld, iwl_rx_packet_payload_len(pkt) < sizeof(*desc), + "Bad RX_NO_DATA_NOTIF size (%d)\n", + iwl_rx_packet_payload_len(pkt))) + return; + + desc = (void *)pkt->data; + + rssi = le32_to_cpu(desc->rssi); + phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); + phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); + phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + phy_data.data0 = desc->phy_info[0]; + phy_data.data1 = desc->phy_info[1]; + phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; + phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); + phy_data.rate_n_flags = le32_to_cpu(desc->rate); + phy_data.with_data = false; + + BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec)); + memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec)); + + format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + + /* Don't use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mld, "alloc_skb failed\n"); + return; + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + /* 0-length PSDU */ + rx_status->flag |= RX_FLAG_NO_PSDU; + + /* mark as failed PLCP on any errors to skip checks in mac80211 */ + if (le32_get_bits(desc->info, RX_NO_DATA_INFO_ERR_MSK) != + RX_NO_DATA_INFO_ERR_NONE) + rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; + + switch (le32_get_bits(desc->info, RX_NO_DATA_INFO_TYPE_MSK)) { + case RX_NO_DATA_INFO_TYPE_NDP: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING; + break; + case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED: + case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED; + break; + default: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_VENDOR; + break; + } + + rx_status->band = phy_data.channel > 14 ? NL80211_BAND_5GHZ : + NL80211_BAND_2GHZ; + + rx_status->freq = ieee80211_channel_to_frequency(phy_data.channel, + rx_status->band); + + iwl_mld_rx_fill_status(mld, skb, &phy_data, NULL, NULL, queue); + + /* No more radiotap info should be added after this point. + * Mark it as mac header for upper layers to know where + * the radiotap header ends. + */ + skb_set_mac_header(skb, skb->len); + + /* Override the nss from the rx_vec since the rate_n_flags has + * only 1 bit for the nss which gives a max of 2 ss but there + * may be up to 8 spatial streams. + */ + switch (format) { + case RATE_MCS_VHT_MSK: + rx_status->nss = + le32_get_bits(desc->rx_vec[0], + RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1; + break; + case RATE_MCS_HE_MSK: + rx_status->nss = + le32_get_bits(desc->rx_vec[0], + RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1; + break; + case RATE_MCS_EHT_MSK: + rx_status->nss = + le32_get_bits(desc->rx_vec[2], + RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1; + } + + /* pass the packet to mac80211 */ + rcu_read_lock(); + ieee80211_rx_napi(mld->hw, NULL, skb, napi); + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h new file mode 100644 index 000000000000..2beabd7e70b1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_rx_h__ +#define __iwl_mld_rx_h__ + +#include "mld.h" + +/** + * enum iwl_mld_internal_rxq_notif_type - RX queue sync notif types + * + * @IWL_MLD_RXQ_EMPTY: empty sync notification + * @IWL_MLD_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA + */ +enum iwl_mld_internal_rxq_notif_type { + IWL_MLD_RXQ_EMPTY, + IWL_MLD_RXQ_NOTIF_DEL_BA, +}; + +/** + * struct iwl_mld_internal_rxq_notif - @iwl_rxq_sync_cmd internal data. + * This data is echoed by the firmware to all RSS queues and should be DWORD + * aligned. FW is agnostic to the data, so there are no endianness requirements + * + * @type: one of &iwl_mld_internal_rxq_notif_type + * @cookie: unique internal cookie to identify old notifications + * @reserved: reserved for alignment + * @payload: data to send to RX queues based on the type (may be empty) + */ +struct iwl_mld_internal_rxq_notif { + u8 type; + u8 reserved[3]; + u32 cookie; + u8 payload[]; +} __packed; + +/** + * struct iwl_mld_rx_queues_sync - RX queues sync data + * + * @waitq: wait queue for RX queues sync completion + * @cookie: unique id to correlate sync requests with responses + * @state: bitmask representing the sync state of RX queues + * all RX queues bits are set before sending the command, and the + * corresponding queue bit cleared upon handling the notification + */ +struct iwl_mld_rx_queues_sync { + wait_queue_head_t waitq; + u32 cookie; + unsigned long state; +}; + +void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue); + +void iwl_mld_sync_rx_queues(struct iwl_mld *mld, + enum iwl_mld_internal_rxq_notif_type type, + const void *notif_payload, u32 notif_payload_size); + +void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); + +void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta); + +void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); + +#endif /* __iwl_mld_agg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c new file mode 100644 index 000000000000..5f4ede92028b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -0,0 +1,2006 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include + +#include "mld.h" +#include "scan.h" +#include "hcmd.h" +#include "iface.h" +#include "phy.h" +#include "mlo.h" + +#include "fw/api/scan.h" +#include "fw/dbg.h" + +#define IWL_SCAN_DWELL_ACTIVE 10 +#define IWL_SCAN_DWELL_PASSIVE 110 +#define IWL_SCAN_NUM_OF_FRAGS 3 + +/* adaptive dwell max budget time [TU] for full scan */ +#define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 + +/* adaptive dwell max budget time [TU] for directed scan */ +#define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100 + +/* adaptive dwell default high band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8 + +/* adaptive dwell default low band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2 + +/* adaptive dwell default APs number for P2P social channels (1, 6, 11) */ +#define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 + +/* adaptive dwell number of APs override for P2P friendly GO channels */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10 + +/* adaptive dwell number of APs override for P2P social channels */ +#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2 + +/* adaptive dwell number of APs override mask for p2p friendly GO */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20) + +/* 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 + +enum iwl_mld_scan_type { + IWL_SCAN_TYPE_NOT_SET, + IWL_SCAN_TYPE_UNASSOC, + IWL_SCAN_TYPE_WILD, + IWL_SCAN_TYPE_MILD, + IWL_SCAN_TYPE_FRAGMENTED, + IWL_SCAN_TYPE_FAST_BALANCE, +}; + +struct iwl_mld_scan_timing_params { + u32 suspend_time; + u32 max_out_time; +}; + +static const struct iwl_mld_scan_timing_params scan_timing[] = { + [IWL_SCAN_TYPE_UNASSOC] = { + .suspend_time = 0, + .max_out_time = 0, + }, + [IWL_SCAN_TYPE_WILD] = { + .suspend_time = 30, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_MILD] = { + .suspend_time = 120, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_FRAGMENTED] = { + .suspend_time = 95, + .max_out_time = 44, + }, + [IWL_SCAN_TYPE_FAST_BALANCE] = { + .suspend_time = 30, + .max_out_time = 37, + }, +}; + +struct iwl_mld_scan_params { + enum iwl_mld_scan_type type; + u32 n_channels; + u16 delay; + int n_ssids; + struct cfg80211_ssid *ssids; + struct ieee80211_channel **channels; + u32 flags; + u8 *mac_addr; + u8 *mac_addr_mask; + bool no_cck; + bool pass_all; + int n_match_sets; + struct iwl_scan_probe_req preq; + struct cfg80211_match_set *match_sets; + int n_scan_plans; + struct cfg80211_sched_scan_plan *scan_plans; + bool iter_notif; + bool respect_p2p_go; + u8 fw_link_id; + struct cfg80211_scan_6ghz_params *scan_6ghz_params; + u32 n_6ghz_params; + bool scan_6ghz; + bool enable_6ghz_passive; + u8 bssid[ETH_ALEN] __aligned(2); +}; + +struct iwl_mld_scan_respect_p2p_go_iter_data { + struct ieee80211_vif *current_vif; + bool p2p_go; +}; + +static void iwl_mld_scan_respect_p2p_go_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_scan_respect_p2p_go_iter_data *data = _data; + + /* exclude the given vif */ + if (vif == data->current_vif) + return; + + /* TODO: CDB check the band of the GO */ + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && + iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) + data->p2p_go = true; +} + +static bool iwl_mld_get_respect_p2p_go(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency) +{ + struct iwl_mld_scan_respect_p2p_go_iter_data data = { + .current_vif = vif, + .p2p_go = false, + }; + + if (!low_latency) + return false; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_respect_p2p_go_iter, + &data); + + return data.p2p_go; +} + +struct iwl_mld_scan_iter_data { + struct ieee80211_vif *current_vif; + bool active_vif; + bool is_dcm_with_p2p_go; + bool global_low_latency; +}; + +static void iwl_mld_scan_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_scan_iter_data *data = _data; + struct ieee80211_vif *curr_vif = data->current_vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_vif *curr_mld_vif; + unsigned long curr_vif_active_links; + u16 link_id; + + data->global_low_latency |= iwl_mld_vif_low_latency(mld_vif); + + if ((ieee80211_vif_is_mld(vif) && vif->active_links) || + (vif->type != NL80211_IFTYPE_P2P_DEVICE && + mld_vif->deflink.active)) + data->active_vif = true; + + if (vif == curr_vif) + return; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_GO) + return; + + /* Currently P2P GO can't be AP MLD so the logic below assumes that */ + WARN_ON_ONCE(ieee80211_vif_is_mld(vif)); + + curr_vif_active_links = + ieee80211_vif_is_mld(curr_vif) ? curr_vif->active_links : 1; + + curr_mld_vif = iwl_mld_vif_from_mac80211(curr_vif); + + for_each_set_bit(link_id, &curr_vif_active_links, + IEEE80211_MLD_MAX_NUM_LINKS) { + struct iwl_mld_link *curr_mld_link = + iwl_mld_link_dereference_check(curr_mld_vif, link_id); + + if (WARN_ON(!curr_mld_link)) + return; + + if (rcu_access_pointer(curr_mld_link->chan_ctx) && + rcu_access_pointer(mld_vif->deflink.chan_ctx) != + rcu_access_pointer(curr_mld_link->chan_ctx)) { + data->is_dcm_with_p2p_go = true; + return; + } + } +} + +static enum +iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_scan_iter_data *data) +{ + enum iwl_mld_traffic_load load = mld->scan.traffic_load.status; + + /* A scanning AP interface probably wants to generate a survey to do + * ACS (automatic channel selection). + * Force a non-fragmented scan in that case. + */ + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + return IWL_SCAN_TYPE_WILD; + + if (!data->active_vif) + return IWL_SCAN_TYPE_UNASSOC; + + if ((load == IWL_MLD_TRAFFIC_HIGH || data->global_low_latency) && + vif->type != NL80211_IFTYPE_P2P_DEVICE) + return IWL_SCAN_TYPE_FRAGMENTED; + + /* In case of DCM with P2P GO set all scan requests as + * fast-balance scan + */ + if (vif->type == NL80211_IFTYPE_STATION && + data->is_dcm_with_p2p_go) + return IWL_SCAN_TYPE_FAST_BALANCE; + + if (load >= IWL_MLD_TRAFFIC_MEDIUM || data->global_low_latency) + return IWL_SCAN_TYPE_MILD; + + return IWL_SCAN_TYPE_WILD; +} + +static u8 * +iwl_mld_scan_add_2ghz_elems(struct iwl_mld *mld, const u8 *ies, + size_t len, u8 *const pos) +{ + static const u8 before_ds_params[] = { + WLAN_EID_SSID, + WLAN_EID_SUPP_RATES, + WLAN_EID_REQUEST, + WLAN_EID_EXT_SUPP_RATES, + }; + size_t offs; + u8 *newpos = pos; + + offs = ieee80211_ie_split(ies, len, + before_ds_params, + ARRAY_SIZE(before_ds_params), + 0); + + memcpy(newpos, ies, offs); + newpos += offs; + + /* Add a placeholder for DS Parameter Set element */ + *newpos++ = WLAN_EID_DS_PARAMS; + *newpos++ = 1; + *newpos++ = 0; + + memcpy(newpos, ies + offs, len - offs); + newpos += len - offs; + + return newpos; +} + +static void +iwl_mld_scan_add_tpc_report_elem(u8 *pos) +{ + pos[0] = WLAN_EID_VENDOR_SPECIFIC; + pos[1] = WFA_TPC_IE_LEN - 2; + pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff; + pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff; + pos[4] = WLAN_OUI_MICROSOFT & 0xff; + pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC; + pos[6] = 0; + /* pos[7] - tx power will be inserted by the FW */ + pos[7] = 0; + pos[8] = 0; +} + +static u32 +iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status) +{ + if (scan_status == IWL_MLD_SCAN_REGULAR) + return IWL_SCAN_PRIORITY_EXT_6; + if (scan_status == IWL_MLD_SCAN_INT_MLO) + return IWL_SCAN_PRIORITY_EXT_4; + + return IWL_SCAN_PRIORITY_EXT_2; +} + +static bool +iwl_mld_scan_is_regular(struct iwl_mld_scan_params *params) +{ + return params->n_scan_plans == 1 && + params->scan_plans[0].iterations == 1; +} + +static bool +iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type) +{ + return (type == IWL_SCAN_TYPE_FRAGMENTED || + type == IWL_SCAN_TYPE_FAST_BALANCE); +} + +static int +iwl_mld_scan_uid_by_status(struct iwl_mld *mld, int status) +{ + for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++) + if (mld->scan.uid_status[i] == status) + return i; + + return -ENOENT; +} + +static const char * +iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status) +{ + switch (status) { + case IWL_SCAN_EBS_SUCCESS: + return "successful"; + case IWL_SCAN_EBS_INACTIVE: + return "inactive"; + case IWL_SCAN_EBS_FAILED: + case IWL_SCAN_EBS_CHAN_NOT_FOUND: + default: + return "failed"; + } +} + +static int +iwl_mld_scan_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) +{ + for (int i = 0; i < PROBE_OPTION_MAX; i++) { + if (!ssid_list[i].len) + return -1; + if (ssid_list[i].len == ssid_len && + !memcmp(ssid_list[i].ssid, ssid, ssid_len)) + return i; + } + + return -1; +} + +static bool +iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids, + struct ieee80211_scan_ies *ies, int n_channels) +{ + return ((n_ssids <= PROBE_OPTION_MAX) && + (n_channels <= mld->fw->ucode_capa.n_scan_channels) & + (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] + + ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <= + iwl_mld_scan_max_template_size())); +} + +static void +iwl_mld_scan_build_probe_req(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_scan_ies *ies, + struct iwl_mld_scan_params *params) +{ + struct ieee80211_mgmt *frame = (void *)params->preq.buf; + u8 *pos, *newpos; + const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? + params->mac_addr : NULL; + + if (mac_addr) + get_random_mask_addr(frame->sa, mac_addr, + params->mac_addr_mask); + else + memcpy(frame->sa, vif->addr, ETH_ALEN); + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + ether_addr_copy(frame->bssid, params->bssid); + frame->seq_ctrl = 0; + + pos = frame->u.probe_req.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + params->preq.mac_header.offset = 0; + params->preq.mac_header.len = cpu_to_le16(24 + 2); + + /* Insert DS parameter set element on 2.4 GHz band */ + newpos = iwl_mld_scan_add_2ghz_elems(mld, + ies->ies[NL80211_BAND_2GHZ], + ies->len[NL80211_BAND_2GHZ], + pos); + params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[0].len = cpu_to_le16(newpos - pos); + pos = newpos; + + memcpy(pos, ies->ies[NL80211_BAND_5GHZ], + ies->len[NL80211_BAND_5GHZ]); + params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[1].len = + cpu_to_le16(ies->len[NL80211_BAND_5GHZ]); + pos += ies->len[NL80211_BAND_5GHZ]; + + memcpy(pos, ies->ies[NL80211_BAND_6GHZ], + ies->len[NL80211_BAND_6GHZ]); + params->preq.band_data[2].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[2].len = + cpu_to_le16(ies->len[NL80211_BAND_6GHZ]); + pos += ies->len[NL80211_BAND_6GHZ]; + + memcpy(pos, ies->common_ies, ies->common_ie_len); + params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); + + iwl_mld_scan_add_tpc_report_elem(pos + ies->common_ie_len); + params->preq.common_data.len = cpu_to_le16(ies->common_ie_len + + WFA_TPC_IE_LEN); +} + +static u16 +iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + enum iwl_mld_scan_status scan_status) +{ + u16 flags = 0; + + /* If no direct SSIDs are provided perform a passive scan. Otherwise, + * if there is a single SSID which is not the broadcast SSID, assume + * that the scan is intended for roaming purposes and thus enable Rx on + * all chains to improve chances of hearing the beacons/probe responses. + */ + if (params->n_ssids == 0) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE; + else if (params->n_ssids == 1 && params->ssids[0].ssid_len) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_USE_ALL_RX_CHAINS; + + if (params->pass_all) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL; + else + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH; + + if (iwl_mld_scan_is_fragmented(params->type)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1; + + if (!iwl_mld_scan_is_regular(params)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC; + + if (params->iter_notif || + mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE; + + if (scan_status == IWL_MLD_SCAN_SCHED || + scan_status == IWL_MLD_SCAN_NETDETECT) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE; + + if (params->flags & (NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE | + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_OCE; + + if ((scan_status == IWL_MLD_SCAN_SCHED || + scan_status == IWL_MLD_SCAN_NETDETECT) && + params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN; + + if (params->enable_6ghz_passive) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN; + + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL; + + return flags; +} + +static u8 +iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, u16 gen_flags) +{ + u8 flags = 0; + + /* TODO: CDB */ + if (params->respect_p2p_go) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB | + IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB; + + if (params->scan_6ghz) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT; + + return flags; +} + +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) +{ + const struct iwl_mld_scan_timing_params *timing = + &scan_timing[params->type]; + + gp->adwell_default_social_chn = + IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; + gp->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; + gp->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; + + if (params->n_ssids && params->ssids[0].ssid_len) + gp->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); + else + gp->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); + + gp->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + + gp->max_out_of_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->max_out_time); + gp->suspend_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->suspend_time); + + gp->active_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; + gp->passive_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; + gp->active_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; + gp->passive_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; + + IWL_DEBUG_SCAN(mld, + "Scan: adwell_max_budget=%d max_out_of_time=%d suspend_time=%d\n", + gp->adwell_max_budget, + gp->max_out_of_time[SCAN_LB_LMAC_IDX], + gp->suspend_time[SCAN_LB_LMAC_IDX]); +} + +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, + enum iwl_mld_scan_status scan_status) +{ + 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, + gen_flags); + + IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n", + gen_flags, gen_flags2); + + gp->flags = cpu_to_le16(gen_flags); + gp->flags2 = gen_flags2; + + iwl_mld_scan_cmd_set_dwell(mld, gp, params); + + if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) + gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + + if (params->fw_link_id != IWL_MLD_INVALID_FW_ID) + gp->scan_start_mac_or_link_id = params->fw_link_id; +} + +static int +iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params, + struct iwl_scan_umac_schedule *schedule, + __le16 *delay) +{ + if (WARN_ON(!params->n_scan_plans || + params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) + return -EINVAL; + + for (int i = 0; i < params->n_scan_plans; i++) { + struct cfg80211_sched_scan_plan *scan_plan = + ¶ms->scan_plans[i]; + + schedule[i].iter_count = scan_plan->iterations; + schedule[i].interval = + cpu_to_le16(scan_plan->interval); + } + + /* If the number of iterations of the last scan plan is set to zero, + * it should run infinitely. However, this is not always the case. + * For example, when regular scan is requested the driver sets one scan + * plan with one iteration. + */ + if (!schedule[params->n_scan_plans - 1].iter_count) + schedule[params->n_scan_plans - 1].iter_count = 0xff; + + *delay = cpu_to_le16(params->delay); + + return 0; +} + +/* We insert the SSIDs in an inverted order, because the FW will + * invert it back. + */ +static void +iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params, + struct iwl_ssid_ie *ssids, u32 *ssid_bitmap) +{ + int i, j; + int index; + u32 tmp_bitmap = 0; + + /* copy SSIDs from match list. iwl_config_sched_scan_profiles() + * uses the order of these ssids to config match list. + */ + for (i = 0, j = params->n_match_sets - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + /* skip empty SSID match_sets */ + if (!params->match_sets[j].ssid.ssid_len) + continue; + + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->match_sets[j].ssid.ssid_len; + memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, + ssids[i].len); + } + + /* add SSIDs from scan SSID list */ + for (j = params->n_ssids - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + index = iwl_mld_scan_ssid_exist(params->ssids[j].ssid, + params->ssids[j].ssid_len, + ssids); + if (index < 0) { + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->ssids[j].ssid_len; + memcpy(ssids[i].ssid, params->ssids[j].ssid, + ssids[i].len); + tmp_bitmap |= BIT(i); + } else { + tmp_bitmap |= BIT(index); + } + } + + if (ssid_bitmap) + *ssid_bitmap = tmp_bitmap; +} + +static void +iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params, + struct iwl_scan_probe_params_v4 *pp) +{ + int j, idex_s = 0, idex_b = 0; + struct cfg80211_scan_6ghz_params *scan_6ghz_params = + params->scan_6ghz_params; + + for (j = 0; + j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE; + j++) { + if (!params->ssids[j].ssid_len) + continue; + + pp->short_ssid[idex_s] = + cpu_to_le32(~crc32_le(~0, params->ssids[j].ssid, + params->ssids[j].ssid_len)); + + /* hidden 6ghz scan */ + pp->direct_scan[idex_s].id = WLAN_EID_SSID; + pp->direct_scan[idex_s].len = params->ssids[j].ssid_len; + memcpy(pp->direct_scan[idex_s].ssid, params->ssids[j].ssid, + params->ssids[j].ssid_len); + idex_s++; + } + + /* Populate the arrays of the short SSIDs and the BSSIDs using the 6GHz + * collocated parameters. This might not be optimal, as this processing + * does not (yet) correspond to the actual channels, so it is possible + * that some entries would be left out. + */ + for (j = 0; j < params->n_6ghz_params; j++) { + int k; + + /* First, try to place the short SSID */ + if (scan_6ghz_params[j].short_ssid_valid) { + for (k = 0; k < idex_s; k++) { + if (pp->short_ssid[k] == + cpu_to_le32(scan_6ghz_params[j].short_ssid)) + break; + } + + if (k == idex_s && idex_s < SCAN_SHORT_SSID_MAX_SIZE) { + pp->short_ssid[idex_s++] = + cpu_to_le32(scan_6ghz_params[j].short_ssid); + } + } + + /* try to place BSSID for the same entry */ + for (k = 0; k < idex_b; k++) { + if (!memcmp(&pp->bssid_array[k], + scan_6ghz_params[j].bssid, ETH_ALEN)) + break; + } + + if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE && + !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid), + "scan: invalid BSSID at index %u, index_b=%u\n", + j, idex_b)) { + memcpy(&pp->bssid_array[idex_b++], + scan_6ghz_params[j].bssid, ETH_ALEN); + } + } + + pp->short_ssid_num = idex_s; + pp->bssid_num = idex_b; +} + +static void +iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params, + struct iwl_scan_probe_params_v4 *pp, + u32 *bitmap_ssid) +{ + pp->preq = params->preq; + + if (params->scan_6ghz) { + iwl_mld_scan_fill_6g_chan_list(params, pp); + return; + } + + /* relevant only for 2.4 GHz /5 GHz scan */ + iwl_mld_scan_cmd_build_ssids(params, pp->direct_scan, bitmap_ssid); +} + +static bool +iwl_mld_scan_use_ebs(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool low_latency) +{ + const struct iwl_ucode_capabilities *capa = &mld->fw->ucode_capa; + + /* We can only use EBS if: + * 1. the feature is supported. + * 2. the last EBS was successful. + * 3. it's not a p2p find operation. + * 4. we are not in low latency mode, + * or if fragmented ebs is supported by the FW + * 5. the VIF is not an AP interface (scan wants survey results) + */ + return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && + !mld->scan.last_ebs_failed && + vif->type != NL80211_IFTYPE_P2P_DEVICE && + (!low_latency || fw_has_api(capa, IWL_UCODE_TLV_API_FRAG_EBS)) && + ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_AP); +} + +static u8 +iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + bool low_latency) +{ + u8 flags = 0; + + flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; + + if (iwl_mld_scan_use_ebs(mld, vif, low_latency)) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + + /* set fragmented ebs for fragmented scan */ + if (iwl_mld_scan_is_fragmented(params->type)) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + + /* Force EBS in case the scan is a fragmented and there is a need + * to take P2P GO operation into consideration during scan operation. + */ + /* TODO: CDB */ + if (iwl_mld_scan_is_fragmented(params->type) && + params->respect_p2p_go) { + IWL_DEBUG_SCAN(mld, "Respect P2P GO. Force EBS\n"); + flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS; + } + + return flags; +} + +static const u8 p2p_go_friendly_chs[] = { + 36, 40, 44, 48, 149, 153, 157, 161, 165, +}; + +static const u8 social_chs[] = { + 1, 6, 11 +}; + +static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id) +{ + if (vif_type != NL80211_IFTYPE_P2P_DEVICE) + return 0; + + for (int i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { + if (ch_id == p2p_go_friendly_chs[i]) + return IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; + } + + for (int i = 0; i < ARRAY_SIZE(social_chs); i++) { + if (ch_id == social_chs[i]) + return IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT; + } + + return 0; +} + +static void +iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld, + struct ieee80211_channel **channels, + struct iwl_scan_channel_params_v7 *cp, + int n_channels, u32 flags, + enum nl80211_iftype vif_type) +{ + 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]; + u8 iwl_band = iwl_mld_nl80211_band_to_fw(band); + u32 n_aps_flag = + iwl_mld_scan_ch_n_aps_flag(vif_type, + channels[i]->hw_value); + + if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) + n_aps_flag = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; + + cfg->flags = cpu_to_le32(flags | n_aps_flag); + cfg->channel_num = channels[i]->hw_value; + if (cfg80211_channel_is_psc(channels[i])) + cfg->flags = 0; + + if (band == NL80211_BAND_6GHZ) { + /* 6 GHz channels should only appear in a scan request + * that has scan_6ghz set. The only exception is MLO + * scan, which has to be passive. + */ + WARN_ON_ONCE(cfg->flags != 0); + cfg->flags = + cpu_to_le32(IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE); + } + + cfg->v2.iter_count = 1; + cfg->v2.iter_interval = 0; + cfg->flags |= cpu_to_le32(iwl_band << + IWL_CHAN_CFG_FLAGS_BAND_POS); + } +} + +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, + enum nl80211_iftype vif_type) +{ + struct cfg80211_scan_6ghz_params *scan_6ghz_params = + params->scan_6ghz_params; + u32 i; + u8 ch_cnt; + + for (i = 0, ch_cnt = 0; i < params->n_channels; i++) { + struct iwl_scan_channel_cfg_umac *cfg = + &cp->channel_config[ch_cnt]; + + u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; + u8 k, n_s_ssids = 0, n_bssids = 0; + u8 max_s_ssids, max_bssids; + bool force_passive = false, found = false, allow_passive = true, + unsolicited_probe_on_chan = false, psc_no_listen = false; + s8 psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; + + /* Avoid performing passive scan on non PSC channels unless the + * scan is specifically a passive scan, i.e., no SSIDs + * configured in the scan command. + */ + if (!cfg80211_channel_is_psc(params->channels[i]) && + !params->n_6ghz_params && params->n_ssids) + continue; + + cfg->channel_num = params->channels[i]->hw_value; + cfg->flags |= + cpu_to_le32(PHY_BAND_6 << IWL_CHAN_CFG_FLAGS_BAND_POS); + + cfg->v5.iter_count = 1; + cfg->v5.iter_interval = 0; + + for (u32 j = 0; j < params->n_6ghz_params; j++) { + s8 tmp_psd_20; + + if (!(scan_6ghz_params[j].channel_idx == i)) + continue; + + unsolicited_probe_on_chan |= + scan_6ghz_params[j].unsolicited_probe; + + /* Use the highest PSD value allowed as advertised by + * APs for this channel + */ + tmp_psd_20 = scan_6ghz_params[j].psd_20; + if (tmp_psd_20 != + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED && + (psd_20 == + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED || + psd_20 < tmp_psd_20)) + psd_20 = tmp_psd_20; + + psc_no_listen |= scan_6ghz_params[j].psc_no_listen; + } + + /* In the following cases apply passive scan: + * 1. Non fragmented scan: + * - PSC channel with NO_LISTEN_FLAG on should be treated + * like non PSC channel + * - Non PSC channel with more than 3 short SSIDs or more + * than 9 BSSIDs. + * - Non PSC Channel with unsolicited probe response and + * more than 2 short SSIDs or more than 6 BSSIDs. + * - PSC channel with more than 2 short SSIDs or more than + * 6 BSSIDs. + * 2. Fragmented scan: + * - PSC channel with more than 1 SSID or 3 BSSIDs. + * - Non PSC channel with more than 2 SSIDs or 6 BSSIDs. + * - Non PSC channel with unsolicited probe response and + * more than 1 SSID or more than 3 BSSIDs. + */ + if (!iwl_mld_scan_is_fragmented(params->type)) { + if (!cfg80211_channel_is_psc(params->channels[i]) || + psc_no_listen) { + if (unsolicited_probe_on_chan) { + max_s_ssids = 2; + max_bssids = 6; + } else { + max_s_ssids = 3; + max_bssids = 9; + } + } else { + max_s_ssids = 2; + max_bssids = 6; + } + } else if (cfg80211_channel_is_psc(params->channels[i])) { + max_s_ssids = 1; + max_bssids = 3; + } else { + if (unsolicited_probe_on_chan) { + max_s_ssids = 1; + max_bssids = 3; + } else { + max_s_ssids = 2; + max_bssids = 6; + } + } + + /* To optimize the scan time, i.e., reduce the scan dwell time + * on each channel, the below logic tries to set 3 direct BSSID + * probe requests for each broadcast probe request with a short + * SSID. + */ + for (u32 j = 0; j < params->n_6ghz_params; j++) { + if (!(scan_6ghz_params[j].channel_idx == i)) + continue; + + found = false; + + for (k = 0; + k < pp->short_ssid_num && n_s_ssids < max_s_ssids; + k++) { + if (!scan_6ghz_params[j].unsolicited_probe && + le32_to_cpu(pp->short_ssid[k]) == + scan_6ghz_params[j].short_ssid) { + /* Relevant short SSID bit set */ + if (s_ssid_bitmap & BIT(k)) { + found = true; + break; + } + + /* Prefer creating BSSID entries unless + * the short SSID probe can be done in + * the same channel dwell iteration. + * + * We also need to create a short SSID + * entry for any hidden AP. + */ + if (3 * n_s_ssids > n_bssids && + !pp->direct_scan[k].len) + break; + + /* Hidden AP, cannot do passive scan */ + if (pp->direct_scan[k].len) + allow_passive = false; + + s_ssid_bitmap |= BIT(k); + n_s_ssids++; + found = true; + break; + } + } + + if (found) + continue; + + for (k = 0; k < pp->bssid_num; k++) { + if (memcmp(&pp->bssid_array[k], + scan_6ghz_params[j].bssid, + ETH_ALEN)) + continue; + + if (bssid_bitmap & BIT(k)) + break; + + if (n_bssids < max_bssids) { + bssid_bitmap |= BIT(k); + n_bssids++; + } else { + force_passive = TRUE; + } + + break; + } + } + + if (cfg80211_channel_is_psc(params->channels[i]) && + psc_no_listen) + flags |= IWL_UHB_CHAN_CFG_FLAG_PSC_CHAN_NO_LISTEN; + + if (unsolicited_probe_on_chan) + flags |= IWL_UHB_CHAN_CFG_FLAG_UNSOLICITED_PROBE_RES; + + if ((allow_passive && force_passive) || + (!(bssid_bitmap | s_ssid_bitmap) && + !cfg80211_channel_is_psc(params->channels[i]))) + flags |= IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE; + else + flags |= bssid_bitmap | (s_ssid_bitmap << 16); + + cfg->flags |= cpu_to_le32(flags); + cfg->v5.psd_20 = psd_20; + + ch_cnt++; + } + + if (params->n_channels > ch_cnt) + IWL_DEBUG_SCAN(mld, + "6GHz: reducing number channels: (%u->%u)\n", + params->n_channels, ch_cnt); + + return ch_cnt; +} + +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, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params; + struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params; + + chan_p->flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, + scan_status); + chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params, + params->n_channels, + probe_p, chan_p, + vif->type); + if (!chan_p->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; + + return 0; +} + +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, + 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 ieee80211_supported_band *sband = + &mld->nvm_data->bands[NL80211_BAND_6GHZ]; + + cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; + cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS; + + if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) + cp->n_aps_override[0] = IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE; + + if (params->scan_6ghz) + return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params, + vif, scan_p, + scan_status); + + /* 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, + params->n_channels, channel_cfg_flags, + vif->type); + + if (!params->enable_6ghz_passive) + return 0; + + /* fill 6 GHz passive scan cfg */ + for (int i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *channel = + &sband->channels[i]; + struct iwl_scan_channel_cfg_umac *cfg = + &cp->channel_config[cp->count]; + + if (!cfg80211_channel_is_psc(channel)) + continue; + + cfg->channel_num = channel->hw_value; + cfg->v5.iter_count = 1; + cfg->v5.iter_interval = 0; + cfg->v5.psd_20 = + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; + cfg->flags = cpu_to_le32(PHY_BAND_6 << + IWL_CHAN_CFG_FLAGS_BAND_POS); + cp->count++; + } + + return 0; +} + +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) +{ + 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; + + memset(mld->scan.cmd, 0, mld->scan.cmd_size); + + /* find a free UID entry */ + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE); + if (uid < 0) + return uid; + + cmd->uid = cpu_to_le32(uid); + cmd->ooc_priority = + cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status)); + + iwl_mld_scan_cmd_set_gen_params(mld, params, vif, + &scan_p->general_params, scan_status); + + ret = iwl_mld_scan_cmd_set_sched_params(params, + scan_p->periodic_params.schedule, + &scan_p->periodic_params.delay); + if (ret) + return ret; + + iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params, + &bitmap_ssid); + + ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p, + low_latency, scan_status, + bitmap_ssid); + if (ret) + return ret; + + return uid; +} + +static bool +iwl_mld_scan_pass_all(struct iwl_mld *mld, + struct cfg80211_sched_scan_request *req) +{ + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mld, + "Sending scheduled scan with filtering, n_match_sets %d\n", + req->n_match_sets); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + return false; + } + + IWL_DEBUG_SCAN(mld, "Sending Scheduled scan without filtering\n"); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; + + return true; +} + +static int +iwl_mld_config_sched_scan_profiles(struct iwl_mld *mld, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_host_cmd hcmd = { + .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + struct iwl_scan_offload_profile *profile; + struct iwl_scan_offload_profile_cfg_data *cfg_data; + struct iwl_scan_offload_profile_cfg *profile_cfg; + struct iwl_scan_offload_blocklist *blocklist; + u32 blocklist_size = IWL_SCAN_MAX_BLACKLIST_LEN * sizeof(*blocklist); + u32 cmd_size = blocklist_size + sizeof(*profile_cfg); + u8 *cmd; + int ret; + + if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES_V2)) + return -EIO; + + cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + hcmd.data[0] = cmd; + hcmd.len[0] = cmd_size; + + blocklist = (struct iwl_scan_offload_blocklist *)cmd; + profile_cfg = (struct iwl_scan_offload_profile_cfg *)(cmd + blocklist_size); + + /* No blocklist configuration */ + cfg_data = &profile_cfg->data; + cfg_data->num_profiles = req->n_match_sets; + cfg_data->active_clients = SCAN_CLIENT_SCHED_SCAN; + cfg_data->pass_match = SCAN_CLIENT_SCHED_SCAN; + cfg_data->match_notify = SCAN_CLIENT_SCHED_SCAN; + + if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) + cfg_data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; + + for (int i = 0; i < req->n_match_sets; i++) { + profile = &profile_cfg->profiles[i]; + + /* Support any cipher and auth algorithm */ + profile->unicast_cipher = 0xff; + profile->auth_alg = IWL_AUTH_ALGO_UNSUPPORTED | + IWL_AUTH_ALGO_NONE | IWL_AUTH_ALGO_PSK | + IWL_AUTH_ALGO_8021X | IWL_AUTH_ALGO_SAE | + IWL_AUTH_ALGO_8021X_SHA384 | IWL_AUTH_ALGO_OWE; + profile->network_type = IWL_NETWORK_TYPE_ANY; + profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; + profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; + profile->ssid_index = i; + } + + IWL_DEBUG_SCAN(mld, + "Sending scheduled scan profile config (n_match_sets=%u)\n", + req->n_match_sets); + + ret = iwl_mld_send_cmd(mld, &hcmd); + + kfree(cmd); + + return ret; +} + +static int +iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params *params, + bool *non_psc_included) +{ + int i, j; + + *non_psc_included = false; + /* for 6 GHZ band only PSC channels need to be added */ + for (i = 0; i < params->n_channels; i++) { + struct ieee80211_channel *channel = params->channels[i]; + + if (channel->band == NL80211_BAND_6GHZ && + !cfg80211_channel_is_psc(channel)) { + *non_psc_included = true; + break; + } + } + + if (!*non_psc_included) + return 0; + + params->channels = + kmemdup(params->channels, + sizeof(params->channels[0]) * params->n_channels, + GFP_KERNEL); + if (!params->channels) + return -ENOMEM; + + for (i = j = 0; i < params->n_channels; i++) { + if (params->channels[i]->band == NL80211_BAND_6GHZ && + !cfg80211_channel_is_psc(params->channels[i])) + continue; + params->channels[j++] = params->channels[i]; + } + + params->n_channels = j; + + return 0; +} + +static void +iwl_mld_scan_6ghz_passive_scan(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband = + &mld->nvm_data->bands[NL80211_BAND_6GHZ]; + u32 n_disabled, i; + + params->enable_6ghz_passive = false; + + /* 6 GHz passive scan may be enabled in the first 2.4 GHz/5 GHz scan + * phase to discover geo location if no AP's are found. Skip it when + * we're in the 6 GHz scan phase. + */ + if (params->scan_6ghz) + return; + + /* 6 GHz passive scan allowed only on station interface */ + if (vif->type != NL80211_IFTYPE_STATION) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: not station interface\n"); + return; + } + + /* 6 GHz passive scan is allowed in a defined time interval following + * HW reset or resume flow, or while not associated and a large + * interval has passed since the last 6 GHz passive scan. + */ + if ((vif->cfg.assoc || + time_after(mld->scan.last_6ghz_passive_jiffies + + (IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT * HZ), jiffies)) && + (time_before(mld->scan.last_start_time_jiffies + + (IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT * HZ), + jiffies))) { + IWL_DEBUG_SCAN(mld, "6GHz passive scan: %s\n", + vif->cfg.assoc ? "associated" : + "timeout did not expire"); + return; + } + + /* not enough channels in the regular scan request */ + if (params->n_channels < IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: not enough channels %d\n", + params->n_channels); + return; + } + + for (i = 0; i < params->n_ssids; i++) { + if (!params->ssids[i].ssid_len) + break; + } + + /* not a wildcard scan, so cannot enable passive 6 GHz scan */ + if (i == params->n_ssids) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: no wildcard SSID\n"); + return; + } + + if (!sband || !sband->n_channels) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: no 6GHz channels\n"); + return; + } + + for (i = 0, n_disabled = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED)) + n_disabled++; + } + + /* Not all the 6 GHz channels are disabled, so no need for 6 GHz + * passive scan + */ + if (n_disabled != sband->n_channels) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: 6GHz channels enabled\n"); + return; + } + + /* all conditions to enable 6 GHz passive scan are satisfied */ + IWL_DEBUG_SCAN(mld, "6GHz passive scan: can be enabled\n"); + params->enable_6ghz_passive = true; +} + +static void +iwl_mld_scan_set_link_id(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + s8 tsf_report_link_id, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link; + + if (tsf_report_link_id < 0) { + if (vif->active_links) + tsf_report_link_id = __ffs(vif->active_links); + else + tsf_report_link_id = 0; + } + + link = iwl_mld_link_dereference_check(mld_vif, tsf_report_link_id); + if (!WARN_ON(!link)) { + params->fw_link_id = link->fw_id; + /* we to store fw_link_id only for regular scan, + * and use it in scan complete notif + */ + if (scan_status == IWL_MLD_SCAN_REGULAR) + mld->scan.fw_link_id = link->fw_id; + } else { + mld->scan.fw_link_id = IWL_MLD_INVALID_FW_ID; + params->fw_link_id = IWL_MLD_INVALID_FW_ID; + } +} + +static int +_iwl_mld_single_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), + .len = { mld->scan.cmd_size, }, + .data = { mld->scan.cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mld_scan_iter_data scan_iter_data = { + .current_vif = vif, + }; + struct cfg80211_sched_scan_plan scan_plan = {.iterations = 1}; + struct iwl_mld_scan_params params = {}; + int ret, uid; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mld->scan.cmd)) + return -ENOMEM; + + if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, req->n_channels)) + return -ENOBUFS; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_iterator, + &scan_iter_data); + + params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); + params.n_ssids = req->n_ssids; + params.flags = req->flags; + params.n_channels = req->n_channels; + params.delay = 0; + params.ssids = req->ssids; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = req->no_cck; + params.pass_all = true; + params.n_match_sets = 0; + params.match_sets = NULL; + params.scan_plans = &scan_plan; + params.n_scan_plans = 1; + + params.n_6ghz_params = req->n_6ghz_params; + params.scan_6ghz_params = req->scan_6ghz_params; + params.scan_6ghz = req->scan_6ghz; + + ether_addr_copy(params.bssid, req->bssid); + /* TODO: CDB - per-band flag */ + params.respect_p2p_go = + iwl_mld_get_respect_p2p_go(mld, vif, + scan_iter_data.global_low_latency); + + if (req->duration) + params.iter_notif = true; + + iwl_mld_scan_set_link_id(mld, vif, ¶ms, req->tsf_report_link_id, + scan_status); + + iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); + + iwl_mld_scan_6ghz_passive_scan(mld, ¶ms, vif); + + uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, scan_status, + scan_iter_data.global_low_latency); + if (uid < 0) + return uid; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) { + IWL_ERR(mld, "Scan failed! ret %d\n", ret); + return ret; + } + + IWL_DEBUG_SCAN(mld, "Scan request send success: status=%u, uid=%u\n", + scan_status, uid); + + mld->scan.uid_status[uid] = scan_status; + mld->scan.status |= scan_status; + + if (params.enable_6ghz_passive) + mld->scan.last_6ghz_passive_jiffies = jiffies; + + return 0; +} + +static int +iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status) +{ + struct iwl_umac_scan_abort abort_cmd = { + .uid = cpu_to_le32(uid), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_ABORT_UMAC), + .flags = CMD_WANT_SKB, + .data = { &abort_cmd }, + .len[0] = sizeof(abort_cmd), + }; + struct iwl_rx_packet *pkt; + struct iwl_cmd_response *resp; + u32 resp_len; + int ret; + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + return ret; + + pkt = cmd.resp_pkt; + + resp_len = iwl_rx_packet_payload_len(pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp), + "Scan Abort: unexpected response length %d\n", + resp_len)) { + ret = -EIO; + goto out; + } + + resp = (void *)pkt->data; + *status = le32_to_cpu(resp->status); + +out: + iwl_free_resp(&cmd); + return ret; +} + +static int +iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait) +{ + enum iwl_umac_scan_abort_status status; + int ret; + + *wait = true; + + IWL_DEBUG_SCAN(mld, "Sending scan abort, uid %u\n", uid); + + ret = iwl_mld_scan_send_abort_cmd_status(mld, uid, &status); + + IWL_DEBUG_SCAN(mld, "Scan abort: ret=%d status=%u\n", ret, status); + + /* We don't need to wait to scan complete in the following cases: + * 1. Driver failed to send the scan abort cmd. + * 2. The FW is no longer familiar with the scan that needs to be + * stopped. It is expected that the scan complete notification was + * already received but not yet processed. + * + * In both cases the flow should continue similar to the case that the + * scan was really aborted. + */ + if (ret || status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND) + *wait = false; + + return ret; +} + +static int +iwl_mld_scan_stop_wait(struct iwl_mld *mld, int type, int uid) +{ + struct iwl_notification_wait wait_scan_done; + static const u16 scan_comp_notif[] = { SCAN_COMPLETE_UMAC }; + bool wait = true; + int ret; + + iwl_init_notification_wait(&mld->notif_wait, &wait_scan_done, + scan_comp_notif, + ARRAY_SIZE(scan_comp_notif), + NULL, NULL); + + IWL_DEBUG_SCAN(mld, "Preparing to stop scan, type=%x\n", type); + + ret = iwl_mld_scan_abort(mld, type, uid, &wait); + if (ret) { + IWL_DEBUG_SCAN(mld, "couldn't stop scan type=%d\n", type); + goto return_no_wait; + } + + if (!wait) { + IWL_DEBUG_SCAN(mld, "no need to wait for scan type=%d\n", type); + goto return_no_wait; + } + + return iwl_wait_notification(&mld->notif_wait, &wait_scan_done, HZ); + +return_no_wait: + iwl_remove_notification(&mld->notif_wait, &wait_scan_done); + return ret; +} + +int iwl_mld_sched_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), + .len = { mld->scan.cmd_size, }, + .data = { mld->scan.cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mld_scan_params params = {}; + struct iwl_mld_scan_iter_data scan_iter_data = { + .current_vif = vif, + }; + bool non_psc_included = false; + int ret, uid; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mld->scan.cmd)) + return -ENOMEM; + + /* FW supports only a single periodic scan */ + if (mld->scan.status & (IWL_MLD_SCAN_SCHED | IWL_MLD_SCAN_NETDETECT)) + return -EBUSY; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_iterator, + &scan_iter_data); + + params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); + params.flags = req->flags; + params.n_ssids = req->n_ssids; + params.ssids = req->ssids; + params.n_channels = req->n_channels; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = false; + params.pass_all = iwl_mld_scan_pass_all(mld, req); + params.n_match_sets = req->n_match_sets; + params.match_sets = req->match_sets; + params.n_scan_plans = req->n_scan_plans; + params.scan_plans = req->scan_plans; + /* TODO: CDB - per-band flag */ + params.respect_p2p_go = + iwl_mld_get_respect_p2p_go(mld, vif, + scan_iter_data.global_low_latency); + + /* UMAC scan supports up to 16-bit delays, trim it down to 16-bits */ + params.delay = req->delay > U16_MAX ? U16_MAX : req->delay; + + eth_broadcast_addr(params.bssid); + + ret = iwl_mld_config_sched_scan_profiles(mld, req); + if (ret) + return ret; + + iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); + + ret = iwl_mld_sched_scan_handle_non_psc_channels(¶ms, + &non_psc_included); + if (ret) + goto out; + + if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, params.n_channels)) { + ret = -ENOBUFS; + goto out; + } + + uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, type, + scan_iter_data.global_low_latency); + if (uid < 0) { + ret = uid; + goto out; + } + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (!ret) { + IWL_DEBUG_SCAN(mld, + "Sched scan request send success: type=%u, uid=%u\n", + type, uid); + mld->scan.uid_status[uid] = type; + mld->scan.status |= type; + } else { + IWL_ERR(mld, "Sched scan failed! ret %d\n", ret); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } + +out: + if (non_psc_included) + kfree(params.channels); + return ret; +} + +int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify) +{ + int uid, ret; + + IWL_DEBUG_SCAN(mld, + "Request to stop scan: type=0x%x, status=0x%x\n", + type, mld->scan.status); + + if (!(mld->scan.status & type)) + return 0; + + uid = iwl_mld_scan_uid_by_status(mld, type); + /* must be valid, we just checked it's running */ + if (WARN_ON_ONCE(uid < 0)) + return uid; + + ret = iwl_mld_scan_stop_wait(mld, type, uid); + if (ret) + IWL_DEBUG_SCAN(mld, "Failed to stop scan\n"); + + /* Clear the scan status so the next scan requests will + * succeed and mark the scan as stopping, so that the Rx + * handler doesn't do anything, as the scan was stopped from + * above. Also remove the handler to not notify mac80211 + * erroneously after a new scan starts, for example. + */ + mld->scan.status &= ~type; + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_SCAN, + uid); + + if (type == IWL_MLD_SCAN_REGULAR) { + if (notify) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mld->hw, &info); + } + } else if (notify) { + ieee80211_sched_scan_stopped(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } + + return ret; +} + +int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + return _iwl_mld_single_scan_start(mld, vif, req, ies, + IWL_MLD_SCAN_REGULAR); +} + +static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_channel **channels, + size_t n_channels) +{ + struct cfg80211_scan_request *req __free(kfree) = NULL; + struct ieee80211_scan_ies ies = {}; + size_t size; + int ret; + + IWL_DEBUG_SCAN(mld, "Starting Internal MLO scan: n_channels=%zu\n", + n_channels); + + size = struct_size(req, channels, n_channels); + req = kzalloc(size, GFP_KERNEL); + if (!req) + return; + + /* set the requested channels */ + for (int i = 0; i < n_channels; i++) + req->channels[i] = channels[i]; + + req->n_channels = n_channels; + + /* set the rates */ + for (int i = 0; i < NUM_NL80211_BANDS; i++) + if (mld->wiphy->bands[i]) + req->rates[i] = + (1 << mld->wiphy->bands[i]->n_bitrates) - 1; + + req->wdev = ieee80211_vif_to_wdev(vif); + req->wiphy = mld->wiphy; + req->scan_start = jiffies; + req->tsf_report_link_id = -1; + + ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, + IWL_MLD_SCAN_INT_MLO); + + IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); +} + +void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS]; + unsigned long usable_links = ieee80211_vif_usable_links(vif); + size_t n_channels = 0; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) || + hweight16(vif->valid_links) == 1) + return; + + if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan is already running\n"); + return; + } + + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_check(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + channels[n_channels++] = link_conf->chanreq.oper.chan; + } + + if (!n_channels) + return; + + iwl_mld_int_mlo_scan_start(mld, vif, channels, n_channels); +} + +void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; + u32 uid = __le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) + mld->scan.start_tsf = le64_to_cpu(notif->start_tsf); + + IWL_DEBUG_SCAN(mld, + "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n", + notif->status, notif->scanned_channels); + + if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_FOUND) { + IWL_DEBUG_SCAN(mld, "Pass all scheduled scan results found\n"); + ieee80211_sched_scan_results(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; + } + + IWL_DEBUG_SCAN(mld, + "UMAC Scan iteration complete: scan started at %llu (TSF)\n", + le64_to_cpu(notif->start_tsf)); +} + +void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + IWL_DEBUG_SCAN(mld, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mld->hw); +} + +void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_complete *notif = (void *)pkt->data; + bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); + u32 uid = __le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + IWL_DEBUG_SCAN(mld, + "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n", + uid, mld->scan.uid_status[uid], + notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted", + iwl_mld_scan_ebs_status_str(notif->ebs_status)); + IWL_DEBUG_SCAN(mld, "Scan completed: scan_status=0x%x\n", + mld->scan.status); + IWL_DEBUG_SCAN(mld, + "Scan completed: line=%u, iter=%u, elapsed time=%u\n", + notif->last_schedule, notif->last_iter, + __le32_to_cpu(notif->time_from_last_iter)); + + if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status), + "FW reports scan UID %d we didn't trigger\n", uid)) + return; + + /* if the scan is already stopping, we don't need to notify mac80211 */ + if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) { + struct cfg80211_scan_info info = { + .aborted = aborted, + .scan_start_tsf = mld->scan.start_tsf, + }; + int fw_link_id = mld->scan.fw_link_id; + 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]); + + /* It is possible that by the time the scan is complete the + * link was already removed and is not valid. + */ + if (link_conf) + ether_addr_copy(info.tsf_bssid, link_conf->bssid); + else + IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n"); + + ieee80211_scan_completed(mld->hw, &info); + } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) { + ieee80211_sched_scan_stopped(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n"); + mld->scan.last_mlo_scan_jiffies = jiffies; + + /* + * We limit link selection to internal MLO scans as otherwise + * we do not know whether all channels were covered. + */ + iwl_mld_select_links(mld); + } + + mld->scan.status &= ~mld->scan.uid_status[uid]; + + IWL_DEBUG_SCAN(mld, "Scan completed: after update: scan_status=0x%x\n", + mld->scan.status); + + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + + if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && + notif->ebs_status != IWL_SCAN_EBS_INACTIVE) + mld->scan.last_ebs_failed = true; +} + +/* This function is used in nic restart flow, to inform mac80211 about scans + * that were aborted by restart flow or by an assert. + */ +void iwl_mld_report_scan_aborted(struct iwl_mld *mld) +{ + int uid; + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_REGULAR); + if (uid >= 0) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mld->hw, &info); + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + } + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_SCHED); + if (uid >= 0) { + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + + /* sched scan will be restarted by mac80211 in reconfig. + * report to mac80211 that sched scan stopped only if we won't + * restart the firmware. + */ + if (!iwlwifi_mod_params.fw_restart) + ieee80211_sched_scan_stopped(mld->hw); + } + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_INT_MLO); + if (uid >= 0) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan aborted\n"); + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + } + + BUILD_BUG_ON(IWL_MLD_SCAN_NONE != 0); + memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); +} + +int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) +{ + u8 scan_cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, SCAN_REQ_UMAC, + IWL_FW_CMD_VER_UNKNOWN); + size_t scan_cmd_size; + + if (scan_cmd_ver == 17) { + scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17); + } else { + IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver); + return -EINVAL; + } + + mld->scan.cmd = kmalloc(scan_cmd_size, GFP_KERNEL); + if (!mld->scan.cmd) + return -ENOMEM; + + mld->scan.cmd_size = scan_cmd_size; + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h new file mode 100644 index 000000000000..cb589e002319 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_scan_h__ +#define __iwl_mld_scan_h__ + +int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld); + +int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies); + +void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif); + +void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify); + +int iwl_mld_sched_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type); + +void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#define WFA_TPC_IE_LEN 9 + +static inline int iwl_mld_scan_max_template_size(void) +{ +#define MAC_HDR_LEN 24 +#define DS_IE_LEN 3 +#define SSID_IE_LEN 2 + +/* driver create the 802.11 header, WFA TPC IE, DS parameter and SSID IE */ +#define DRIVER_TOTAL_IES_LEN \ + (MAC_HDR_LEN + WFA_TPC_IE_LEN + DS_IE_LEN + SSID_IE_LEN) + + BUILD_BUG_ON(SCAN_OFFLOAD_PROBE_REQ_SIZE < DRIVER_TOTAL_IES_LEN); + + return SCAN_OFFLOAD_PROBE_REQ_SIZE - DRIVER_TOTAL_IES_LEN; +} + +void iwl_mld_report_scan_aborted(struct iwl_mld *mld); + +enum iwl_mld_scan_status { + IWL_MLD_SCAN_NONE = 0, + IWL_MLD_SCAN_REGULAR = BIT(0), + IWL_MLD_SCAN_SCHED = BIT(1), + IWL_MLD_SCAN_NETDETECT = BIT(2), + IWL_MLD_SCAN_INT_MLO = BIT(3), +}; + +/* enum iwl_mld_pass_all_sched_results_states - Defines the states for + * handling/passing scheduled scan results to mac80211 + * @SCHED_SCAN_PASS_ALL_STATE_DISABLED: Don't pass all scan results, only when + * a match found. + * @SCHED_SCAN_PASS_ALL_STATE_ENABLED: Pass all scan results is enabled + * (no filtering). + * @SCHED_SCAN_PASS_ALL_STATE_FOUND: A scan result is found, pass it on the + * next scan iteration complete notification. + */ +enum iwl_mld_pass_all_sched_results_states { + SCHED_SCAN_PASS_ALL_STATE_DISABLED, + SCHED_SCAN_PASS_ALL_STATE_ENABLED, + SCHED_SCAN_PASS_ALL_STATE_FOUND, +}; + +/** + * enum iwl_mld_traffic_load - Levels of traffic load + * + * @IWL_MLD_TRAFFIC_LOW: low traffic load + * @IWL_MLD_TRAFFIC_MEDIUM: medium traffic load + * @IWL_MLD_TRAFFIC_HIGH: high traffic load + */ +enum iwl_mld_traffic_load { + IWL_MLD_TRAFFIC_LOW, + IWL_MLD_TRAFFIC_MEDIUM, + IWL_MLD_TRAFFIC_HIGH, +}; + +/** + * struct iwl_mld_scan - Scan data + * @status: scan status, a combination of %enum iwl_mld_scan_status, + * reflects the %scan.uid_status array. + * @uid_status: array to track the scan status per uid. + * @start_tsf: start time of last scan in TSF of the link that requested + * the scan. + * @last_ebs_failed: true if the last EBS (Energy Based Scan) failed. + * @pass_all_sched_res: see %enum iwl_mld_pass_all_sched_results_states. + * @fw_link_id: the current (regular) scan fw link id, used by scan + * complete notif. + * @traffic_load: traffic load related data + * @traffic_load.last_stats_ts_usec: The timestamp of the last statistics + * notification, used to calculate the elapsed time between two + * notifications and determine the traffic load + * @traffic_load.status: The current traffic load status, see + * &enum iwl_mld_traffic_load + * @cmd_size: size of %cmd. + * @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. + * @last_start_time_jiffies: stores the last start time in jiffies + * (interface up/reset/resume). + * @last_mlo_scan_jiffies: end time of the last MLO scan in jiffies. + */ +struct iwl_mld_scan { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + unsigned int status; + u32 uid_status[IWL_MAX_UMAC_SCANS]; + u64 start_tsf; + bool last_ebs_failed; + enum iwl_mld_pass_all_sched_results_states pass_all_sched_res; + u8 fw_link_id; + struct { + u32 last_stats_ts_usec; + enum iwl_mld_traffic_load status; + } traffic_load; + ); + /* And here fields that survive a fw restart */ + size_t cmd_size; + void *cmd; + unsigned long last_6ghz_passive_jiffies; + unsigned long last_start_time_jiffies; + unsigned long last_mlo_scan_jiffies; +}; + +#endif /* __iwl_mld_scan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c new file mode 100644 index 000000000000..dbb5615dc3f6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include "session-protect.h" +#include "fw/api/time-event.h" +#include "fw/api/context.h" +#include "iface.h" +#include + +void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_session_prot_notif *notif = (void *)pkt->data; + int fw_link_id = le32_to_cpu(notif->mac_link_id); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_session_protect *session_protect; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + mld_vif = iwl_mld_vif_from_mac80211(vif); + session_protect = &mld_vif->session_protect; + + if (!le32_to_cpu(notif->status)) { + memset(session_protect, 0, sizeof(*session_protect)); + } else if (le32_to_cpu(notif->start)) { + /* End_jiffies indicates an active session */ + session_protect->session_requested = false; + session_protect->end_jiffies = + TU_TO_EXP_TIME(session_protect->duration); + /* !session_protect->end_jiffies means inactive session */ + if (!session_protect->end_jiffies) + session_protect->end_jiffies = 1; + } else { + memset(session_protect, 0, sizeof(*session_protect)); + } +} + +static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = + iwl_mld_link_dereference_check(mld_vif, link_id); + struct iwl_mld_session_protect *session_protect = + &mld_vif->session_protect; + struct iwl_session_prot_cmd cmd = { + .id_and_color = cpu_to_le32(link->fw_id), + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), + .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + WARN(hweight16(vif->active_links) > 1, + "Session protection isn't allowed with more than one active link"); + + if (session_protect->end_jiffies && + time_after(session_protect->end_jiffies, + TU_TO_EXP_TIME(min_duration))) { + IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n", + jiffies_to_msecs(session_protect->end_jiffies - + jiffies)); + return -EALREADY; + } + + IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n", + le32_to_cpu(cmd.duration_tu)); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), &cmd); + + if (ret) + return ret; + + /* end_jiffies will be updated when handling session_prot_notif */ + session_protect->end_jiffies = 0; + session_protect->duration = duration; + session_protect->session_requested = true; + + return 0; +} + +void iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id) +{ + int ret; + + ret = _iwl_mld_schedule_session_protection(mld, vif, duration, + min_duration, link_id); + if (ret && ret != -EALREADY) + IWL_ERR(mld, + "Couldn't send the SESSION_PROTECTION_CMD (%d)\n", + ret); +} + +struct iwl_mld_session_start_data { + struct iwl_mld *mld; + struct ieee80211_bss_conf *link_conf; + bool success; +}; + +static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *_data) +{ + struct iwl_session_prot_notif *notif = (void *)pkt->data; + unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + struct iwl_mld_session_start_data *data = _data; + struct ieee80211_bss_conf *link_conf; + struct iwl_mld *mld = data->mld; + int fw_link_id; + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif), + "short session prot notif (%d)\n", + pkt_len)) + return false; + + fw_link_id = le32_to_cpu(notif->mac_link_id); + link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + + if (link_conf != data->link_conf) + return false; + + if (!le32_to_cpu(notif->status)) + return true; + + if (notif->start) { + data->success = true; + return true; + } + + return false; +} + +int iwl_mld_start_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id, unsigned long timeout) +{ + static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF }; + struct iwl_notification_wait start_wait; + struct iwl_mld_session_start_data data = { + .mld = mld, + .link_conf = wiphy_dereference(mld->wiphy, + vif->link_conf[link_id]), + }; + int ret; + + if (WARN_ON(!data.link_conf)) + return -EINVAL; + + iwl_init_notification_wait(&mld->notif_wait, &start_wait, + start_notif, ARRAY_SIZE(start_notif), + iwl_mld_session_start_fn, &data); + + ret = _iwl_mld_schedule_session_protection(mld, vif, duration, + min_duration, link_id); + + if (ret) { + iwl_remove_notification(&mld->notif_wait, &start_wait); + return ret == -EALREADY ? 0 : ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout); + if (ret) + return ret; + return data.success ? 0 : -EIO; +} + +int iwl_mld_cancel_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = + iwl_mld_link_dereference_check(mld_vif, link_id); + struct iwl_mld_session_protect *session_protect = + &mld_vif->session_protect; + struct iwl_session_prot_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* If there isn't an active session or a requested one for this + * link do nothing + */ + if (!session_protect->session_requested && + !session_protect->end_jiffies) + return 0; + + if (WARN_ON(!link)) + return -EINVAL; + + cmd.id_and_color = cpu_to_le32(link->fw_id); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), &cmd); + if (ret) { + IWL_ERR(mld, + "Couldn't send the SESSION_PROTECTION_CMD\n"); + return ret; + } + + memset(session_protect, 0, sizeof(*session_protect)); + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h new file mode 100644 index 000000000000..642bec8451a1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __session_protect_h__ +#define __session_protect_h__ + +#include "mld.h" +#include "hcmd.h" +#include +#include "fw/api/mac-cfg.h" + +/** + * DOC: session protection + * + * Session protection is an API from the firmware that allows the driver to + * request time on medium. This is needed before the association when we need + * to be on medium for the association frame exchange. Once we configure the + * firmware as 'associated', the firmware will allocate time on medium without + * needed a session protection. + * + * TDLS discover uses this API as well even after association to ensure that + * other activities internal to the firmware will not interrupt our presence + * on medium. + */ + +/** + * struct iwl_mld_session_protect - session protection parameters + * @end_jiffies: expected end_jiffies of current session protection. + * 0 if not active + * @duration: the duration in tu of current session + * @session_requested: A session protection command was sent and wasn't yet + * answered + */ +struct iwl_mld_session_protect { + unsigned long end_jiffies; + u32 duration; + bool session_requested; +}; + +#define IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS 900 +#define IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS 400 + +/** + * iwl_mld_handle_session_prot_notif - handles %SESSION_PROTECTION_NOTIF + * @mld: the mld component + * @pkt: the RX packet containing the notification + */ +void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +/** + * iwl_mld_schedule_session_protection - schedule a session protection + * @mld: the mld component + * @vif: the virtual interface for which the protection issued + * @duration: the requested duration of the protection + * @min_duration: the minimum duration of the protection + * @link_id: The link to schedule a session protection for + */ +void iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id); + +/** + * iwl_mld_start_session_protection - start a session protection + * @mld: the mld component + * @vif: the virtual interface for which the protection issued + * @duration: the requested duration of the protection + * @min_duration: the minimum duration of the protection + * @link_id: The link to schedule a session protection for + * @timeout: timeout for waiting + * + * This schedules the session protection, and waits for it to start + * (with timeout) + * + * Returns: 0 if successful, error code otherwise + */ +int iwl_mld_start_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id, unsigned long timeout); + +/** + * iwl_mld_cancel_session_protection - cancel the session protection. + * @mld: the mld component + * @vif: the virtual interface for which the session is issued + * @link_id: cancel the session protection for given link + * + * This functions cancels the session protection which is an act of good + * citizenship. If it is not needed any more it should be canceled because + * the other mac contexts wait for the medium during that time. + * + * Returns: 0 if successful, error code otherwise + * + */ +int iwl_mld_cancel_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + int link_id); + +#endif /* __session_protect_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c new file mode 100644 index 000000000000..f266a81dd29b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -0,0 +1,1265 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include +#include + +#include "sta.h" +#include "hcmd.h" +#include "iface.h" +#include "mlo.h" +#include "key.h" +#include "agg.h" +#include "tlc.h" +#include "fw/api/sta.h" +#include "fw/api/mac.h" +#include "fw/api/rx.h" + +int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_link_sta *mld_link_sta; + + /* This function should only be used with the wiphy lock held, + * In other cases, it is not guaranteed that the link_sta will exist + * in the driver too, and it is checked here. + */ + lockdep_assert_wiphy(mld->wiphy); + + /* This is not meant to be called with a NULL pointer */ + if (WARN_ON(!link_sta)) + return -ENOENT; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (!mld_link_sta) { + WARN_ON(!iwl_mld_error_before_recovery(mld)); + return -ENOENT; + } + + return mld_link_sta->fw_id; +} + +static void +iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, + struct ieee80211_bss_conf *link, + __le32 *tx_ampdu_max_size, + __le32 *tx_ampdu_spacing) +{ + u32 agg_size = 0, mpdu_dens = 0; + + if (WARN_ON(!link_sta || !link)) + return; + + /* Note that we always use only legacy & highest supported PPDUs, so + * of Draft P802.11be D.30 Table 10-12a--Fields used for calculating + * the maximum A-MPDU size of various PPDU types in different bands, + * we only need to worry about the highest supported PPDU type here. + */ + + if (link_sta->ht_cap.ht_supported) { + agg_size = link_sta->ht_cap.ampdu_factor; + mpdu_dens = link_sta->ht_cap.ampdu_density; + } + + if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { + /* overwrite HT values on 6 GHz */ + mpdu_dens = + le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + agg_size = + le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } else if (link_sta->vht_cap.vht_supported) { + /* if VHT supported overwrite HT value */ + agg_size = + u32_get_bits(link_sta->vht_cap.cap, + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); + } + + /* D6.0 10.12.2 A-MPDU length limit rules + * A STA indicates the maximum length of the A-MPDU preEOF padding + * that it can receive in an HE PPDU in the Maximum A-MPDU Length + * Exponent field in its HT Capabilities, VHT Capabilities, + * and HE 6 GHz Band Capabilities elements (if present) and the + * Maximum AMPDU Length Exponent Extension field in its HE + * Capabilities element + */ + if (link_sta->he_cap.has_he) + agg_size += + u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3], + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); + + if (link_sta->eht_cap.has_eht) + agg_size += + u8_get_bits(link_sta->eht_cap.eht_cap_elem.mac_cap_info[1], + IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK); + + /* Limit to max A-MPDU supported by FW */ + agg_size = min_t(u32, agg_size, + STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT); + + *tx_ampdu_max_size = cpu_to_le32(agg_size); + *tx_ampdu_spacing = cpu_to_le32(mpdu_dens); +} + +static u8 iwl_mld_get_uapsd_acs(struct ieee80211_sta *sta) +{ + u8 uapsd_acs = 0; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd_acs |= BIT(AC_BK); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd_acs |= BIT(AC_BE); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd_acs |= BIT(AC_VI); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd_acs |= BIT(AC_VO); + + return uapsd_acs | uapsd_acs << 4; +} + +static u8 iwl_mld_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit) +{ + u8 byte_num = ppe_pos_bit / 8; + u8 bit_num = ppe_pos_bit % 8; + u8 residue_bits; + u8 res; + + if (bit_num <= 5) + return (ppe[byte_num] >> bit_num) & + (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1); + + /* If bit_num > 5, we have to combine bits with next byte. + * Calculate how many bits we need to take from current byte (called + * here "residue_bits"), and add them to bits from next byte. + */ + + residue_bits = 8 - bit_num; + + res = (ppe[byte_num + 1] & + (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) << + residue_bits; + res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1); + + return res; +} + +static void iwl_mld_parse_ppe(struct iwl_mld *mld, + struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss, + u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit, + bool inheritance) +{ + /* FW currently supports only nss == MAX_HE_SUPP_NSS + * + * If nss > MAX: we can ignore values we don't support + * If nss < MAX: we can set zeros in other streams + */ + if (nss > MAX_HE_SUPP_NSS) { + IWL_DEBUG_INFO(mld, "Got NSS = %d - trimming to %d\n", nss, + MAX_HE_SUPP_NSS); + nss = MAX_HE_SUPP_NSS; + } + + for (int i = 0; i < nss; i++) { + u8 ru_index_tmp = ru_index_bitmap << 1; + u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE; + + for (u8 bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + ru_index_tmp >>= 1; + + /* According to the 11be spec, if for a specific BW the PPE Thresholds + * isn't present - it should inherit the thresholds from the last + * BW for which we had PPE Thresholds. In 11ax though, we don't have + * this inheritance - continue in this case + */ + if (!(ru_index_tmp & 1)) { + if (inheritance) + goto set_thresholds; + else + continue; + } + + high_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + low_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + +set_thresholds: + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } +} + +static void iwl_mld_set_pkt_ext_from_he_ppe(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + struct iwl_he_pkt_ext_v2 *pkt_ext, + bool inheritance) +{ + u8 nss = (link_sta->he_cap.ppe_thres[0] & + IEEE80211_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &link_sta->he_cap.ppe_thres[0]; + u8 ru_index_bitmap = + u8_get_bits(*ppe, + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit, + inheritance); +} + +static int +iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + int low_th = -1; + int high_th = -1; + + /* all the macros are the same for EHT and HE */ + switch (nominal_padding) { + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US: + low_th = IWL_HE_PKT_EXT_BPSK; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_BPSK; + break; + } + + if (low_th < 0 || high_th < 0) + return -EINVAL; + + /* Set the PPE thresholds accordingly */ + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (u8 bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } + + return 0; +} + +static void iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (u8 bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + if (nominal_padding > + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[1] = IWL_HE_PKT_EXT_4096QAM; + else if (nominal_padding == + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[0] == IWL_HE_PKT_EXT_NONE && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[0] = IWL_HE_PKT_EXT_4096QAM; + } + } +} + +static void iwl_mld_fill_pkt_ext(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + struct iwl_he_pkt_ext_v2 *pkt_ext) +{ + if (WARN_ON(!link_sta)) + return; + + /* Initialize the PPE thresholds to "None" (7), as described in Table + * 9-262ac of 80211.ax/D3.0. + */ + memset(pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(*pkt_ext)); + + if (link_sta->eht_cap.has_eht) { + u8 nominal_padding = + u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5], + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); + + /* If PPE Thresholds exists, parse them into a FW-familiar + * format. + */ + if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] & + IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0]; + u8 ru_index_bitmap = + u16_get_bits(*ppe, + IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, + ppe, ppe_pos_bit, true); + /* EHT PPE Thresholds doesn't exist - set the API according to + * HE PPE Tresholds + */ + } else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + /* Even though HE Capabilities IE doesn't contain PPE + * Thresholds for BW 320Mhz, thresholds for this BW will + * be filled in with the same values as 160Mhz, due to + * the inheritance, as required. + */ + iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, + true); + + /* According to the requirements, for MCSs 12-13 the + * maximum value between HE PPE Threshold and Common + * Nominal Packet Padding needs to be taken + */ + iwl_mld_get_optimal_ppe_info(pkt_ext, nominal_padding); + + /* if PPE Thresholds doesn't present in both EHT IE and HE IE - + * take the Thresholds from Common Nominal Packet Padding field + */ + } else { + iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext, + nominal_padding); + } + } else if (link_sta->he_cap.has_he) { + /* If PPE Thresholds exist, parse them into a FW-familiar format. */ + if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, + false); + /* PPE Thresholds doesn't exist - set the API PPE values + * according to Common Nominal Packet Padding field. + */ + } else { + u8 nominal_padding = + u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9], + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext, + nominal_padding); + } + } + + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (int bw = 0; + bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = + &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + IWL_DEBUG_HT(mld, + "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", + i, bw, qam_th[0], qam_th[1]); + } + } +} + +static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta) +{ + u8 *mac_cap_info = + &link_sta->he_cap.he_cap_elem.mac_cap_info[0]; + u32 htc_flags = 0; + + if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) + htc_flags |= IWL_HE_HTC_SUPPORT; + if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) || + (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) { + u8 link_adap = + ((mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) + + (mac_cap_info[1] & + IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION); + + if (link_adap == 2) + htc_flags |= + IWL_HE_HTC_LINK_ADAP_UNSOLICITED; + else if (link_adap == 3) + htc_flags |= IWL_HE_HTC_LINK_ADAP_BOTH; + } + if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) + htc_flags |= IWL_HE_HTC_BSR_SUPP; + if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) + htc_flags |= IWL_HE_HTC_OMI_SUPP; + if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) + htc_flags |= IWL_HE_HTC_BQR_SUPP; + + return htc_flags; +} + +static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, + const struct iwl_sta_cfg_cmd *cmd) +{ + int ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), + cmd); + if (ret) + IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); + return ret; +} + +static int +iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + struct iwl_sta_cfg_cmd cmd = {}; + int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + lockdep_assert_wiphy(mld->wiphy); + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + + mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!link || !mld_link) || fw_id < 0) + return -EINVAL; + + cmd.sta_id = cpu_to_le32(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); + + if (mld_sta->sta_state >= IEEE80211_STA_ASSOC) + cmd.assoc_id = cpu_to_le32(sta->aid); + + if (sta->mfp || mld_sta->sta_state < IEEE80211_STA_AUTHORIZED) + cmd.mfp = cpu_to_le32(1); + + switch (link_sta->rx_nss) { + case 1: + cmd.mimo = cpu_to_le32(0); + break; + case 2 ... 8: + cmd.mimo = cpu_to_le32(1); + break; + } + + switch (link_sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_NUM_MODES: + WARN_ON(1); + break; + case IEEE80211_SMPS_STATIC: + /* override NSS */ + cmd.mimo = cpu_to_le32(0); + break; + case IEEE80211_SMPS_DYNAMIC: + cmd.mimo_protection = cpu_to_le32(1); + break; + case IEEE80211_SMPS_OFF: + /* nothing */ + break; + } + + iwl_mld_fill_ampdu_size_and_dens(link_sta, link, + &cmd.tx_ampdu_max_size, + &cmd.tx_ampdu_spacing); + + if (sta->wme) { + cmd.sp_length = + cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128); + cmd.uapsd_acs = cpu_to_le32(iwl_mld_get_uapsd_acs(sta)); + } + + if (link_sta->he_cap.has_he) { + cmd.trig_rnd_alloc = + cpu_to_le32(link->uora_exists ? 1 : 0); + + /* PPE Thresholds */ + iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext); + + /* HTC flags */ + cmd.htc_flags = + cpu_to_le32(iwl_mld_get_htc_flags(link_sta)); + + if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_ACK_EN) + cmd.ack_enabled = cpu_to_le32(1); + } + + return iwl_mld_send_sta_cmd(mld, &cmd); +} + +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) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct iwl_mld_link_sta *mld_link_sta; + int ret; + u8 fw_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* We will fail to add it to the FW anyway */ + if (iwl_mld_error_before_recovery(mld)) + return -ENODEV; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + + /* We need to preserve the fw sta ids during a restart, since the fw + * will recover SN/PN for them, this is why the mld_link_sta exists. + */ + if (mld_link_sta) { + /* But if we are not restarting, this is not OK */ + WARN_ON(!mld->fw_status.in_hw_restart); + + /* Avoid adding a STA that is already in FW to avoid an assert */ + if (WARN_ON(mld_link_sta->in_fw)) + return -EINVAL; + + fw_id = mld_link_sta->fw_id; + goto add_to_fw; + } + + /* Allocate a fw id and map it to the link_sta */ + ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); + if (ret) + return ret; + + if (link_sta == &link_sta->sta->deflink) { + mld_link_sta = &mld_sta->deflink; + } else { + mld_link_sta = kzalloc(sizeof(*mld_link_sta), GFP_KERNEL); + if (!mld_link_sta) + return -ENOMEM; + } + + mld_link_sta->fw_id = fw_id; + rcu_assign_pointer(mld_sta->link[link_sta->link_id], mld_link_sta); + +add_to_fw: + ret = iwl_mld_add_modify_sta_cmd(mld, link_sta); + if (ret) { + RCU_INIT_POINTER(mld->fw_id_to_link_sta[fw_id], NULL); + RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL); + if (link_sta != &link_sta->sta->deflink) + kfree(mld_link_sta); + return ret; + } + mld_link_sta->in_fw = true; + + return 0; +} + +static int iwl_mld_rm_sta_from_fw(struct iwl_mld *mld, u8 fw_sta_id) +{ + struct iwl_remove_sta_cmd cmd = { + .sta_id = cpu_to_le32(fw_sta_id), + }; + int ret; + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD), + &cmd); + if (ret) + IWL_ERR(mld, "Failed to remove station. Id=%d\n", fw_sta_id); + + return ret; +} + +static void +iwl_mld_remove_link_sta(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct iwl_mld_link_sta *mld_link_sta = + iwl_mld_link_sta_from_mac80211(link_sta); + + if (WARN_ON(!mld_link_sta)) + return; + + iwl_mld_rm_sta_from_fw(mld, mld_link_sta->fw_id); + mld_link_sta->in_fw = false; + + /* Now that the STA doesn't exist in FW, we don't expect any new + * notifications for it. Cancel the ones that are already pending + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_STA, + mld_link_sta->fw_id); + + /* This will not be done upon reconfig, so do it also when + * failed to remove from fw + */ + RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], NULL); + RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL); + if (mld_link_sta != &mld_sta->deflink) + kfree_rcu(mld_link_sta, rcu_head); +} + +int iwl_mld_update_all_link_stations(struct iwl_mld *mld, + struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_link_sta *link_sta; + int link_id; + + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + int ret = iwl_mld_add_modify_sta_cmd(mld, link_sta); + + if (ret) + return ret; + } + return 0; +} + +static void iwl_mld_destroy_sta(struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + kfree(mld_sta->dup_data); + kfree(mld_sta->mpdu_counters); +} + +static int +iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) +{ + struct iwl_mld_rxq_dup_data *dup_data; + + if (mld->fw_status.in_hw_restart) + return 0; + + dup_data = kcalloc(mld->trans->num_rx_queues, sizeof(*dup_data), + GFP_KERNEL); + if (!dup_data) + return -ENOMEM; + + /* Initialize all the last_seq values to 0xffff which can never + * compare equal to the frame's seq_ctrl in the check in + * iwl_mld_is_dup() since the lower 4 bits are the fragment + * number and fragmented packets don't reach that function. + * + * This thus allows receiving a packet with seqno 0 and the + * retry bit set as the very first packet on a new TID. + */ + for (int q = 0; q < mld->trans->num_rx_queues; q++) + memset(dup_data[q].last_seq, 0xff, + sizeof(dup_data[q].last_seq)); + mld_sta->dup_data = dup_data; + + return 0; +} + +static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld, + struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_vif *vif = mld_sta->vif; + + if (mld->fw_status.in_hw_restart) + return; + + /* MPDUs are counted only when EMLSR is possible */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION || + sta->tdls || !ieee80211_vif_is_mld(vif)) + return; + + mld_sta->mpdu_counters = kcalloc(mld->trans->num_rx_queues, + sizeof(*mld_sta->mpdu_counters), + GFP_KERNEL); + if (!mld_sta->mpdu_counters) + return; + + for (int q = 0; q < mld->trans->num_rx_queues; q++) + spin_lock_init(&mld_sta->mpdu_counters[q].lock); +} + +static int +iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, enum iwl_fw_sta_type type) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + mld_sta->vif = vif; + mld_sta->sta_type = type; + mld_sta->mld = mld; + + if (!mld->fw_status.in_hw_restart) + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + iwl_mld_init_txq(iwl_mld_txq_from_mac80211(sta->txq[i])); + + iwl_mld_alloc_mpdu_counters(mld, sta); + + iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant); + + return iwl_mld_alloc_dup_data(mld, mld_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 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); + if (ret) + return ret; + + /* We could have add only the deflink link_sta, but it will not work + * in the restart case if the single link that is active during + * reconfig is not the deflink one. + */ + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + ret = iwl_mld_add_link_sta(mld, link_sta); + if (ret) + goto destroy_sta; + } + + return 0; + +destroy_sta: + iwl_mld_destroy_sta(sta); + + return ret; +} + +void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_link_sta *link_sta; + int link_id; + + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + if (fw_sta_id < 0) + continue; + + iwl_mld_flush_link_sta_txqs(mld, fw_sta_id); + } +} + +void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + /* Avoid a warning in iwl_trans_wait_txq_empty if are anyway on the way + * to a restart. + */ + if (iwl_mld_error_before_recovery(mld)) + return; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct iwl_mld_txq *mld_txq = + iwl_mld_txq_from_mac80211(sta->txq[i]); + + if (!mld_txq->status.allocated) + continue; + + iwl_trans_wait_txq_empty(mld->trans, mld_txq->fw_id); + } +} + +void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_vif *vif = mld_sta->vif; + struct ieee80211_link_sta *link_sta; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* Tell the HW to flush the queues */ + iwl_mld_flush_sta_txqs(mld, sta); + + /* Wait for trans to empty its queues */ + iwl_mld_wait_sta_txqs_empty(mld, sta); + + /* Now we can remove the queues */ + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + iwl_mld_remove_txq(mld, sta->txq[i]); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + /* Mac8011 will remove the groupwise keys after the sta is + * removed, but FW expects all the keys to be removed before + * the STA is, so remove them all here. + */ + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mld_remove_ap_keys(mld, vif, sta, link_id); + + /* Remove the link_sta */ + iwl_mld_remove_link_sta(mld, link_sta); + } + + iwl_mld_destroy_sta(sta); +} + +u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + u32 result = 0; + + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_fw_sta_id_mask, mld, sta); + + /* This function should only be used with the wiphy lock held, + * In other cases, it is not guaranteed that the link_sta will exist + * in the driver too, and it is checked in + * iwl_mld_fw_sta_id_from_link_sta. + */ + lockdep_assert_wiphy(mld->wiphy); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + if (!(fw_id < 0)) + result |= BIT(fw_id); + } + + return result; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_fw_sta_id_mask); + +static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue, + u32 count, bool tx) +{ + struct iwl_mld_per_q_mpdu_counter *queue_counter; + struct iwl_mld_per_link_mpdu_counter *link_counter; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link *mld_link; + struct iwl_mld *mld; + int total_mpdus = 0; + + if (WARN_ON(!link_sta)) + return; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + if (!mld_sta->mpdu_counters) + return; + + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + mld_link = iwl_mld_link_dereference_check(mld_vif, link_sta->link_id); + + if (WARN_ON_ONCE(!mld_link)) + return; + + queue_counter = &mld_sta->mpdu_counters[queue]; + + mld = mld_vif->mld; + + /* If it the window is over, first clear the counters. + * When we are not blocked by TPT, the window is managed by check_tpt_wk + */ + if ((mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT) && + time_is_before_jiffies(queue_counter->window_start_time + + IWL_MLD_TPT_COUNT_WINDOW)) { + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + queue_counter->window_start_time = jiffies; + + IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n"); + } + + link_counter = &queue_counter->per_link[mld_link->fw_id]; + + spin_lock_bh(&queue_counter->lock); + + /* Update the statistics for this TPT measurement window */ + if (tx) + link_counter->tx += count; + else + link_counter->rx += count; + + /* + * Next, evaluate whether we should queue an unblock, + * skip this if we are not blocked due to low throughput. + */ + if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) + goto unlock; + + for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++) + total_mpdus += tx ? queue_counter->per_link[i].tx : + queue_counter->per_link[i].rx; + + /* Unblock is already queued if the threshold was reached before */ + if (total_mpdus - count >= IWL_MLD_ENTER_EMLSR_TPT_THRESH) + goto unlock; + + if (total_mpdus >= IWL_MLD_ENTER_EMLSR_TPT_THRESH) + wiphy_work_queue(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk); + +unlock: + spin_unlock_bh(&queue_counter->lock); +} + +/* must be called under rcu_read_lock() */ +void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue, + u32 count) +{ + iwl_mld_count_mpdu(link_sta, queue, count, false); +} + +/* must be called under rcu_read_lock() */ +void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count) +{ + /* use queue 0 for all TX */ + iwl_mld_count_mpdu(link_sta, 0, count, true); +} + +static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + u8 tid) +{ + u32 sta_mask = BIT(internal_sta->sta_id); + int queue, size; + + size = max_t(u32, IWL_MGMT_QUEUE_SIZE, + mld->trans->cfg->min_txq_size); + + queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size, + IWL_WATCHDOG_DISABLED); + + if (queue >= 0) + IWL_DEBUG_TX_QUEUES(mld, + "Enabling TXQ #%d for sta mask 0x%x tid %d\n", + queue, sta_mask, tid); + return queue; +} + +static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld, + const struct iwl_mld_int_sta *internal_sta) +{ + struct iwl_aux_sta_cmd cmd = { + .sta_id = cpu_to_le32(internal_sta->sta_id), + /* TODO: CDB - properly set the lmac_id */ + .lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX), + }; + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD), + &cmd); +} + +static int +iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, + const struct iwl_mld_int_sta *internal_sta, + u8 fw_link_id, + const u8 *addr) +{ + struct iwl_sta_cfg_cmd cmd = {}; + + if (internal_sta->sta_type == STATION_TYPE_AUX) + 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.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. + * On the other hand, FW will never check this flag during RX since + * an AP/GO doesn't receive protected broadcast management frames. + * So, we can set it unconditionally. + */ + if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT) + cmd.mfp = cpu_to_le32(1); + + if (addr) { + memcpy(cmd.peer_mld_address, addr, ETH_ALEN); + memcpy(cmd.peer_link_address, addr, ETH_ALEN); + } + + return iwl_mld_send_sta_cmd(mld, &cmd); +} + +static int iwl_mld_add_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + enum iwl_fw_sta_type sta_type, + u8 fw_link_id, const u8 *addr, u8 tid) +{ + int ret, queue_id; + + ret = iwl_mld_allocate_link_sta_fw_id(mld, + &internal_sta->sta_id, + ERR_PTR(-EINVAL)); + if (ret) + return ret; + + internal_sta->sta_type = sta_type; + + ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id, + addr); + if (ret) + goto err; + + queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid); + if (queue_id < 0) { + iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); + ret = queue_id; + goto err; + } + + internal_sta->queue_id = queue_id; + + return 0; +err: + iwl_mld_free_internal_sta(mld, internal_sta); + return ret; +} + +int iwl_mld_add_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const u8 *addr; + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + addr = vif->type == NL80211_IFTYPE_ADHOC ? link->bssid : bcast_addr; + + return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta, + STATION_TYPE_BCAST_MGMT, + mld_link->fw_id, addr, + IWL_MGMT_TID); +} + +int iwl_mld_add_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + const u8 mcast_addr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta, + STATION_TYPE_MCAST, + mld_link->fw_id, mcast_addr, 0); +} + +int iwl_mld_add_aux_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta) +{ + return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX, + 0, NULL, IWL_MAX_TID_COUNT); +} + +static void iwl_mld_remove_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + bool flush, u8 tid) +{ + if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA || + internal_sta->queue_id == IWL_MLD_INVALID_QUEUE)) + return; + + if (flush) + iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id); + + iwl_mld_free_txq(mld, BIT(internal_sta->sta_id), + tid, internal_sta->queue_id); + + iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); + + iwl_mld_free_internal_sta(mld, internal_sta); +} + +void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->bcast_sta, true, + IWL_MGMT_TID); +} + +void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->mcast_sta, true, 0); +} + +void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + /* TODO: Hotspot 2.0 */ + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->aux_sta, false, + IWL_MAX_TID_COUNT); +} + +static int iwl_mld_update_sta_resources(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask) +{ + int ret; + + ret = iwl_mld_update_sta_txqs(mld, sta, old_sta_mask, new_sta_mask); + if (ret) + return ret; + + ret = iwl_mld_update_sta_keys(mld, vif, sta, old_sta_mask, new_sta_mask); + if (ret) + return ret; + + return iwl_mld_update_sta_baids(mld, old_sta_mask, new_sta_mask); +} + +int iwl_mld_update_link_stas(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + 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; + unsigned long sta_mask_added = 0; + u32 current_sta_mask = 0, sta_mask_to_rem = 0; + unsigned int link_id, sta_id; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_set_bit(link_id, &old_links_long, + IEEE80211_MLD_MAX_NUM_LINKS) { + mld_link_sta = + iwl_mld_link_sta_dereference_check(mld_sta, link_id); + + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + current_sta_mask |= BIT(mld_link_sta->fw_id); + if (links_to_rem & BIT(link_id)) + sta_mask_to_rem |= BIT(mld_link_sta->fw_id); + } + + if (sta_mask_to_rem) { + ret = iwl_mld_update_sta_resources(mld, vif, sta, + current_sta_mask, + current_sta_mask & + ~sta_mask_to_rem); + if (ret) + return ret; + + current_sta_mask &= ~sta_mask_to_rem; + } + + for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_protected(sta, link_id); + + if (WARN_ON(!link_sta)) + return -EINVAL; + + iwl_mld_remove_link_sta(mld, link_sta); + } + + for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_protected(sta, link_id); + struct ieee80211_bss_conf *link; + + if (WARN_ON(!link_sta)) + return -EINVAL; + + ret = iwl_mld_add_link_sta(mld, link_sta); + if (ret) + goto remove_added_link_stas; + + mld_link_sta = + iwl_mld_link_sta_dereference_check(mld_sta, + link_id); + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + + sta_mask_added |= BIT(mld_link_sta->fw_id); + } + + if (sta_mask_added) { + ret = iwl_mld_update_sta_resources(mld, vif, sta, + current_sta_mask, + current_sta_mask | + sta_mask_added); + if (ret) + goto remove_added_link_stas; + } + + /* We couldn't activate the links before it has a STA. Now we can */ + for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link = + link_conf_dereference_protected(mld_sta->vif, link_id); + + if (WARN_ON(!link)) + continue; + + iwl_mld_activate_link(mld, link); + } + + return 0; + +remove_added_link_stas: + for_each_set_bit(sta_id, &sta_mask_added, mld->fw->ucode_capa.num_stations) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[sta_id]); + + if (WARN_ON(!link_sta)) + continue; + + iwl_mld_remove_link_sta(mld, link_sta); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h new file mode 100644 index 000000000000..ddcffd7b9fde --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __iwl_mld_sta_h__ +#define __iwl_mld_sta_h__ + +#include + +#include "mld.h" +#include "tx.h" + +/** + * struct iwl_mld_rxq_dup_data - Duplication detection data, per STA & Rx queue + * @last_seq: last sequence per tid. + * @last_sub_frame_idx: the index of the last subframe in an A-MSDU. This value + * will be zero if the packet is not part of an A-MSDU. + */ +struct iwl_mld_rxq_dup_data { + __le16 last_seq[IWL_MAX_TID_COUNT + 1]; + u8 last_sub_frame_idx[IWL_MAX_TID_COUNT + 1]; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_link_sta - link-level station + * + * This represents the link-level sta - the driver level equivalent to the + * ieee80211_link_sta + * + * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif + * @signal_avg: the signal average coming from the firmware + * @in_fw: whether the link STA is uploaded to the FW (false during restart) + * @rcu_head: RCU head for freeing this object + * @fw_id: the FW id of this link sta. + */ +struct iwl_mld_link_sta { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u32 last_rate_n_flags; + bool in_fw; + s8 signal_avg; + ); + /* And here fields that survive a fw restart */ + struct rcu_head rcu_head; + u32 fw_id; +}; + +#define iwl_mld_link_sta_dereference_check(mld_sta, link_id) \ + rcu_dereference_check((mld_sta)->link[link_id], \ + lockdep_is_held(&mld_sta->mld->wiphy->mtx)) + +#define for_each_mld_link_sta(mld_sta, link_sta, link_id) \ + for (link_id = 0; link_id < ARRAY_SIZE((mld_sta)->link); \ + link_id++) \ + if ((link_sta = \ + iwl_mld_link_sta_dereference_check(mld_sta, link_id))) + +#define IWL_NUM_DEFAULT_KEYS 4 + +/* struct iwl_mld_ptk_pn - Holds Packet Number (PN) per TID. + * @rcu_head: RCU head for freeing this data. + * @pn: Array storing PN for each TID. + */ +struct iwl_mld_ptk_pn { + struct rcu_head rcu_head; + struct { + u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN]; + } ____cacheline_aligned_in_smp q[]; +}; + +/** + * struct iwl_mld_per_link_mpdu_counter - per-link TX/RX MPDU counters + * + * @tx: Number of TX MPDUs. + * @rx: Number of RX MPDUs. + */ +struct iwl_mld_per_link_mpdu_counter { + u32 tx; + u32 rx; +}; + +/** + * struct iwl_mld_per_q_mpdu_counter - per-queue MPDU counter + * + * @lock: Needed to protect the counters when modified from statistics. + * @per_link: per-link counters. + * @window_start_time: timestamp of the counting-window start + */ +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]; + unsigned long window_start_time; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_sta - representation of a station in the driver. + * + * This represent the MLD-level sta, and will not be added to the FW. + * Embedded in ieee80211_sta. + * + * @vif: pointer the vif object. + * @sta_state: station state according to enum %ieee80211_sta_state + * @sta_type: type of this station. See &enum iwl_fw_sta_type + * @mld: a pointer to the iwl_mld object + * @dup_data: per queue duplicate packet detection data + * @data_tx_ant: stores the last TX antenna index; used for setting + * TX rate_n_flags for injected data frames (toggles on every TX failure). + * @tid_to_baid: a simple map of TID to Block-Ack fw id + * @deflink: This holds the default link STA information, for non MLO STA all + * link specific STA information is accessed through @deflink or through + * link[0] which points to address of @deflink. For MLO Link STA + * the first added link STA will point to deflink. + * @link: reference to Link Sta entries. For Non MLO STA, except 1st link, + * i.e link[0] all links would be assigned to NULL by default and + * would access link information via @deflink or link[0]. For MLO + * STA, first link STA being added will point its link pointer to + * @deflink address and remaining would be allocated and the address + * would be assigned to link[link_id] where link_id is the id assigned + * by the AP. + * @ptk_pn: Array of pointers to PTK PN data, used to track the Packet Number + * per key index and per queue (TID). + * @mpdu_counters: RX/TX MPDUs counters for each queue. + */ +struct iwl_mld_sta { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + enum ieee80211_sta_state sta_state; + enum iwl_fw_sta_type sta_type; + ); + /* And here fields that survive a fw restart */ + struct iwl_mld *mld; + struct ieee80211_vif *vif; + struct iwl_mld_rxq_dup_data *dup_data; + u8 tid_to_baid[IWL_MAX_TID_COUNT]; + u8 data_tx_ant; + + struct iwl_mld_link_sta deflink; + struct iwl_mld_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct iwl_mld_ptk_pn __rcu *ptk_pn[IWL_NUM_DEFAULT_KEYS]; + struct iwl_mld_per_q_mpdu_counter *mpdu_counters; +}; + +static inline struct iwl_mld_sta * +iwl_mld_sta_from_mac80211(struct ieee80211_sta *sta) +{ + return (void *)sta->drv_priv; +} + +static inline void +iwl_mld_cleanup_sta(void *data, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + CLEANUP_STRUCT(iwl_mld_txq_from_mac80211(sta->txq[i])); + + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) { + CLEANUP_STRUCT(mld_link_sta); + + if (!ieee80211_vif_is_mld(mld_sta->vif)) { + /* not an MLD STA; only has the deflink with ID zero */ + WARN_ON(link_id); + continue; + } + + if (mld_sta->vif->active_links & BIT(link_id)) + continue; + + /* Should not happen as link removal should always succeed */ + WARN_ON(1); + RCU_INIT_POINTER(mld_sta->link[link_id], NULL); + RCU_INIT_POINTER(mld_sta->mld->fw_id_to_link_sta[mld_link_sta->fw_id], + NULL); + if (mld_link_sta != &mld_sta->deflink) + kfree_rcu(mld_link_sta, rcu_head); + } + + CLEANUP_STRUCT(mld_sta); +} + +static inline struct iwl_mld_link_sta * +iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + return iwl_mld_link_sta_dereference_check(mld_sta, link_sta->link_id); +} + +int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, enum iwl_fw_sta_type type); +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); +u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta); +int iwl_mld_update_all_link_stations(struct iwl_mld *mld, + struct ieee80211_sta *sta); +void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta); +void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, + struct ieee80211_sta *sta); +void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue, + u32 count); +void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count); + +/** + * struct iwl_mld_int_sta - representation of an internal station + * (a station that exist in FW and in driver, but not in mac80211) + * + * @sta_id: the index of the station in the fw + * @queue_id: the if of the queue used by the station + * @sta_type: station type. One of &iwl_fw_sta_type + */ +struct iwl_mld_int_sta { + u8 sta_id; + u32 queue_id; + enum iwl_fw_sta_type sta_type; +}; + +static inline void +iwl_mld_init_internal_sta(struct iwl_mld_int_sta *internal_sta) +{ + internal_sta->sta_id = IWL_INVALID_STA; + internal_sta->queue_id = IWL_MLD_INVALID_QUEUE; +} + +static inline void +iwl_mld_free_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta) +{ + if (WARN_ON(internal_sta->sta_id == IWL_INVALID_STA)) + return; + + RCU_INIT_POINTER(mld->fw_id_to_link_sta[internal_sta->sta_id], NULL); + iwl_mld_init_internal_sta(internal_sta); +} + +int iwl_mld_add_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_add_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_add_aux_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta); + +void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_update_link_stas(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links); +#endif /* __iwl_mld_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c new file mode 100644 index 000000000000..842b9b9fdd8c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "stats.h" +#include "sta.h" +#include "mlo.h" +#include "hcmd.h" +#include "iface.h" +#include "scan.h" +#include "phy.h" +#include "fw/api/stats.h" + +static int iwl_mld_send_fw_stats_cmd(struct iwl_mld *mld, u32 cfg_mask, + u32 cfg_time, u32 type_mask) +{ + u32 cmd_id = WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_CMD); + struct iwl_system_statistics_cmd stats_cmd = { + .cfg_mask = cpu_to_le32(cfg_mask), + .config_time_sec = cpu_to_le32(cfg_time), + .type_id_mask = cpu_to_le32(type_mask), + }; + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &stats_cmd); +} + +int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld) +{ + u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK; + u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1; + + return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); +} + +static void +iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u8 fw_sta_id, struct station_info *sinfo) +{ + const struct iwl_system_statistics_notif_oper *notif = + (void *)&pkt->data; + const struct iwl_stats_ntfy_per_sta *per_sta = + ¬if->per_sta[fw_sta_id]; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link_sta *mld_link_sta; + + /* 0 isn't a valid value, but FW might send 0. + * In that case, set the latest non-zero value we stored + */ + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[fw_sta_id]); + if (IS_ERR_OR_NULL(link_sta)) + goto unlock; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + goto unlock; + + if (per_sta->average_energy) + mld_link_sta->signal_avg = + -(s8)le32_to_cpu(per_sta->average_energy); + + sinfo->signal_avg = mld_link_sta->signal_avg; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); + +unlock: + rcu_read_unlock(); +} + +struct iwl_mld_stats_data { + u8 fw_sta_id; + struct station_info *sinfo; + struct iwl_mld *mld; +}; + +static bool iwl_mld_wait_stats_handler(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_stats_data *stats_data = data; + u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + switch (cmd) { + case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF): + iwl_mld_fill_stats_from_oper_notif(stats_data->mld, pkt, + stats_data->fw_sta_id, + stats_data->sinfo); + break; + case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF): + break; + case WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF): + return true; + } + + return false; +} + +static int +iwl_mld_fw_stats_to_mac80211(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta, + struct station_info *sinfo) +{ + u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK | + IWL_STATS_CFG_FLG_RESET_MSK; + u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1; + static const u16 notifications[] = { + WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF), + WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF), + WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF), + }; + struct iwl_mld_stats_data wait_stats_data = { + /* We don't support drv_sta_statistics in EMLSR */ + .fw_sta_id = mld_sta->deflink.fw_id, + .sinfo = sinfo, + .mld = mld, + }; + struct iwl_notification_wait stats_wait; + int ret; + + iwl_init_notification_wait(&mld->notif_wait, &stats_wait, + notifications, ARRAY_SIZE(notifications), + iwl_mld_wait_stats_handler, + &wait_stats_data); + + ret = iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); + if (ret) { + iwl_remove_notification(&mld->notif_wait, &stats_wait); + return ret; + } + + /* Wait 500ms for OPERATIONAL, PART1, and END notifications, + * which should be sufficient for the firmware to gather data + * from all LMACs and send notifications to the host. + */ + ret = iwl_wait_notification(&mld->notif_wait, &stats_wait, HZ / 2); + if (ret) + return ret; + + /* When periodic statistics are sent, FW will clear its statistics DB. + * If the statistics request here happens shortly afterwards, + * the response will contain data collected over a short time + * interval. The response we got here shouldn't be processed by + * the general statistics processing because it's incomplete. + * So, we delete it from the list so it won't be processed. + */ + iwl_mld_delete_handlers(mld, notifications, ARRAY_SIZE(notifications)); + + return 0; +} + +#define PERIODIC_STATS_SECONDS 5 + +int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable) +{ + u32 cfg_mask = enable ? 0 : IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK; + u32 type_mask = enable ? (IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1) : 0; + u32 cfg_time = enable ? PERIODIC_STATS_SECONDS : 0; + + return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, cfg_time, type_mask); +} + +static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, + struct station_info *sinfo) +{ + struct rate_info *rinfo = &sinfo->txrate; + u32 rate_n_flags = mld_sta->deflink.last_rate_n_flags; + u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + u32 gi_ltf; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + rinfo->bw = RATE_INFO_BW_20; + break; + case RATE_MCS_CHAN_WIDTH_40: + rinfo->bw = RATE_INFO_BW_40; + break; + case RATE_MCS_CHAN_WIDTH_80: + rinfo->bw = RATE_INFO_BW_80; + break; + case RATE_MCS_CHAN_WIDTH_160: + rinfo->bw = RATE_INFO_BW_160; + break; + case RATE_MCS_CHAN_WIDTH_320: + rinfo->bw = RATE_INFO_BW_320; + break; + } + + if (format == RATE_MCS_CCK_MSK || format == RATE_MCS_LEGACY_OFDM_MSK) { + int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); + + /* add the offset needed to get to the legacy ofdm indices */ + if (format == RATE_MCS_LEGACY_OFDM_MSK) + rate += IWL_FIRST_OFDM_RATE; + + switch (rate) { + case IWL_RATE_1M_INDEX: + rinfo->legacy = 10; + break; + case IWL_RATE_2M_INDEX: + rinfo->legacy = 20; + break; + case IWL_RATE_5M_INDEX: + rinfo->legacy = 55; + break; + case IWL_RATE_11M_INDEX: + rinfo->legacy = 110; + break; + case IWL_RATE_6M_INDEX: + rinfo->legacy = 60; + break; + case IWL_RATE_9M_INDEX: + rinfo->legacy = 90; + break; + case IWL_RATE_12M_INDEX: + rinfo->legacy = 120; + break; + case IWL_RATE_18M_INDEX: + rinfo->legacy = 180; + break; + case IWL_RATE_24M_INDEX: + rinfo->legacy = 240; + break; + case IWL_RATE_36M_INDEX: + rinfo->legacy = 360; + break; + case IWL_RATE_48M_INDEX: + rinfo->legacy = 480; + break; + case IWL_RATE_54M_INDEX: + rinfo->legacy = 540; + } + return; + } + + rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; + + if (format == RATE_MCS_HT_MSK) + rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags); + else + rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); + + if (rate_n_flags & RATE_MCS_SGI_MSK) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + + switch (format) { + case RATE_MCS_EHT_MSK: + rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; + break; + case RATE_MCS_HE_MSK: + gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); + + rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; + + if (rate_n_flags & RATE_MCS_HE_106T_MSK) { + rinfo->bw = RATE_INFO_BW_HE_RU; + rinfo->he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106; + } + + switch (rate_n_flags & RATE_MCS_HE_TYPE_MSK) { + case RATE_MCS_HE_TYPE_SU: + case RATE_MCS_HE_TYPE_EXT_SU: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + else if (gi_ltf == 2) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else if (gi_ltf == 3) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + break; + case RATE_MCS_HE_TYPE_MU: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + else if (gi_ltf == 2) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + break; + case RATE_MCS_HE_TYPE_TRIG: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + break; + } + + if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) + rinfo->he_dcm = 1; + break; + case RATE_MCS_HT_MSK: + rinfo->flags |= RATE_INFO_FLAGS_MCS; + break; + case RATE_MCS_VHT_MSK: + rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; + break; + } +} + +void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + /* This API is not EMLSR ready, so we cannot provide complete + * information if EMLSR is active + */ + if (hweight16(vif->active_links) > 1) + return; + + if (iwl_mld_fw_stats_to_mac80211(mld_sta->mld, mld_sta, sinfo)) + return; + + iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo); + + /* TODO: NL80211_STA_INFO_BEACON_RX */ + + /* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */ +} + +#define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */ +#define IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH 50 /* percentage */ +#define IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC (500 * 1000) + +static u8 iwl_mld_stats_load_percentage(u32 last_ts_usec, u32 curr_ts_usec, + u32 total_airtime_usec) +{ + u32 elapsed_usec = curr_ts_usec - last_ts_usec; + + if (elapsed_usec < IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC) + return 0; + + return (100 * total_airtime_usec / elapsed_usec); +} + +static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld, + u32 total_airtime_usec, + u32 curr_ts_usec) +{ + u32 last_ts_usec = mld->scan.traffic_load.last_stats_ts_usec; + u8 load_prec; + + /* Skip the calculation as this is the first notification received */ + if (!last_ts_usec) + goto out; + + load_prec = iwl_mld_stats_load_percentage(last_ts_usec, curr_ts_usec, + total_airtime_usec); + + if (load_prec > IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH) + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_HIGH; + else if (load_prec > IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH) + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_MEDIUM; + else + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_LOW; + +out: + mld->scan.traffic_load.last_stats_ts_usec = curr_ts_usec; +} + +static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; + int exit_emlsr_thresh; + + if (sig == 0) { + IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n"); + return; + } + + /* TODO: task=statistics handle CQM notifications */ + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + /* Handle inactive EMLSR, check whether to switch links */ + if (!iwl_mld_emlsr_active(vif)) { + if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) + iwl_mld_trigger_link_selection(mld, vif); + return; + } + + /* We are in EMLSR, check if we need to exit */ + exit_emlsr_thresh = + iwl_mld_get_emlsr_rssi_thresh(mld, &bss_conf->chanreq.oper, + true); + + if (sig < exit_emlsr_thresh) + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LOW_RSSI, + iwl_mld_get_other_link(vif, + bss_conf->link_id)); +} + +static void +iwl_mld_process_per_link_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_link *per_link, + u32 curr_ts_usec) +{ + u32 total_airtime_usec = 0; + + for (u32 fw_id = 0; + fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf); + 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]); + if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION) + continue; + + link_stats = &per_link[fw_id]; + + total_airtime_usec += le32_to_cpu(link_stats->air_time); + + sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); + iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf); + + /* TODO: parse more fields here (task=statistics)*/ + } + + iwl_mld_stats_recalc_traffic_load(mld, total_airtime_usec, + curr_ts_usec); +} + +static void +iwl_mld_process_per_sta_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_sta *per_sta) +{ + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + struct iwl_mld_link_sta *mld_link_sta; + s8 avg_energy = + -(s8)le32_to_cpu(per_sta[i].average_energy); + + if (IS_ERR_OR_NULL(link_sta) || !avg_energy) + continue; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + continue; + + mld_link_sta->signal_avg = avg_energy; + } +} + +static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + void *data) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + const struct iwl_stats_ntfy_per_phy *per_phy = data; + + if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL)) + return; + + phy->channel_load_by_us = + le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us); + + /* TODO: channel load not by us (task=statistics) */ +} + +static void +iwl_mld_process_per_phy_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_phy *per_phy) +{ + ieee80211_iter_chan_contexts_mtx(mld->hw, + iwl_mld_fill_chanctx_stats, + (void *)(uintptr_t)per_phy); + + /* channel_load_by_us may have been updated, so recheck */ + iwl_mld_emlsr_check_chan_load(mld); +} + +void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_system_statistics_notif_oper *stats = + (void *)&pkt->data; + u32 curr_ts_usec = le32_to_cpu(stats->time_stamp); + + BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX); + BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) < + ARRAY_SIZE(mld->fw_id_to_bss_conf)); + + iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec); + iwl_mld_process_per_sta_stats(mld, stats->per_sta); + iwl_mld_process_per_phy_stats(mld, stats->per_phy); + + iwl_mld_check_omi_bw_reduction(mld); +} + +void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + /* TODO */ +} + diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.h b/drivers/net/wireless/intel/iwlwifi/mld/stats.h new file mode 100644 index 000000000000..de434e66d555 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_stats_h__ +#define __iwl_mld_stats_h__ + +int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable); + +void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo); + +void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld); + +#endif /* __iwl_mld_stats_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile new file mode 100644 index 000000000000..36317feb923b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o + +ccflags-y += -I$(src)/../ +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c new file mode 100644 index 000000000000..1fd664be1a7c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include +#include +#include + +#include "utils.h" +#include "mld.h" +#include "sta.h" +#include "agg.h" +#include "rx.h" + +#define FC_QOS_DATA (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA) +#define BA_WINDOW_SIZE 64 +#define QUEUE 0 + +static const struct reorder_buffer_case { + const char *desc; + struct { + /* ieee80211_hdr fields */ + u16 fc; + u8 tid; + bool multicast; + /* iwl_rx_mpdu_desc fields */ + u16 nssn; + /* used also for setting hdr::seq_ctrl */ + u16 sn; + u8 baid; + bool amsdu; + bool last_subframe; + bool old_sn; + bool dup; + } rx_pkt; + struct { + bool valid; + u16 head_sn; + u8 baid; + u16 num_entries; + /* The test prepares the reorder buffer with fake skbs based + * on the sequence numbers provided in @entries array. + */ + struct { + u16 sn; + /* Set add_subframes > 0 to simulate an A-MSDU by + * queueing additional @add_subframes skbs in the + * appropriate reorder buffer entry (based on the @sn) + */ + u8 add_subframes; + } entries[BA_WINDOW_SIZE]; + } reorder_buf_state; + struct { + enum iwl_mld_reorder_result reorder_res; + u16 head_sn; + u16 num_stored; + u16 skb_release_order[BA_WINDOW_SIZE]; + u16 skb_release_order_count; + } expected; +} reorder_buffer_cases[] = { + { + .desc = "RX packet with invalid BAID", + .rx_pkt = { + .fc = FC_QOS_DATA, + .baid = IWL_RX_REORDER_DATA_INVALID_BAID, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* Invalid BAID should not be buffered. The frame is + * passed to the network stack immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + }, + }, + { + .desc = "RX Multicast packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .multicast = true, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* Multicast packets are not buffered. The packet is + * passed to the network stack immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + }, + }, + { + .desc = "RX non-QoS data", + .rx_pkt = { + .fc = IEEE80211_FTYPE_DATA, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* non-QoS data frames do not require reordering. + * The packet is passed to the network stack + * immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + }, + }, + { + .desc = "RX old SN packet, reorder buffer is not yet valid", + .rx_pkt = { + .fc = FC_QOS_DATA, + .old_sn = true, + }, + .reorder_buf_state = { + .valid = false, + }, + .expected = { + /* The buffer is invalid and the RX packet has an old + * SN. The packet is passed to the network stack + * immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + }, + }, + { + .desc = "RX old SN packet, reorder buffer valid", + .rx_pkt = { + .fc = FC_QOS_DATA, + .old_sn = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* The buffer is valid and the RX packet has an old SN. + * The packet should be dropped. + */ + .reorder_res = IWL_MLD_DROP_SKB, + .num_stored = 0, + .head_sn = 100, + }, + }, + { + .desc = "RX duplicate packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .dup = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* Duplicate packets should be dropped */ + .reorder_res = IWL_MLD_DROP_SKB, + .num_stored = 0, + .head_sn = 100, + }, + }, + { + .desc = "RX In-order packet, sn < nssn", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX packet SN is in order and less than NSSN. + * Packet is released to the network stack immediately + * and buffer->head_sn is updated to NSSN. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 101, + }, + }, + { + .desc = "RX In-order packet, sn == head_sn", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 101, + .nssn = 100, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 101, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX packet SN is equal to buffer->head_sn. + * Packet is released to the network stack immediately + * and buffer->head_sn is incremented. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 102, + }, + }, + { + .desc = "RX In-order packet, IEEE80211_MAX_SN wrap around", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = IEEE80211_MAX_SN, + .nssn = IEEE80211_MAX_SN - 1, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = IEEE80211_MAX_SN, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX SN == buffer->head_sn == IEEE80211_MAX_SN + * Packet is released to the network stack immediately + * and buffer->head_sn is incremented correctly (wraps + * around to 0). + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 0, + }, + }, + { + .desc = "RX Out-of-order packet, pending packet in buffer", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 102, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0].sn = 101, + }, + .expected = { + /* 1. Reorder buffer contains one packet with SN=101. + * 2. RX packet SN = buffer->head_sn. + * Both packets are released (in order) to the network + * stack as there are no gaps. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 102, + .skb_release_order = {100, 101}, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, pending packet in buffer (wrap around)", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 0, + .nssn = 1, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = IEEE80211_MAX_SN - 1, + .num_entries = 1, + .entries[0].sn = IEEE80211_MAX_SN, + }, + .expected = { + /* 1. Reorder buffer contains one packet with + * SN=IEEE80211_MAX_SN. + * 2. RX Packet SN = 0 (after wrap around) + * Both packets are released (in order) to the network + * stack as there are no gaps. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 1, + .skb_release_order = { 4095, 0 }, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/2 holes in buffer, release RX packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0].sn = 102, + }, + .expected = { + /* 1. Reorder buffer contains one packet with SN=102. + * 2. There are 2 holes at SN={100, 101}. + * Only the RX packet (SN=100) is released, there is + * still a hole at 101. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 1, + .head_sn = 101, + .skb_release_order = {100}, + .skb_release_order_count = 1, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/2 holes, release 2 packets", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 102, + .nssn = 103, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 3, + .entries[0].sn = 101, + .entries[1].sn = 104, + .entries[2].sn = 105, + }, + .expected = { + /* 1. Reorder buffer contains three packets. + * 2. RX packet fills one of two holes (at SN=102). + * Two packets are released (until the next hole at + * SN=103). + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 2, + .head_sn = 103, + .skb_release_order = {101, 102}, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/1 holes, no packets released", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 102, + .nssn = 100, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 3, + .entries[0].sn = 101, + .entries[1].sn = 103, + .entries[2].sn = 104, + }, + .expected = { + /* 1. Reorder buffer contains three packets: + * SN={101, 103, 104}. + * 2. RX packet fills a hole (SN=102), but NSSN is + * smaller than buffered frames. + * No packets can be released yet and buffer->head_sn + * is not updated. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 4, + .head_sn = 100, + }, + }, + { + .desc = "RX In-order A-MSDU, last subframe", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + .amsdu = true, + .last_subframe = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0] = { + .sn = 100, + .add_subframes = 1, + }, + }, + .expected = { + /* 1. Reorder buffer contains a 2-sub frames A-MSDU + * at SN=100. + * 2. RX packet is the last SN=100 A-MSDU subframe + * All packets are released in order (3 x SN=100). + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 101, + .skb_release_order = {100, 100, 100}, + .skb_release_order_count = 3, + }, + }, + { + .desc = "RX In-order A-MSDU, not the last subframe", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + .amsdu = true, + .last_subframe = false, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0] = { + .sn = 100, + .add_subframes = 1, + }, + }, + .expected = { + /* 1. Reorder buffer contains a 2-sub frames A-MSDU + * at SN=100. + * 2. RX packet additional SN=100 A-MSDU subframe, + * but not the last one + * No packets are released and head_sn is not updated. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 3, + .head_sn = 100, + }, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_reorder_buffer, reorder_buffer_cases, desc); + +static struct sk_buff_head g_released_skbs; +static u16 g_num_released_skbs; + +/* Add released skbs from reorder buffer to a global list; This allows us + * to verify the correct release order of packets after they pass through the + * simulated reorder logic. + */ +static void +fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + __skb_queue_tail(&g_released_skbs, skb); + g_num_released_skbs++; +} + +static u32 +fake_iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + u32 sta_mask = 0; + + /* This is the expectation in the real function */ + lockdep_assert_wiphy(mld->wiphy); + + /* We can't use for_each_sta_active_link */ + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + sta_mask |= BIT(mld_link_sta->fw_id); + return sta_mask; +} + +static struct iwl_rx_mpdu_desc *setup_mpdu_desc(void) +{ + struct kunit *test = kunit_get_current_test(); + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_rx_mpdu_desc *mpdu_desc; + + KUNIT_ALLOC_AND_ASSERT(test, mpdu_desc); + + mpdu_desc->reorder_data |= + cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_BAID_MASK, + param->rx_pkt.baid)); + mpdu_desc->reorder_data |= + cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_SN_MASK, + param->rx_pkt.sn)); + mpdu_desc->reorder_data |= + cpu_to_le32(FIELD_PREP(IWL_RX_MPDU_REORDER_NSSN_MASK, + param->rx_pkt.nssn)); + if (param->rx_pkt.old_sn) + mpdu_desc->reorder_data |= + cpu_to_le32(IWL_RX_MPDU_REORDER_BA_OLD_SN); + + if (param->rx_pkt.dup) + mpdu_desc->status |= cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE); + + if (param->rx_pkt.amsdu) { + mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU; + if (param->rx_pkt.last_subframe) + mpdu_desc->amsdu_info |= + IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; + } + + return mpdu_desc; +} + +static struct sk_buff *alloc_and_setup_skb(u16 fc, u16 seq_ctrl, u8 tid, + bool mcast) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_hdr hdr = { + .frame_control = cpu_to_le16(fc), + .seq_ctrl = cpu_to_le16(seq_ctrl), + }; + struct sk_buff *skb; + + skb = kunit_zalloc_skb(test, 128, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, skb); + + if (ieee80211_is_data_qos(hdr.frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(&hdr); + + qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK; + } + + if (mcast) + hdr.addr1[0] = 0x1; + + skb_set_mac_header(skb, skb->len); + skb_put_data(skb, &hdr, ieee80211_hdrlen(hdr.frame_control)); + + return skb; +} + +static struct iwl_mld_reorder_buffer * +setup_reorder_buffer(struct iwl_mld_baid_data *baid_data) +{ + struct kunit *test = kunit_get_current_test(); + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_mld_reorder_buffer *buffer = baid_data->reorder_buf; + struct iwl_mld_reorder_buf_entry *entries = baid_data->entries; + struct sk_buff *fake_skb; + + buffer->valid = param->reorder_buf_state.valid; + buffer->head_sn = param->reorder_buf_state.head_sn; + buffer->queue = QUEUE; + + for (int i = 0; i < baid_data->buf_size; i++) + __skb_queue_head_init(&entries[i].frames); + + for (int i = 0; i < param->reorder_buf_state.num_entries; i++) { + u16 sn = param->reorder_buf_state.entries[i].sn; + int index = sn % baid_data->buf_size; + u8 add_subframes = + param->reorder_buf_state.entries[i].add_subframes; + /* create 1 skb per entry + additional skbs per num of + * requested subframes + */ + u8 num_skbs = 1 + add_subframes; + + for (int j = 0; j < num_skbs; j++) { + fake_skb = alloc_and_setup_skb(FC_QOS_DATA, sn, 0, + false); + __skb_queue_tail(&entries[index].frames, fake_skb); + buffer->num_stored++; + } + } + + return buffer; +} + +static struct iwl_mld_reorder_buffer *setup_ba_data(struct ieee80211_sta *sta) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_mld_baid_data *baid_data = NULL; + struct iwl_mld_reorder_buffer *buffer; + u32 reorder_buf_size = BA_WINDOW_SIZE * sizeof(baid_data->entries[0]); + u8 baid = param->reorder_buf_state.baid; + + /* Assuming only 1 RXQ */ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, baid_data, + sizeof(*baid_data) + reorder_buf_size); + + baid_data->baid = baid; + baid_data->tid = param->rx_pkt.tid; + baid_data->buf_size = BA_WINDOW_SIZE; + + wiphy_lock(mld->wiphy); + baid_data->sta_mask = iwl_mld_fw_sta_id_mask(mld, sta); + wiphy_unlock(mld->wiphy); + + baid_data->entries_per_queue = BA_WINDOW_SIZE; + + buffer = setup_reorder_buffer(baid_data); + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld->fw_id_to_ba[baid])); + rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data); + + return buffer; +} + +static void test_reorder_buffer(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_rx_mpdu_desc *mpdu_desc; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct sk_buff *skb; + struct iwl_mld_reorder_buffer *buffer; + enum iwl_mld_reorder_result reorder_res; + u16 skb_release_order_count = param->expected.skb_release_order_count; + u16 skb_idx = 0; + + /* Init globals and activate stubs */ + __skb_queue_head_init(&g_released_skbs); + g_num_released_skbs = 0; + kunit_activate_static_stub(test, iwl_mld_fw_sta_id_mask, + fake_iwl_mld_fw_sta_id_mask); + kunit_activate_static_stub(test, iwl_mld_pass_packet_to_mac80211, + fake_iwl_mld_pass_packet_to_mac80211); + + vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION); + sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1); + + /* Prepare skb, mpdu_desc, BA data and the reorder buffer */ + skb = alloc_and_setup_skb(param->rx_pkt.fc, param->rx_pkt.sn, + param->rx_pkt.tid, param->rx_pkt.multicast); + buffer = setup_ba_data(sta); + mpdu_desc = setup_mpdu_desc(); + + rcu_read_lock(); + reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc); + rcu_read_unlock(); + + KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res); + KUNIT_ASSERT_EQ(test, buffer->num_stored, param->expected.num_stored); + KUNIT_ASSERT_EQ(test, buffer->head_sn, param->expected.head_sn); + + /* Verify skbs release order */ + KUNIT_ASSERT_EQ(test, skb_release_order_count, g_num_released_skbs); + while ((skb = __skb_dequeue(&g_released_skbs))) { + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + + KUNIT_ASSERT_EQ(test, le16_to_cpu(hdr->seq_ctrl), + param->expected.skb_release_order[skb_idx]); + skb_idx++; + } + KUNIT_ASSERT_EQ(test, skb_idx, skb_release_order_count); +} + +static struct kunit_case reorder_buffer_test_cases[] = { + KUNIT_CASE_PARAM(test_reorder_buffer, test_reorder_buffer_gen_params), + {}, +}; + +static struct kunit_suite reorder_buffer = { + .name = "iwlmld-reorder-buffer", + .test_cases = reorder_buffer_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(reorder_buffer); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c new file mode 100644 index 000000000000..1cefa8b2cf61 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include + +#include +#include "mld.h" + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static void test_hcmd_names_sorted(struct kunit *test) +{ + int i; + + for (i = 0; i < global_iwl_mld_goups_size; i++) { + const struct iwl_hcmd_arr *arr = &iwl_mld_groups[i]; + int j; + + if (!arr->arr) + continue; + for (j = 0; j < arr->size - 1; j++) + KUNIT_EXPECT_LE(test, arr->arr[j].cmd_id, + arr->arr[j + 1].cmd_id); + } +} + +static void test_hcmd_names_for_rx(struct kunit *test) +{ + static struct iwl_trans t = { + .command_groups = iwl_mld_groups, + }; + + t.command_groups_size = global_iwl_mld_goups_size; + + for (unsigned int i = 0; i < iwl_mld_rx_handlers_num; i++) { + const struct iwl_rx_handler *rxh; + const char *name; + + rxh = &iwl_mld_rx_handlers[i]; + + name = iwl_get_cmd_string(&t, rxh->cmd_id); + KUNIT_EXPECT_NOT_NULL(test, name); + KUNIT_EXPECT_NE_MSG(test, strcmp(name, "UNKNOWN"), 0, + "ID 0x%04x is UNKNOWN", rxh->cmd_id); + } +} + +static struct kunit_case hcmd_names_cases[] = { + KUNIT_CASE(test_hcmd_names_sorted), + KUNIT_CASE(test_hcmd_names_for_rx), + {}, +}; + +static struct kunit_suite hcmd_names = { + .name = "iwlmld-hcmd-names", + .test_cases = hcmd_names_cases, +}; + +kunit_test_suite(hcmd_names); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c new file mode 100644 index 000000000000..d835550c1a6b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for link selection functions + * + * Copyright (C) 2025 Intel Corporation + */ +#include + +#include "utils.h" +#include "mld.h" +#include "link.h" +#include "iface.h" +#include "phy.h" + +static const struct link_grading_test_case { + const char *desc; + struct { + struct { + u8 link_id; + const struct cfg80211_chan_def *chandef; + bool active; + s32 signal; + bool has_chan_util_elem; + u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */ + u8 chan_load_by_us; /* 0-100, used only if active is true */; + } link; + } input; + unsigned int expected_grade; +} link_grading_cases[] = { + { + .desc = "channel util of 128 (50%)", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz, + .active = false, + .has_chan_util_elem = true, + .chan_util = 128, + }, + .expected_grade = 86, + }, + { + .desc = "channel util of 180 (70%)", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz, + .active = false, + .has_chan_util_elem = true, + .chan_util = 180, + }, + .expected_grade = 51, + }, + { + .desc = "channel util of 180 (70%), channel load by us of 10%", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz, + .has_chan_util_elem = true, + .chan_util = 180, + .active = true, + .chan_load_by_us = 10, + }, + .expected_grade = 67, + }, + { + .desc = "no channel util element", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz, + .active = true, + }, + .expected_grade = 120, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc); + +static void setup_link(struct ieee80211_bss_conf *link) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + const struct link_grading_test_case *test_param = + (const void *)(test->param_value); + + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + + link->bss->signal = DBM_TO_MBM(test_param->input.link.signal); + + link->chanreq.oper = *test_param->input.link.chandef; + + if (test_param->input.link.has_chan_util_elem) { + struct cfg80211_bss_ies *ies; + struct ieee80211_bss_load_elem bss_load = { + .channel_util = test_param->input.link.chan_util, + }; + struct element *elem = + iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD, + &bss_load, + sizeof(bss_load)); + unsigned int elem_len = sizeof(*elem) + sizeof(bss_load); + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ies, sizeof(*ies) + elem_len); + memcpy(ies->data, elem, elem_len); + ies->len = elem_len; + rcu_assign_pointer(link->bss->beacon_ies, ies); + rcu_assign_pointer(link->bss->ies, ies); + } + + if (test_param->input.link.active) { + struct ieee80211_chanctx_conf *chan_ctx = + wiphy_dereference(mld->wiphy, link->chanctx_conf); + struct iwl_mld_phy *phy; + + KUNIT_ASSERT_NOT_NULL(test, chan_ctx); + + phy = iwl_mld_phy_from_mac80211(chan_ctx); + + phy->channel_load_by_us = test_param->input.link.chan_load_by_us; + } +} + +static void test_link_grading(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct link_grading_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + unsigned int actual_grade; + u8 assoc_link_id; + /* Extract test case parameters */ + u8 link_id = test_param->input.link.link_id; + enum nl80211_band band = test_param->input.link.chandef->chan->band; + bool active = test_param->input.link.active; + u16 valid_links; + + /* If the link is not active, use a different link as the assoc link */ + if (active) { + assoc_link_id = link_id; + valid_links = BIT(link_id); + } else { + assoc_link_id = BIT(ffz(BIT(link_id))); + valid_links = BIT(assoc_link_id) | BIT(link_id); + } + + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, assoc_link_id, band); + + wiphy_lock(mld->wiphy); + link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); + KUNIT_ASSERT_NOT_NULL(test, link); + + setup_link(link); + + actual_grade = iwl_mld_get_link_grade(mld, link); + wiphy_unlock(mld->wiphy); + + /* Assert that the returned grade matches the expected grade */ + KUNIT_EXPECT_EQ(test, actual_grade, test_param->expected_grade); +} + +static struct kunit_case link_selection_cases[] = { + KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params), + {}, +}; + +static struct kunit_suite link_selection = { + .name = "iwlmld-link-selection-tests", + .test_cases = link_selection_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(link_selection); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c new file mode 100644 index 000000000000..6e251dbc1dfe --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include + +#include "utils.h" +#include "mld.h" +#include "link.h" +#include "iface.h" +#include "fw/api/mac-cfg.h" + +static const struct missed_beacon_test_case { + const char *desc; + struct { + struct iwl_missed_beacons_notif notif; + bool emlsr; + } input; + struct { + bool disconnected; + bool emlsr; + } output; +} missed_beacon_cases[] = { + { + .desc = "no EMLSR, no disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(4), + }, + }, + { + .desc = "no EMLSR, no beacon loss since Rx, no disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(20), + }, + }, + { + .desc = "no EMLSR, beacon loss since Rx, disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(20), + .consec_missed_beacons_since_last_rx = + cpu_to_le32(10), + }, + .output.disconnected = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_missed_beacon, missed_beacon_cases, desc); + +static void fake_ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + vif->cfg.assoc = false; +} + +static void test_missed_beacon(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + struct iwl_missed_beacons_notif *notif; + const struct missed_beacon_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif; + struct iwl_rx_packet *pkt; + + kunit_activate_static_stub(test, ieee80211_connection_loss, + fake_ieee80211_connection_loss); + pkt = iwl_mld_kunit_create_pkt(test_param->input.notif); + notif = (void *)pkt->data; + + if (test_param->input.emlsr) { + vif = iwlmld_kunit_assoc_emlsr(0x3, NL80211_BAND_5GHZ, + NL80211_BAND_6GHZ); + } else { + struct iwl_mld_vif *mld_vif; + + vif = iwlmld_kunit_setup_non_mlo_assoc(NL80211_BAND_6GHZ); + mld_vif = iwl_mld_vif_from_mac80211(vif); + notif->link_id = cpu_to_le32(mld_vif->deflink.fw_id); + } + + wiphy_lock(mld->wiphy); + + iwl_mld_handle_missed_beacon_notif(mld, pkt); + + wiphy_unlock(mld->wiphy); + + KUNIT_ASSERT_NE(test, vif->cfg.assoc, test_param->output.disconnected); + + /* TODO: add test cases for esr and check */ +} + +static struct kunit_case link_cases[] = { + KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), + {}, +}; + +static struct kunit_suite link = { + .name = "iwlmld-link", + .test_cases = link_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c new file mode 100644 index 000000000000..b7ea2043c366 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * This is just module boilerplate for the iwlmld kunit module. + * + * Copyright (C) 2024 Intel Corporation + */ +#include + +MODULE_IMPORT_NS(IWLWIFI); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("kunit tests for iwlmld"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c new file mode 100644 index 000000000000..20cb4e03ab41 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include +#include "utils.h" +#include "iwl-trans.h" +#include "mld.h" +#include "sta.h" + +static const struct is_dup_case { + const char *desc; + struct { + /* ieee80211_hdr fields */ + __le16 fc; + __le16 seq; + u8 tid; + bool multicast; + /* iwl_rx_mpdu_desc fields */ + bool is_amsdu; + u8 sub_frame_idx; + } rx_pkt; + struct { + __le16 last_seq; + u8 last_sub_frame_idx; + u8 tid; + } dup_data_state; + struct { + bool is_dup; + u32 rx_status_flag; + } result; +} is_dup_cases[] = { + { + .desc = "Control frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_CTL), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "Null func frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "Multicast data", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .multicast = true, + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "QoS null func frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "QoS data new sequence", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x101), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "QoS data same sequence, no retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "QoS data same sequence, has retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "QoS data invalid tid", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .tid = IWL_MAX_TID_COUNT + 1, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "non-QoS data, same sequence, same tid, no retry", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = IWL_MAX_TID_COUNT, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "non-QoS data, same sequence, same tid, has retry", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = IWL_MAX_TID_COUNT, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "non-QoS data, same sequence on different tid's", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = 7, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU new subframe, allow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 1, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_ALLOW_SAME_PN | + RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU subframe with smaller idx, disallow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 1, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 2, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU same subframe, no retry, disallow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 0, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU same subframe, has retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 0, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_is_dup, is_dup_cases, desc); + +static void +setup_dup_data_state(struct ieee80211_sta *sta) +{ + struct kunit *test = kunit_get_current_test(); + const struct is_dup_case *param = (const void *)(test->param_value); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + u8 tid = param->dup_data_state.tid; + struct iwl_mld_rxq_dup_data *dup_data; + + /* Allocate dup_data only for 1 queue */ + KUNIT_ALLOC_AND_ASSERT(test, dup_data); + + /* Initialize dup data, see iwl_mld_alloc_dup_data */ + memset(dup_data->last_seq, 0xff, sizeof(dup_data->last_seq)); + + dup_data->last_seq[tid] = param->dup_data_state.last_seq; + dup_data->last_sub_frame_idx[tid] = + param->dup_data_state.last_sub_frame_idx; + + mld_sta->dup_data = dup_data; +} + +static void setup_rx_pkt(const struct is_dup_case *param, + struct ieee80211_hdr *hdr, + struct iwl_rx_mpdu_desc *mpdu_desc) +{ + u8 tid = param->rx_pkt.tid; + + /* Set "new rx packet" header */ + hdr->frame_control = param->rx_pkt.fc; + hdr->seq_ctrl = param->rx_pkt.seq; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + + qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK; + } + + if (param->rx_pkt.multicast) + hdr->addr1[0] = 0x1; + + /* Set mpdu_desc */ + mpdu_desc->amsdu_info = param->rx_pkt.sub_frame_idx & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + if (param->rx_pkt.is_amsdu) + mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU; +} + +static void test_is_dup(struct kunit *test) +{ + const struct is_dup_case *param = (const void *)(test->param_value); + struct iwl_mld *mld = test->priv; + struct iwl_rx_mpdu_desc mpdu_desc = { }; + struct ieee80211_rx_status rx_status = { }; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ieee80211_hdr hdr; + + vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION); + sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1); + + /* Prepare test case state */ + setup_dup_data_state(sta); + setup_rx_pkt(param, &hdr, &mpdu_desc); + + KUNIT_EXPECT_EQ(test, + iwl_mld_is_dup(mld, sta, &hdr, &mpdu_desc, &rx_status, + 0), /* assuming only 1 queue */ + param->result.is_dup); + KUNIT_EXPECT_EQ(test, rx_status.flag, param->result.rx_status_flag); +} + +static struct kunit_case is_dup_test_cases[] = { + KUNIT_CASE_PARAM(test_is_dup, test_is_dup_gen_params), + {}, +}; + +static struct kunit_suite is_dup = { + .name = "iwlmld-rx-is-dup", + .test_cases = is_dup_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(is_dup); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c new file mode 100644 index 000000000000..a8c1e6c72138 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024-2025 Intel Corporation + */ +#include +#include + +#include "utils.h" + +#include + +#include "fw/api/scan.h" +#include "fw/api/mac-cfg.h" +#include "iwl-trans.h" +#include "mld.h" +#include "iface.h" +#include "link.h" +#include "phy.h" +#include "sta.h" + +int iwlmld_kunit_test_init(struct kunit *test) +{ + struct iwl_mld *mld; + struct iwl_trans *trans; + const struct iwl_cfg *cfg; + struct iwl_fw *fw; + struct ieee80211_hw *hw; + + KUNIT_ALLOC_AND_ASSERT(test, trans); + KUNIT_ALLOC_AND_ASSERT(test, trans->dev); + KUNIT_ALLOC_AND_ASSERT(test, cfg); + KUNIT_ALLOC_AND_ASSERT(test, fw); + KUNIT_ALLOC_AND_ASSERT(test, hw); + KUNIT_ALLOC_AND_ASSERT(test, hw->wiphy); + + mutex_init(&hw->wiphy->mtx); + + /* Allocate and initialize the mld structure */ + KUNIT_ALLOC_AND_ASSERT(test, mld); + 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; + + mld->fwrt.trans = trans; + mld->fwrt.fw = fw; + mld->fwrt.dev = trans->dev; + + /* TODO: add priv_size to hw allocation and setup hw->priv to enable + * testing mac80211 callbacks + */ + + KUNIT_ALLOC_AND_ASSERT(test, mld->nvm_data); + KUNIT_ALLOC_AND_ASSERT_SIZE(test, mld->scan.cmd, + sizeof(struct iwl_scan_req_umac_v17)); + mld->scan.cmd_size = sizeof(struct iwl_scan_req_umac_v17); + + /* This is not the state at the end of the regular opmode_start, + * but it is more common to need it. Explicitly undo this if needed. + */ + mld->trans->state = IWL_TRANS_FW_ALIVE; + mld->fw_status.running = true; + + /* Avoid passing mld struct around */ + test->priv = mld; + return 0; +} + +IWL_MLD_ALLOC_FN(link, bss_conf) + +static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct iwl_mld_link *mld_link, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + /* setup mac80211 link */ + rcu_assign_pointer(vif->link_conf[link_id], link); + link->link_id = link_id; + link->vif = vif; + link->beacon_int = 100; + link->dtim_period = 3; + link->qos = true; + + /* and mld_link */ + ret = iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); + KUNIT_ASSERT_EQ(test, ret, 0); + rcu_assign_pointer(mld_vif->link[link_id], mld_link); + rcu_assign_pointer(vif->link_conf[link_id], link); +} + +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) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + int ret; + + /* TODO: support more types */ + KUNIT_ASSERT_EQ(test, type, NL80211_IFTYPE_STATION); + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, vif, + sizeof(*vif) + sizeof(*mld_vif)); + + vif->type = type; + mld_vif = iwl_mld_vif_from_mac80211(vif); + mld_vif->mld = mld; + + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* TODO: revisit (task=EHT) */ + if (mlo) + return vif; + + /* Initialize the default link */ + iwlmld_kunit_init_link(vif, &vif->bss_conf, &mld_vif->deflink, 0); + + return vif; +} + +/* Use only for MLO vif */ +struct ieee80211_bss_conf * +iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + + KUNIT_ALLOC_AND_ASSERT(test, link); + KUNIT_ALLOC_AND_ASSERT(test, mld_link); + + iwlmld_kunit_init_link(vif, link, mld_link, link_id); + vif->valid_links |= BIT(link_id); + + return link; +} + +struct ieee80211_chanctx_conf * +iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_chanctx_conf *ctx; + struct iwl_mld_phy *phy; + int fw_id; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ctx, sizeof(*ctx) + sizeof(*phy)); + + /* Setup the chanctx conf */ + ctx->def = *def; + ctx->min_def = *def; + ctx->ap = *def; + + /* and the iwl_mld_phy */ + phy = iwl_mld_phy_from_mac80211(ctx); + + fw_id = iwl_mld_allocate_fw_phy_id(mld); + KUNIT_ASSERT_GE(test, fw_id, 0); + + phy->fw_id = fw_id; + phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); + + return ctx; +} + +void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct iwl_mld_link *mld_link; + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(link->chanctx_conf)); + rcu_assign_pointer(link->chanctx_conf, ctx); + + lockdep_assert_wiphy(mld->wiphy); + + mld_link = iwl_mld_link_from_mac80211(link); + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld_link->chan_ctx)); + KUNIT_EXPECT_FALSE(test, mld_link->active); + + rcu_assign_pointer(mld_link->chan_ctx, ctx); + mld_link->active = true; + + if (ieee80211_vif_is_mld(vif)) + vif->active_links |= BIT(link->link_id); +} + +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, + struct iwl_mld_link_sta *mld_link_sta, + u8 link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld *mld = test->priv; + u8 fw_id; + int ret; + + /* initialize mac80211's link_sta */ + link_sta->link_id = link_id; + rcu_assign_pointer(sta->link[link_id], link_sta); + + link_sta->sta = sta; + + /* and the iwl_mld_link_sta */ + ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); + KUNIT_ASSERT_EQ(test, ret, 0); + mld_link_sta->fw_id = fw_id; + + rcu_assign_pointer(mld_sta->link[link_id], mld_link_sta); +} + +static struct ieee80211_link_sta * +iwlmld_kunit_alloc_link_sta(struct ieee80211_sta *sta, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link_sta *mld_link_sta; + + /* Only valid for MLO */ + KUNIT_ASSERT_TRUE(test, sta->valid_links); + + KUNIT_ALLOC_AND_ASSERT(test, link_sta); + KUNIT_ALLOC_AND_ASSERT(test, mld_link_sta); + + iwlmld_kunit_add_link_sta(sta, link_sta, mld_link_sta, link_id); + + sta->valid_links |= BIT(link_id); + + return link_sta; +} + +/* Allocate and initialize a STA with the first link_sta */ +static struct ieee80211_sta * +iwlmld_kunit_add_sta(struct ieee80211_vif *vif, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_sta *sta; + struct iwl_mld_sta *mld_sta; + + /* Allocate memory for ieee80211_sta with embedded iwl_mld_sta */ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, sta, sizeof(*sta) + sizeof(*mld_sta)); + + /* TODO: allocate and initialize the TXQs ? */ + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta->vif = vif; + mld_sta->mld = test->priv; + + /* TODO: adjust for internal stations */ + mld_sta->sta_type = STATION_TYPE_PEER; + + if (link_id >= 0) { + iwlmld_kunit_add_link_sta(sta, &sta->deflink, + &mld_sta->deflink, link_id); + sta->valid_links = BIT(link_id); + } else { + iwlmld_kunit_add_link_sta(sta, &sta->deflink, + &mld_sta->deflink, 0); + } + return sta; +} + +/* Move s STA to a state */ +static void iwlmld_kunit_move_sta_state(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state state) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_vif *mld_vif; + + /* The sta will be removed automatically at the end of the test */ + KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta->sta_state = state; + + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + mld_vif->authorized = state == IEEE80211_STA_AUTHORIZED; + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mld_vif->ap_sta = sta; +} + +struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, + enum ieee80211_sta_state state, + int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_sta *sta; + + /* The sta will be removed automatically at the end of the test */ + KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); + + /* First - allocate and init the STA */ + sta = iwlmld_kunit_add_sta(vif, link_id); + + /* Now move it all the way to the wanted state */ + for (enum ieee80211_sta_state _state = IEEE80211_STA_NONE; + _state <= state; _state++) + iwlmld_kunit_move_sta_state(vif, sta, state); + + return sta; +} + +static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif) +{ + /* TODO: setup chanreq */ + /* TODO setup capabilities */ + + vif->cfg.assoc = 1; +} + +static struct ieee80211_vif * +iwlmld_kunit_setup_assoc(bool mlo, u8 link_id, enum nl80211_band band) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + struct ieee80211_chanctx_conf *chan_ctx; + + KUNIT_ASSERT_TRUE(test, mlo || link_id == 0); + + vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION); + + if (mlo) + link = iwlmld_kunit_add_link(vif, link_id); + else + link = &vif->bss_conf; + + chan_ctx = iwlmld_kunit_add_chanctx(band); + + wiphy_lock(mld->wiphy); + iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); + wiphy_unlock(mld->wiphy); + + /* The AP sta will now be pointer to by mld_vif->ap_sta */ + iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, link_id); + + iwlmld_kunit_set_vif_associated(vif); + + return vif; +} + +struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + u8 assoc_link_id, + enum nl80211_band band) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_vif *vif; + + KUNIT_ASSERT_TRUE(test, + hweight16(valid_links) == 1 || + hweight16(valid_links) == 2); + KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link_id)); + + vif = iwlmld_kunit_setup_assoc(true, assoc_link_id, band); + + /* Add the other link, if applicable */ + if (hweight16(valid_links) > 1) { + u8 other_link_id = ffs(valid_links & ~BIT(assoc_link_id)) - 1; + + iwlmld_kunit_add_link(vif, other_link_id); + } + + return vif; +} + +struct ieee80211_vif *iwlmld_kunit_setup_non_mlo_assoc(enum nl80211_band band) +{ + return iwlmld_kunit_setup_assoc(false, 0, band); +} + +struct iwl_rx_packet * +_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_rx_packet *pkt; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, pkt, sizeof(pkt) + notif_sz); + + memcpy(pkt->data, notif, notif_sz); + pkt->len_n_flags = cpu_to_le32(sizeof(pkt->hdr) + notif_sz); + + return pkt; +} + +struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, + enum nl80211_band band1, + enum nl80211_band band2) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + struct ieee80211_chanctx_conf *chan_ctx; + struct ieee80211_sta *sta; + struct iwl_mld_vif *mld_vif; + u8 assoc_link_id, other_link_id; + + KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2); + + assoc_link_id = ffs(valid_links) - 1; + other_link_id = ffs(valid_links & ~BIT(assoc_link_id)) - 1; + + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, assoc_link_id, band1); + mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* Activate second link */ + wiphy_lock(mld->wiphy); + + link = wiphy_dereference(mld->wiphy, vif->link_conf[other_link_id]); + KUNIT_EXPECT_NOT_NULL(test, link); + + chan_ctx = iwlmld_kunit_add_chanctx(band2); + iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); + + wiphy_unlock(mld->wiphy); + + /* And other link sta */ + sta = mld_vif->ap_sta; + KUNIT_EXPECT_NOT_NULL(test, sta); + + iwlmld_kunit_alloc_link_sta(sta, other_link_id); + + return vif; +} + +struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len) +{ + struct kunit *test = kunit_get_current_test(); + struct element *elem; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, elem, sizeof(*elem) + len); + + elem->id = id; + elem->datalen = len; + memcpy(elem->data, data, len); + + return elem; +} + +struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, + u8 link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link = + wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); + + KUNIT_EXPECT_NOT_NULL(test, link); + + chanctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + KUNIT_EXPECT_NOT_NULL(test, chanctx); + + return iwl_mld_phy_from_mac80211(chanctx); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h new file mode 100644 index 000000000000..9b1f2ada997f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __iwl_mld_kunit_utils_h__ +#define __iwl_mld_kunit_utils_h__ + +#include +#include + +struct iwl_mld; + +int iwlmld_kunit_test_init(struct kunit *test); + +enum nl80211_iftype; + +struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type); + +struct ieee80211_bss_conf * +iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id); + +#define KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, size) \ +do { \ + (ptr) = kunit_kzalloc((test), (size), GFP_KERNEL); \ + KUNIT_ASSERT_NOT_NULL((test), (ptr)); \ +} while (0) + +#define KUNIT_ALLOC_AND_ASSERT(test, ptr) \ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, sizeof(*(ptr))) + +#define CHANNEL(_name, _band, _freq) \ +static struct ieee80211_channel _name = { \ + .band = (_band), \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + +#define CHANDEF(_name, _channel, _freq1, _width) \ +__maybe_unused static struct cfg80211_chan_def _name = { \ + .chan = &(_channel), \ + .center_freq1 = (_freq1), \ + .width = (_width), \ +} + +CHANNEL(chan_2ghz, NL80211_BAND_2GHZ, 2412); +CHANNEL(chan_5ghz, NL80211_BAND_5GHZ, 5200); +CHANNEL(chan_6ghz, NL80211_BAND_6GHZ, 6115); +/* Feel free to add more */ + +CHANDEF(chandef_2ghz, chan_2ghz, 2412, NL80211_CHAN_WIDTH_20); +CHANDEF(chandef_5ghz, chan_5ghz, 5200, NL80211_CHAN_WIDTH_40); +CHANDEF(chandef_6ghz, chan_6ghz, 6115, NL80211_CHAN_WIDTH_160); +/* Feel free to add more */ + +//struct cfg80211_chan_def; + +struct ieee80211_chanctx_conf * +iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def); + +static inline struct ieee80211_chanctx_conf * +iwlmld_kunit_add_chanctx(enum nl80211_band band) +{ + struct cfg80211_chan_def *chandef; + + switch (band) { + case NL80211_BAND_2GHZ: + chandef = &chandef_2ghz; + break; + case NL80211_BAND_5GHZ: + chandef = &chandef_5ghz; + break; + default: + case NL80211_BAND_6GHZ: + chandef = &chandef_6ghz; + break; + } + + return iwlmld_kunit_add_chanctx_from_def(chandef); +} + +void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx); + +/* Allocate a sta, initialize it and move it to the wanted state */ +struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, + enum ieee80211_sta_state state, + int link_id); + +struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + u8 assoc_link_id, + enum nl80211_band band); +struct ieee80211_vif *iwlmld_kunit_setup_non_mlo_assoc(enum nl80211_band band); + +struct iwl_rx_packet * +_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz); + +#define iwl_mld_kunit_create_pkt(_notif) \ + _iwl_mld_kunit_create_pkt(&(_notif), sizeof(_notif)) + +struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, + enum nl80211_band band1, + enum nl80211_band band2); + +struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len); + +/** + * iwlmld_kunit_get_phy_of_link - Get the phy of a link + * + * @vif: The vif to get the phy from. + * @link_id: The id of the link to get the phy for. + * + * given a vif and link id, return the phy pointer of that link. + * This assumes that the link exists, and that it had a chanctx + * assigned. + * If this is not the case, the test will fail. + * + * Return: phy pointer. + */ +struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, + u8 link_id); + +#endif /* __iwl_mld_kunit_utils_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c new file mode 100644 index 000000000000..1909953a9be9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifdef CONFIG_THERMAL +#include +#include +#endif + +#include "fw/api/phy.h" + +#include "thermal.h" +#include "mld.h" +#include "hcmd.h" + +#define IWL_MLD_CT_KILL_DURATION (5 * HZ) + +void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct ct_kill_notif *notif = (const void *)pkt->data; + + IWL_ERR(mld, + "CT Kill notification: temp = %d, DTS = 0x%x, Scheme 0x%x - Enter CT Kill\n", + le16_to_cpu(notif->temperature), notif->dts, + notif->scheme); + + iwl_mld_set_ctkill(mld, true); + + wiphy_delayed_work_queue(mld->wiphy, &mld->ct_kill_exit_wk, + round_jiffies_relative(IWL_MLD_CT_KILL_DURATION)); +} + +static void iwl_mld_exit_ctkill(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld; + + mld = container_of(wk, struct iwl_mld, ct_kill_exit_wk.work); + + IWL_ERR(mld, "Exit CT Kill\n"); + iwl_mld_set_ctkill(mld, false); +} + +void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) +{ + const struct iwl_dts_measurement_notif *notif = + (const void *)pkt->data; + int temp; + u32 ths_crossed; + + temp = le32_to_cpu(notif->temp); + + /* shouldn't be negative, but since it's s32, make sure it isn't */ + if (IWL_FW_CHECK(mld, temp < 0, "negative temperature %d\n", temp)) + return; + + ths_crossed = le32_to_cpu(notif->threshold_idx); + + /* 0xFF in ths_crossed means the notification is not related + * to a trip, so we can ignore it here. + */ + if (ths_crossed == 0xFF) + return; + + IWL_DEBUG_TEMP(mld, "Temp = %d Threshold crossed = %d\n", + temp, ths_crossed); + + if (IWL_FW_CHECK(mld, ths_crossed >= IWL_MAX_DTS_TRIPS, + "bad threshold: %d\n", ths_crossed)) + return; + +#ifdef CONFIG_THERMAL + if (mld->tzone) + thermal_zone_device_update(mld->tzone, THERMAL_TRIP_VIOLATED); +#endif /* CONFIG_THERMAL */ +} + +#ifdef CONFIG_THERMAL +static int iwl_mld_get_temp(struct iwl_mld *mld, s32 *temp) +{ + struct iwl_host_cmd cmd = { + .id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + .flags = CMD_WANT_SKB, + }; + const struct iwl_dts_measurement_resp *resp; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) { + IWL_ERR(mld, + "Failed to send the temperature measurement command (err=%d)\n", + ret); + return ret; + } + + if (iwl_rx_packet_payload_len(cmd.resp_pkt) < sizeof(*resp)) { + IWL_ERR(mld, + "Failed to get a valid response to DTS measurement\n"); + ret = -EIO; + goto free_resp; + } + + resp = (const void *)cmd.resp_pkt->data; + *temp = le32_to_cpu(resp->temp); + + IWL_DEBUG_TEMP(mld, + "Got temperature measurement response: temp=%d\n", + *temp); + +free_resp: + iwl_free_resp(&cmd); + return ret; +} + +static int compare_temps(const void *a, const void *b) +{ + return ((s16)le16_to_cpu(*(__le16 *)a) - + (s16)le16_to_cpu(*(__le16 *)b)); +} + +struct iwl_trip_walk_data { + __le16 *thresholds; + int count; +}; + +static int iwl_trip_temp_iter(struct thermal_trip *trip, void *arg) +{ + struct iwl_trip_walk_data *twd = arg; + + if (trip->temperature == THERMAL_TEMP_INVALID) + return 0; + + twd->thresholds[twd->count++] = + cpu_to_le16((s16)(trip->temperature / 1000)); + return 0; +} +#endif + +int iwl_mld_config_temp_report_ths(struct iwl_mld *mld) +{ + struct temp_report_ths_cmd cmd = {0}; + int ret; +#ifdef CONFIG_THERMAL + struct iwl_trip_walk_data twd = { + .thresholds = cmd.thresholds, + .count = 0 + }; + + if (!mld->tzone) + goto send; + + /* The thermal core holds an array of temperature trips that are + * unsorted and uncompressed, the FW should get it compressed and + * sorted. + */ + + /* compress trips to cmd array, remove uninitialized values*/ + for_each_thermal_trip(mld->tzone, iwl_trip_temp_iter, &twd); + + cmd.num_temps = cpu_to_le32(twd.count); + if (twd.count) + sort(cmd.thresholds, twd.count, sizeof(s16), + compare_temps, NULL); + +send: +#endif + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, + TEMP_REPORTING_THRESHOLDS_CMD), + &cmd); + if (ret) + IWL_ERR(mld, "TEMP_REPORT_THS_CMD command failed (err=%d)\n", + ret); + + return ret; +} + +#ifdef CONFIG_THERMAL +static int iwl_mld_tzone_get_temp(struct thermal_zone_device *device, + int *temperature) +{ + struct iwl_mld *mld = thermal_zone_device_priv(device); + int temp; + int ret = 0; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + /* Tell the core that there is no valid temperature value to + * return, but it need not worry about this. + */ + *temperature = THERMAL_TEMP_INVALID; + goto unlock; + } + + ret = iwl_mld_get_temp(mld, &temp); + if (ret) + goto unlock; + + *temperature = temp * 1000; +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static int iwl_mld_tzone_set_trip_temp(struct thermal_zone_device *device, + const struct thermal_trip *trip, + int temp) +{ + struct iwl_mld *mld = thermal_zone_device_priv(device); + int ret; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + ret = -EIO; + goto unlock; + } + + if ((temp / 1000) > S16_MAX) { + ret = -EINVAL; + goto unlock; + } + + ret = iwl_mld_config_temp_report_ths(mld); +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static struct thermal_zone_device_ops tzone_ops = { + .get_temp = iwl_mld_tzone_get_temp, + .set_trip_temp = iwl_mld_tzone_set_trip_temp, +}; + +static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) +{ + int ret; + char name[16]; + static atomic_t counter = ATOMIC_INIT(0); + struct thermal_trip trips[IWL_MAX_DTS_TRIPS] = { + [0 ... IWL_MAX_DTS_TRIPS - 1] = { + .temperature = THERMAL_TEMP_INVALID, + .type = THERMAL_TRIP_PASSIVE, + .flags = THERMAL_TRIP_FLAG_RW_TEMP, + }, + }; + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF); + mld->tzone = + thermal_zone_device_register_with_trips(name, trips, + IWL_MAX_DTS_TRIPS, + mld, &tzone_ops, + NULL, 0, 0); + if (IS_ERR(mld->tzone)) { + IWL_DEBUG_TEMP(mld, + "Failed to register to thermal zone (err = %ld)\n", + PTR_ERR(mld->tzone)); + mld->tzone = NULL; + return; + } + + ret = thermal_zone_device_enable(mld->tzone); + if (ret) { + IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n"); + thermal_zone_device_unregister(mld->tzone); + } +} + +/* budget in mWatt */ +static const u32 iwl_mld_cdev_budgets[] = { + 2400, /* cooling state 0 */ + 2000, /* cooling state 1 */ + 1800, /* cooling state 2 */ + 1600, /* cooling state 3 */ + 1400, /* cooling state 4 */ + 1200, /* cooling state 5 */ + 1000, /* cooling state 6 */ + 900, /* cooling state 7 */ + 800, /* cooling state 8 */ + 700, /* cooling state 9 */ + 650, /* cooling state 10 */ + 600, /* cooling state 11 */ + 550, /* cooling state 12 */ + 500, /* cooling state 13 */ + 450, /* cooling state 14 */ + 400, /* cooling state 15 */ + 350, /* cooling state 16 */ + 300, /* cooling state 17 */ + 250, /* cooling state 18 */ + 200, /* cooling state 19 */ + 150, /* cooling state 20 */ +}; + +int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, + enum iwl_ctdp_cmd_operation op) +{ + struct iwl_ctdp_cmd cmd = { + .operation = cpu_to_le32(op), + .budget = cpu_to_le32(iwl_mld_cdev_budgets[state]), + .window_size = 0, + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), + &cmd); + + if (ret) { + IWL_ERR(mld, "cTDP command failed (err=%d)\n", ret); + return ret; + } + + if (op == CTDP_CMD_OPERATION_START) + mld->cooling_dev.cur_state = state; + + return 0; +} + +static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ARRAY_SIZE(iwl_mld_cdev_budgets) - 1; + + return 0; +} + +static int iwl_mld_tcool_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); + + *state = mld->cooling_dev.cur_state; + + return 0; +} + +static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long new_state) +{ + struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); + int ret; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + ret = -EIO; + goto unlock; + } + + if (new_state >= ARRAY_SIZE(iwl_mld_cdev_budgets)) { + ret = -EINVAL; + goto unlock; + } + + ret = iwl_mld_config_ctdp(mld, new_state, CTDP_CMD_OPERATION_START); + +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static const struct thermal_cooling_device_ops tcooling_ops = { + .get_max_state = iwl_mld_tcool_get_max_state, + .get_cur_state = iwl_mld_tcool_get_cur_state, + .set_cur_state = iwl_mld_tcool_set_cur_state, +}; + +static void iwl_mld_cooling_device_register(struct iwl_mld *mld) +{ + char name[] = "iwlwifi"; + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + mld->cooling_dev.cdev = + thermal_cooling_device_register(name, + mld, + &tcooling_ops); + + if (IS_ERR(mld->cooling_dev.cdev)) { + IWL_DEBUG_TEMP(mld, + "Failed to register to cooling device (err = %ld)\n", + PTR_ERR(mld->cooling_dev.cdev)); + mld->cooling_dev.cdev = NULL; + return; + } +} + +static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld) +{ + if (!mld->tzone) + return; + + IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n"); + if (mld->tzone) { + thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; + } +} + +static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) +{ + if (!mld->cooling_dev.cdev) + return; + + IWL_DEBUG_TEMP(mld, "Cooling device unregister\n"); + if (mld->cooling_dev.cdev) { + thermal_cooling_device_unregister(mld->cooling_dev.cdev); + mld->cooling_dev.cdev = NULL; + } +} +#endif /* CONFIG_THERMAL */ + +void iwl_mld_thermal_initialize(struct iwl_mld *mld) +{ + wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill); + +#ifdef CONFIG_THERMAL + iwl_mld_cooling_device_register(mld); + iwl_mld_thermal_zone_register(mld); +#endif +} + +void iwl_mld_thermal_exit(struct iwl_mld *mld) +{ + wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk); + +#ifdef CONFIG_THERMAL + iwl_mld_cooling_device_unregister(mld); + iwl_mld_thermal_zone_unregister(mld); +#endif +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.h b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h new file mode 100644 index 000000000000..8c8893331b05 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_thermal_h__ +#define __iwl_mld_thermal_h__ + +#include "iwl-trans.h" + +struct iwl_mld; + +#ifdef CONFIG_THERMAL +#include + +/* + * struct iwl_mld_cooling_device + * @cur_state: current state + * @cdev: struct thermal cooling device + */ +struct iwl_mld_cooling_device { + u32 cur_state; + struct thermal_cooling_device *cdev; +}; + +int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, + enum iwl_ctdp_cmd_operation op); +#endif + +void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +int iwl_mld_config_temp_report_ths(struct iwl_mld *mld); +void iwl_mld_thermal_initialize(struct iwl_mld *mld); +void iwl_mld_thermal_exit(struct iwl_mld *mld); + +#endif /* __iwl_mld_thermal_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c new file mode 100644 index 000000000000..50799f9bfccb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ + +#include "mld.h" +#include "hcmd.h" +#include "ptp.h" +#include "time_sync.h" +#include + +static int iwl_mld_init_time_sync(struct iwl_mld *mld, u32 protocols, + const u8 *addr) +{ + struct iwl_mld_time_sync_data *time_sync = kzalloc(sizeof(*time_sync), + GFP_KERNEL); + + if (!time_sync) + return -ENOMEM; + + time_sync->active_protocols = protocols; + ether_addr_copy(time_sync->peer_addr, addr); + skb_queue_head_init(&time_sync->frame_list); + rcu_assign_pointer(mld->time_sync, time_sync); + + return 0; +} + +int iwl_mld_time_sync_fw_config(struct iwl_mld *mld) +{ + struct iwl_time_sync_cfg_cmd cmd = {}; + struct iwl_mld_time_sync_data *time_sync; + int err; + + time_sync = wiphy_dereference(mld->wiphy, mld->time_sync); + if (!time_sync) + return -EINVAL; + + cmd.protocols = cpu_to_le32(time_sync->active_protocols); + ether_addr_copy(cmd.peer_addr, time_sync->peer_addr); + + err = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), + &cmd); + if (err) + IWL_ERR(mld, "Failed to send time sync cfg cmd: %d\n", err); + + return err; +} + +int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, u32 protocols) +{ + struct iwl_mld_time_sync_data *time_sync; + int err; + + time_sync = wiphy_dereference(mld->wiphy, mld->time_sync); + + /* The fw only supports one peer. We do allow reconfiguration of the + * same peer for cases of fw reset etc. + */ + if (time_sync && time_sync->active_protocols && + !ether_addr_equal(addr, time_sync->peer_addr)) { + IWL_DEBUG_INFO(mld, "Time sync: reject config for peer: %pM\n", + addr); + return -ENOBUFS; + } + + if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM | + IWL_TIME_SYNC_PROTOCOL_FTM)) + return -EINVAL; + + IWL_DEBUG_INFO(mld, "Time sync: set peer addr=%pM\n", addr); + + iwl_mld_deinit_time_sync(mld); + err = iwl_mld_init_time_sync(mld, protocols, addr); + if (err) + return err; + + err = iwl_mld_time_sync_fw_config(mld); + return err; +} + +void iwl_mld_deinit_time_sync(struct iwl_mld *mld) +{ + struct iwl_mld_time_sync_data *time_sync = + wiphy_dereference(mld->wiphy, mld->time_sync); + + if (!time_sync) + return; + + RCU_INIT_POINTER(mld->time_sync, NULL); + skb_queue_purge(&time_sync->frame_list); + kfree_rcu(time_sync, rcu_head); +} + +bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, u8 *addr) +{ + struct iwl_mld_time_sync_data *time_sync; + + rcu_read_lock(); + time_sync = rcu_dereference(mld->time_sync); + if (time_sync && ether_addr_equal(time_sync->peer_addr, addr) && + (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) { + skb_queue_tail(&time_sync->frame_list, skb); + rcu_read_unlock(); + return true; + } + rcu_read_unlock(); + + return false; +} + +static bool iwl_mld_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 skb_dialog_token; + + if (ieee80211_is_timing_measurement(skb)) + skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token; + else + skb_dialog_token = mgmt->u.action.u.ftm.dialog_token; + + if ((ether_addr_equal(mgmt->sa, addr) || + ether_addr_equal(mgmt->da, addr)) && + skb_dialog_token == dialog_token) + return true; + + return false; +} + +static struct sk_buff *iwl_mld_time_sync_find_skb(struct iwl_mld *mld, u8 *addr, + u8 dialog_token) +{ + struct iwl_mld_time_sync_data *time_sync; + struct sk_buff *skb; + + rcu_read_lock(); + + time_sync = rcu_dereference(mld->time_sync); + if (IWL_FW_CHECK(mld, !time_sync, + "Time sync notification but time sync is not initialized\n")) { + rcu_read_unlock(); + return NULL; + } + + /* The notifications are expected to arrive in the same order of the + * frames. If the incoming notification doesn't match the first SKB + * in the queue, it means there was no time sync notification for this + * SKB and it can be dropped. + */ + while ((skb = skb_dequeue(&time_sync->frame_list))) { + if (iwl_mld_is_skb_match(skb, addr, dialog_token)) + break; + + kfree_skb(skb); + skb = NULL; + IWL_DEBUG_DROP(mld, + "Time sync: drop SKB without matching notification\n"); + } + rcu_read_unlock(); + + return skb; +} + +static u64 iwl_mld_get_64_bit(__le32 high, __le32 low) +{ + return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low); +} + +void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ptp_data *data = &mld->ptp_data; + struct iwl_time_msmt_notify *notif = (void *)pkt->data; + struct ieee80211_rx_status *rx_status; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns; + struct sk_buff *skb = + iwl_mld_time_sync_find_skb(mld, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + u64 adj_time; + + if (IWL_FW_CHECK(mld, !skb, "Time sync event but no pending skb\n")) + return; + + spin_lock_bh(&data->lock); + ts_10ns = iwl_mld_get_64_bit(notif->t2_hi, notif->t2_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + shwt = skb_hwtstamps(skb); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mld_get_64_bit(notif->t3_hi, notif->t3_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_INFO(mld, + "Time sync: RX event - report frame t2=%llu t3=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(rx_status->ack_tx_hwtstamp)); + ieee80211_rx_napi(mld->hw, NULL, skb, NULL); +} + +void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ptp_data *data = &mld->ptp_data; + struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data; + struct ieee80211_tx_status status = {}; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns, adj_time; + + status.skb = + iwl_mld_time_sync_find_skb(mld, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + + if (IWL_FW_CHECK(mld, !status.skb, + "Time sync confirm but no pending skb\n")) + return; + + spin_lock_bh(&data->lock); + ts_10ns = iwl_mld_get_64_bit(notif->t1_hi, notif->t1_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + shwt = skb_hwtstamps(status.skb); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mld_get_64_bit(notif->t4_hi, notif->t4_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + status.info = IEEE80211_SKB_CB(status.skb); + status.ack_hwtstamp = ktime_set(0, adj_time); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_INFO(mld, + "Time sync: TX event - report frame t1=%llu t4=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(status.ack_hwtstamp)); + ieee80211_tx_status_ext(mld->hw, &status); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h new file mode 100644 index 000000000000..2d4c5512e961 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_time_sync_h__ +#define __iwl_mld_time_sync_h__ + +struct iwl_mld_time_sync_data { + struct rcu_head rcu_head; + u8 peer_addr[ETH_ALEN]; + u32 active_protocols; + struct sk_buff_head frame_list; +}; + +int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, + u32 protocols); +int iwl_mld_time_sync_fw_config(struct iwl_mld *mld); +void iwl_mld_deinit_time_sync(struct iwl_mld *mld); +void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, + u8 *addr); +void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_time_sync_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c new file mode 100644 index 000000000000..85ec358f1417 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ + +#include + +#include "tlc.h" +#include "hcmd.h" +#include "sta.h" + +#include "fw/api/rs.h" +#include "fw/api/context.h" +#include "fw/api/dhc.h" + +static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta) +{ + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_320: + return IWL_TLC_MNG_CH_WIDTH_320MHZ; + case IEEE80211_STA_RX_BW_160: + return IWL_TLC_MNG_CH_WIDTH_160MHZ; + case IEEE80211_STA_RX_BW_80: + return IWL_TLC_MNG_CH_WIDTH_80MHZ; + case IEEE80211_STA_RX_BW_40: + return IWL_TLC_MNG_CH_WIDTH_40MHZ; + case IEEE80211_STA_RX_BW_20: + default: + return IWL_TLC_MNG_CH_WIDTH_20MHZ; + } +} + +static __le16 +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) +{ + struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + bool has_vht = vht_cap->vht_supported; + u16 flags = 0; + + /* STBC flags */ + if (mld->cfg->ht_params->stbc && + (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { + if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + } + + /* LDPC */ + if (mld->cfg->ht_params->ldpc && + ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || + (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) + flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + if (own_he_cap && + !(own_he_cap->he_cap_elem.phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + /* DCM */ + if (he_cap->has_he && + (he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && + own_he_cap && + own_he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) + flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; + + /* Extra EHT LTF */ + if (own_eht_cap && + own_eht_cap->eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && + link_sta->eht_cap.has_eht && + link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) { + flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK; + } + + return cpu_to_le16(flags); +} + +static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld) +{ + u8 chains = iwl_mld_get_valid_tx_ant(mld); + u8 fw_chains = 0; + + if (chains & ANT_A) + fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; + if (chains & ANT_B) + fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; + + return fw_chains; +} + +static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + u8 sgi_chwidths = 0; + + /* If the association supports HE, HT/VHT rates will never be used for + * Tx and therefor there's no need to set the + * sgi-per-channel-width-support bits + */ + if (he_cap->has_he) + return 0; + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); + if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); + if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); + + return sgi_chwidths; +} + +static int +iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap, + int nss) +{ + u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & + (0x3 << (2 * (nss - 1))); + rx_mcs >>= (2 * (nss - 1)); + + switch (rx_mcs) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: + return IWL_TLC_MNG_HT_RATE_MCS7; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + return IWL_TLC_MNG_HT_RATE_MCS8; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + return IWL_TLC_MNG_HT_RATE_MCS9; + default: + WARN_ON_ONCE(1); + break; + } + + return 0; +} + +static void +iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + u16 supp; + int i, highest_mcs; + u8 max_nss = link_sta->rx_nss; + struct ieee80211_vht_cap ieee_vht_cap = { + .vht_cap_info = cpu_to_le32(vht_cap->cap), + .supp_mcs = vht_cap->vht_mcs, + }; + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + max_nss = 1; + + for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { + int nss = i + 1; + + highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss); + if (!highest_mcs) + continue; + + supp = BIT(highest_mcs + 1) - 1; + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) + supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); + + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp); + /* Check if VHT extended NSS indicates that the bandwidth/NSS + * configuration is supported - only for MCS 0 since we already + * decoded the MCS bits anyway ourselves. + */ + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 && + ieee80211_get_vht_max_nss(&ieee_vht_cap, + IEEE80211_VHT_CHANWIDTH_160MHZ, + 0, true, nss) >= nss) + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80]; + } +} + +static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs) +{ + switch (mcs) { + case IEEE80211_HE_MCS_SUPPORT_0_7: + return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1; + case IEEE80211_HE_MCS_SUPPORT_0_9: + return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1; + case IEEE80211_HE_MCS_SUPPORT_0_11: + return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1; + case IEEE80211_HE_MCS_NOT_SUPPORTED: + return 0; + } + + WARN(1, "invalid HE MCS %d\n", mcs); + return 0; +} + +static void +iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_he_cap *own_he_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80); + u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160); + int i; + u8 nss = link_sta->rx_nss; + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + nss = 1; + + for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { + u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3; + u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3; + u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3; + u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3; + + /* If one side doesn't support - mark both as not supporting */ + if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED || + _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) { + _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + } + if (_mcs_80 > _tx_mcs_80) + _mcs_80 = _tx_mcs_80; + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80)); + + /* If one side doesn't support - mark both as not supporting */ + if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED || + _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) { + _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; + _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; + } + if (_mcs_160 > _tx_mcs_160) + _mcs_160 = _tx_mcs_160; + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = + cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160)); + } +} + +static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3], + enum IWL_TLC_MCS_PER_BW bw, + u8 max_nss, u16 mcs_msk) +{ + if (max_nss >= 2) + ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk); + + if (max_nss >= 1) + ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk); +} + +static const +struct ieee80211_eht_mcs_nss_supp_bw * +iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw, + const struct ieee80211_eht_mcs_nss_supp *eht_mcs) +{ + switch (bw) { + case IWL_TLC_MCS_PER_BW_80: + return &eht_mcs->bw._80; + case IWL_TLC_MCS_PER_BW_160: + return &eht_mcs->bw._160; + case IWL_TLC_MCS_PER_BW_320: + return &eht_mcs->bw._320; + default: + return NULL; + } +} + +static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss) +{ + u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX); + u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX); + /* the max nss that can be used, + * is the min with our tx capa and the peer rx capa. + */ + return min(tx, rx); +} + +#define MAX_NSS_MCS(mcs_num, rx, tx) \ + iwl_mld_get_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \ + (tx)->rx_tx_mcs ##mcs_num## _max_nss) + +static void +iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, + const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_he_cap *own_he_cap, + const struct ieee80211_sta_eht_cap *own_eht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + /* peer RX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = + &link_sta->eht_cap.eht_mcs_nss_supp; + /* our TX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = + &own_eht_cap->eht_mcs_nss_supp; + + enum IWL_TLC_MCS_PER_BW bw; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20; + + /* peer is 20 MHz only */ + if (vif->type == NL80211_IFTYPE_AP && + !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_rx_20 = eht_rx_mcs->only_20mhz; + } else { + mcs_rx_20.rx_tx_mcs7_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs9_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs11_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_rx_20.rx_tx_mcs13_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* NIC is capable of 20 MHz only */ + if (!(own_he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_tx_20 = eht_tx_mcs->only_20mhz; + } else { + mcs_tx_20.rx_tx_mcs7_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs9_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs11_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_tx_20.rx_tx_mcs13_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* rates for 20/40/80 MHz */ + bw = IWL_TLC_MCS_PER_BW_80; + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), + GENMASK(7, 0)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), + GENMASK(9, 8)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), + GENMASK(11, 10)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), + GENMASK(13, 12)); + + /* rates for 160/320 MHz */ + for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) { + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx = + iwl_mld_get_eht_mcs_of_bw(bw, eht_rx_mcs); + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = + iwl_mld_get_eht_mcs_of_bw(bw, eht_tx_mcs); + + /* got unsupported index for bw */ + if (!mcs_rx || !mcs_tx) + continue; + + /* break out if we don't support the bandwidth */ + if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ)) + break; + + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(9, mcs_rx, mcs_tx), + GENMASK(9, 0)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(11, mcs_rx, mcs_tx), + GENMASK(11, 10)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(13, mcs_rx, mcs_tx), + GENMASK(13, 12)); + } + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC || + link_sta->rx_nss < 2) + memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, + sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); +} + +static void +iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct ieee80211_supported_band *sband, + const struct ieee80211_sta_he_cap *own_he_cap, + const struct ieee80211_sta_eht_cap *own_eht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + int i; + u16 non_ht_rates = 0; + unsigned long rates_bitmap; + const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + + /* non HT rates */ + rates_bitmap = link_sta->supp_rates[sband->band]; + for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG) + non_ht_rates |= BIT(sband->bitrates[i].hw_value); + + 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) { + cmd->mode = IWL_TLC_MNG_MODE_EHT; + iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, + own_eht_cap, cmd); + } else if (he_cap->has_he && own_he_cap) { + cmd->mode = IWL_TLC_MNG_MODE_HE; + iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd); + } else if (vht_cap->vht_supported) { + cmd->mode = IWL_TLC_MNG_MODE_VHT; + iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd); + } else if (ht_cap->ht_supported) { + cmd->mode = IWL_TLC_MNG_MODE_HT; + cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(ht_cap->mcs.rx_mask[0]); + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = + 0; + else + cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(ht_cap->mcs.rx_mask[1]); + } +} + +static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + enum nl80211_band band) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + 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); + struct iwl_tlc_config_cmd_v4 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), + .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_rc_amsdu_len), + }; + int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + int ret; + + if (fw_sta_id < 0) + return; + + cmd.sta_id = fw_sta_id; + + iwl_mld_fill_supp_rates(mld, vif, link_sta, sband, + own_he_cap, own_eht_cap, + &cmd); + + IWL_DEBUG_RATE(mld, + "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n", + cmd.sta_id, cmd.max_ch_width, cmd.mode); + + /* Send async since this can be called within a RCU-read section */ + ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP, + TLC_MNG_CONFIG_CMD), + CMD_ASYNC, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret); +} + +static void iwl_mld_recalc_amsdu_len(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + + /* For EHT, HE and VHT - we can use the value as it was calculated by + * mac80211. + */ + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he || + link_sta->vht_cap.vht_supported) + goto recalc; + + /* But for HT, mac80211 doesn't enforce to 4095, so force it here */ + if (ht_cap->ht_supported && ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU) + /* Agg is offloaded, so we need to assume that agg are enabled + * and max mpdu in ampdu is 4095 (spec 802.11-2016 9.3.2.1) + */ + link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; + +recalc: + link_sta->agg.max_rc_amsdu_len = link_sta->agg.max_amsdu_len; + ieee80211_sta_recalc_aggregates(link_sta->sta); +} + +int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data) +{ + struct { + struct iwl_dhc_cmd dhc; + struct iwl_dhc_tlc_cmd tlc; + } __packed cmd = { + .tlc.sta_id = sta_id, + .tlc.type = cpu_to_le32(type), + .tlc.data[0] = cpu_to_le32(data), + .dhc.length = cpu_to_le32(sizeof(cmd.tlc) >> 2), + .dhc.index_and_mask = + cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | + DHC_INTEGRATION_TLC_DEBUG_CONFIG), + }; + int ret; + + ret = iwl_mld_send_cmd_with_flags_pdu(mld, + WIDE_ID(IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND), + CMD_ASYNC, &cmd); + IWL_DEBUG_RATE(mld, "sta_id %d, type: 0x%X, value: 0x%X, ret%d\n", + sta_id, type, data, ret); + return ret; +} + +void iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) +{ + enum nl80211_band band; + + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) + return; + + band = link_conf->chanreq.oper.chan->band; + + iwl_mld_recalc_amsdu_len(mld, link_sta); + + iwl_mld_send_tlc_cmd(mld, vif, link_sta, band); +} + +void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ieee80211_bss_conf *link; + int link_id; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_vif_active_link(vif, link, link_id) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_check(sta, link_id); + + if (!link || !link_sta) + continue; + + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + } +} + +static u16 +iwl_mld_get_amsdu_size_of_tid(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + unsigned int tid) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif; + const u8 tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, + }; + unsigned int result = link_sta->agg.max_rc_amsdu_len; + u8 ac, txf, lmac; + + lockdep_assert_wiphy(mld->wiphy); + + /* Don't send an AMSDU that will be longer than the TXF. + * Add a security margin of 256 for the TX command + headers. + * We also want to have the start of the next packet inside the + * fifo to be able to send bursts. + */ + + if (WARN_ON(tid >= ARRAY_SIZE(tid_to_mac80211_ac))) + return 0; + + ac = tid_to_mac80211_ac[tid]; + + /* For HE redirect to trigger based fifos */ + if (link_sta->he_cap.has_he) + ac += 4; + + txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(ac); + + /* Only one link: take the lmac according to the band */ + if (hweight16(sta->valid_links) <= 1) { + enum nl80211_band band; + struct ieee80211_bss_conf *link = + wiphy_dereference(mld->wiphy, + vif->link_conf[link_sta->link_id]); + + if (WARN_ON(!link || !link->chanreq.oper.chan)) + band = NL80211_BAND_2GHZ; + else + band = link->chanreq.oper.chan->band; + lmac = iwl_mld_get_lmac_id(mld, band); + + /* More than one link but with 2 lmacs: take the minimum */ + } else if (fw_has_capa(&mld->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) { + lmac = IWL_LMAC_5G_INDEX; + result = min_t(unsigned int, result, + mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); + lmac = IWL_LMAC_24G_INDEX; + /* More than one link but only one lmac */ + } else { + lmac = IWL_LMAC_24G_INDEX; + } + + return min_t(unsigned int, result, + mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); +} + +void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tlc_update_notif *notif = (void *)pkt->data; + struct ieee80211_link_sta *link_sta; + u32 flags = le32_to_cpu(notif->flags); + u32 enabled; + u16 size; + + if (IWL_FW_CHECK(mld, notif->sta_id >= mld->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in TLC notification\n", + notif->sta_id)) + return; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[notif->sta_id]); + + if (WARN(IS_ERR_OR_NULL(link_sta), + "link_sta of sta id (%d) doesn't exist\n", notif->sta_id)) + return; + + if (flags & IWL_TLC_NOTIF_FLAG_RATE) { + struct iwl_mld_link_sta *mld_link_sta = + iwl_mld_link_sta_from_mac80211(link_sta); + char pretty_rate[100]; + + if (WARN_ON(!mld_link_sta)) + return; + + mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate); + + rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), + mld_link_sta->last_rate_n_flags); + IWL_DEBUG_RATE(mld, "TLC notif: new rate = %s\n", pretty_rate); + } + + /* We are done processing the notif */ + if (!(flags & IWL_TLC_NOTIF_FLAG_AMSDU)) + return; + + enabled = le32_to_cpu(notif->amsdu_enabled); + size = le32_to_cpu(notif->amsdu_size); + + if (size < 2000) { + size = 0; + enabled = 0; + } + + if (IWL_FW_CHECK(mld, size > link_sta->agg.max_amsdu_len, + "Invalid AMSDU len in TLC notif: %d (Max AMSDU len: %d)\n", + size, link_sta->agg.max_amsdu_len)) + return; + + link_sta->agg.max_rc_amsdu_len = size; + + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + if (enabled & BIT(i)) + link_sta->agg.max_tid_amsdu_len[i] = + iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i); + else + link_sta->agg.max_tid_amsdu_len[i] = 0; + } + + ieee80211_sta_recalc_aggregates(link_sta->sta); + + IWL_DEBUG_RATE(mld, + "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", + le32_to_cpu(notif->amsdu_size), size, enabled); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h new file mode 100644 index 000000000000..c32f42e8840b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_tlc_h__ +#define __iwl_mld_tlc_h__ + +#include "mld.h" + +void iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta); + +void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data); + +#endif /* __iwl_mld_tlc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c new file mode 100644 index 000000000000..543abe72e465 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -0,0 +1,1374 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 - 2025 Intel Corporation + */ +#include + +#include "tx.h" +#include "sta.h" +#include "hcmd.h" +#include "iwl-utils.h" +#include "iface.h" + +#include "fw/dbg.h" + +#include "fw/api/tx.h" +#include "fw/api/rs.h" +#include "fw/api/txq.h" +#include "fw/api/datapath.h" +#include "fw/api/time-event.h" + +#define MAX_ANT_NUM 2 + +/* Toggles between TX antennas. Receives the bitmask of valid TX antennas and + * the *index* used for the last TX, and returns the next valid *index* to use. + * In order to set it in the tx_cmd, must do BIT(idx). + */ +static u8 iwl_mld_next_ant(u8 valid, u8 last_idx) +{ + u8 index = last_idx; + + for (int i = 0; i < MAX_ANT_NUM; i++) { + index = (index + 1) % MAX_ANT_NUM; + if (valid & BIT(index)) + return index; + } + + WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); + + return last_idx; +} + +void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant) +{ + *ant = iwl_mld_next_ant(iwl_mld_get_valid_tx_ant(mld), *ant); +} + +static int +iwl_mld_get_queue_size(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct ieee80211_sta *sta = txq->sta; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + int max_size = IWL_DEFAULT_QUEUE_SIZE; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_sta_active_link(txq->vif, sta, link_sta, link_id) { + if (link_sta->eht_cap.has_eht) { + max_size = IWL_DEFAULT_QUEUE_SIZE_EHT; + break; + } + + if (link_sta->he_cap.has_he) + max_size = IWL_DEFAULT_QUEUE_SIZE_HE; + } + + return max_size; +} + +static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + u8 tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : txq->tid; + u32 fw_sta_mask = iwl_mld_fw_sta_id_mask(mld, txq->sta); + /* We can't know when the station is asleep or awake, so we + * must disable the queue hang detection. + */ + unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ? + IWL_WATCHDOG_DISABLED : + mld->trans->trans_cfg->base_params->wd_timeout; + int queue, size; + + lockdep_assert_wiphy(mld->wiphy); + + if (tid == IWL_MGMT_TID) + size = max_t(u32, IWL_MGMT_QUEUE_SIZE, + mld->trans->cfg->min_txq_size); + else + size = iwl_mld_get_queue_size(mld, txq); + + queue = iwl_trans_txq_alloc(mld->trans, 0, fw_sta_mask, tid, size, + watchdog_timeout); + + if (queue >= 0) + IWL_DEBUG_TX_QUEUES(mld, + "Enabling TXQ #%d for sta mask 0x%x tid %d\n", + queue, fw_sta_mask, tid); + return queue; +} + +static int iwl_mld_add_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + int id; + + lockdep_assert_wiphy(mld->wiphy); + + /* This will alse send the SCD_QUEUE_CONFIG_CMD */ + id = iwl_mld_allocate_txq(mld, txq); + if (id < 0) + return id; + + mld_txq->fw_id = id; + mld_txq->status.allocated = true; + + rcu_assign_pointer(mld->fw_id_to_txq[id], txq); + + return 0; +} + +void iwl_mld_add_txq_list(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + while (!list_empty(&mld->txqs_to_add)) { + struct ieee80211_txq *txq; + struct iwl_mld_txq *mld_txq = + list_first_entry(&mld->txqs_to_add, struct iwl_mld_txq, + list); + int failed; + + txq = container_of((void *)mld_txq, struct ieee80211_txq, + drv_priv); + + failed = iwl_mld_add_txq(mld, txq); + + local_bh_disable(); + spin_lock(&mld->add_txqs_lock); + list_del_init(&mld_txq->list); + spin_unlock(&mld->add_txqs_lock); + /* If the queue allocation failed, we can't transmit. Leave the + * frames on the txq, maybe the attempt to allocate the queue + * will succeed. + */ + if (!failed) + iwl_mld_tx_from_txq(mld, txq); + local_bh_enable(); + } +} + +void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + add_txqs_wk); + + /* will reschedule to run after restart */ + if (mld->fw_status.in_hw_restart) + return; + + iwl_mld_add_txq_list(mld); +} + +void +iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id) +{ + struct iwl_scd_queue_cfg_cmd remove_cmd = { + .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), + .u.remove.tid = cpu_to_le32(tid), + .u.remove.sta_mask = cpu_to_le32(fw_sta_mask), + }; + + iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD), + &remove_cmd); + + iwl_trans_txq_free(mld->trans, queue_id); +} + +void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + u32 sta_msk, tid; + + lockdep_assert_wiphy(mld->wiphy); + + spin_lock_bh(&mld->add_txqs_lock); + if (!list_empty(&mld_txq->list)) + list_del_init(&mld_txq->list); + spin_unlock_bh(&mld->add_txqs_lock); + + if (!mld_txq->status.allocated || + WARN_ON(mld_txq->fw_id >= ARRAY_SIZE(mld->fw_id_to_txq))) + return; + + sta_msk = iwl_mld_fw_sta_id_mask(mld, txq->sta); + + tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : + txq->tid; + + iwl_mld_free_txq(mld, sta_msk, tid, mld_txq->fw_id); + + RCU_INIT_POINTER(mld->fw_id_to_txq[mld_txq->fw_id], NULL); + mld_txq->status.allocated = false; +} + +#define OPT_HDR(type, skb, off) \ + (type *)(skb_network_header(skb) + (off)) + +static __le32 +iwl_mld_get_offload_assist(struct sk_buff *skb, bool amsdu) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u16 mh_len = ieee80211_hdrlen(hdr->frame_control); + u16 offload_assist = 0; +#if IS_ENABLED(CONFIG_INET) + u8 protocol = 0; + + /* Do not compute checksum if already computed */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + goto out; + + /* We do not expect to be requested to csum stuff we do not support */ + + /* TBD: do we also need to check + * !(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) now that all + * the devices we support has this flags? + */ + if (WARN_ONCE(skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6), + "No support for requested checksum\n")) { + skb_checksum_help(skb); + goto out; + } + + if (skb->protocol == htons(ETH_P_IP)) { + protocol = ip_hdr(skb)->protocol; + } else { +#if IS_ENABLED(CONFIG_IPV6) + struct ipv6hdr *ipv6h = + (struct ipv6hdr *)skb_network_header(skb); + unsigned int off = sizeof(*ipv6h); + + protocol = ipv6h->nexthdr; + while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) { + struct ipv6_opt_hdr *hp; + + /* only supported extension headers */ + if (protocol != NEXTHDR_ROUTING && + protocol != NEXTHDR_HOP && + protocol != NEXTHDR_DEST) { + skb_checksum_help(skb); + goto out; + } + + hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); + protocol = hp->nexthdr; + off += ipv6_optlen(hp); + } + /* if we get here - protocol now should be TCP/UDP */ +#endif + } + + if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { + WARN_ON_ONCE(1); + skb_checksum_help(skb); + goto out; + } + + /* enable L4 csum */ + offload_assist |= BIT(TX_CMD_OFFLD_L4_EN); + + /* Set offset to IP header (snap). + * We don't support tunneling so no need to take care of inner header. + * Size is in words. + */ + offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR); + + /* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */ + if (skb->protocol == htons(ETH_P_IP) && amsdu) { + ip_hdr(skb)->check = 0; + offload_assist |= BIT(TX_CMD_OFFLD_L3_EN); + } + + /* reset UDP/TCP header csum */ + if (protocol == IPPROTO_TCP) + tcp_hdr(skb)->check = 0; + else + udp_hdr(skb)->check = 0; + +out: +#endif + mh_len /= 2; + offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE; + + if (amsdu) + offload_assist |= BIT(TX_CMD_OFFLD_AMSDU); + else if (ieee80211_hdrlen(hdr->frame_control) % 4) + /* padding is inserted later in transport */ + offload_assist |= BIT(TX_CMD_OFFLD_PAD); + + return cpu_to_le32(offload_assist); +} + +static void iwl_mld_get_basic_rates_and_band(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_tx_info *info, + unsigned long *basic_rates, + u8 *band) +{ + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + *basic_rates = vif->bss_conf.basic_rates; + *band = info->band; + + if (link_id == IEEE80211_LINK_UNSPECIFIED && + ieee80211_vif_is_mld(vif)) { + /* shouldn't do this when >1 link is active */ + WARN_ON(hweight16(vif->active_links) != 1); + link_id = __ffs(vif->active_links); + } + + if (link_id < IEEE80211_LINK_UNSPECIFIED) { + struct ieee80211_bss_conf *link_conf; + + rcu_read_lock(); + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (link_conf) { + *basic_rates = link_conf->basic_rates; + if (link_conf->chanreq.oper.chan) + *band = link_conf->chanreq.oper.chan->band; + } + rcu_read_unlock(); + } +} + +u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband; + u16 lowest_cck = IWL_RATE_COUNT, lowest_ofdm = IWL_RATE_COUNT; + unsigned long basic_rates; + u8 band, rate; + u32 i; + + iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band); + + sband = mld->hw->wiphy->bands[band]; + for_each_set_bit(i, &basic_rates, BITS_PER_LONG) { + u16 hw = sband->bitrates[i].hw_value; + + if (hw >= IWL_FIRST_OFDM_RATE) { + if (lowest_ofdm > hw) + lowest_ofdm = hw; + } else if (lowest_cck > hw) { + lowest_cck = hw; + } + } + + if (band == NL80211_BAND_2GHZ && !vif->p2p && + vif->type != NL80211_IFTYPE_P2P_DEVICE && + !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) { + if (lowest_cck != IWL_RATE_COUNT) + rate = lowest_cck; + else if (lowest_ofdm != IWL_RATE_COUNT) + rate = lowest_ofdm; + else + rate = IWL_FIRST_CCK_RATE; + } else if (lowest_ofdm != IWL_RATE_COUNT) { + rate = lowest_ofdm; + } else { + rate = IWL_FIRST_OFDM_RATE; + } + + return rate; +} + +static u32 iwl_mld_mac80211_rate_idx_to_fw(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + int rate_idx) +{ + u32 rate_flags = 0; + u8 rate_plcp; + + /* if the rate isn't a well known legacy rate, take the lowest one */ + if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY) + rate_idx = iwl_mld_get_lowest_rate(mld, info, + info->control.vif); + + WARN_ON_ONCE(rate_idx < 0); + + /* Set CCK or OFDM flag */ + if (rate_idx <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + else + rate_flags |= RATE_MCS_LEGACY_OFDM_MSK; + + /* Legacy rates are indexed: + * 0 - 3 for CCK and 0 - 7 for OFDM + */ + rate_plcp = (rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : rate_idx); + + return (u32)rate_plcp | rate_flags; +} + +static u32 iwl_mld_get_tx_ant(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) +{ + if (sta && ieee80211_is_data(fc)) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + return BIT(mld_sta->data_tx_ant) << RATE_MCS_ANT_POS; + } + + return BIT(mld->mgmt_tx_ant) << RATE_MCS_ANT_POS; +} + +static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + __le16 fc) +{ + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + u32 result; + + /* we only care about legacy/HT/VHT so far, so we can + * build in v1 and use iwl_new_rate_from_v1() + * FIXME: in newer devices we only support the new rates, build + * the rate_n_flags in the new format here instead of using v1 and + * converting it. + */ + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + u8 mcs = ieee80211_rate_get_vht_mcs(rate); + u8 nss = ieee80211_rate_get_vht_nss(rate); + + result = RATE_MCS_VHT_MSK_V1; + result |= u32_encode_bits(mcs, RATE_VHT_MCS_RATE_CODE_MSK); + result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + result |= RATE_MCS_SGI_MSK_V1; + + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1); + else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1); + + result = iwl_new_rate_from_v1(result); + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + result = RATE_MCS_HT_MSK_V1; + result |= u32_encode_bits(rate->idx, + RATE_HT_MCS_RATE_CODE_MSK_V1 | + RATE_HT_MCS_NSS_MSK_V1); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + result |= RATE_MCS_SGI_MSK_V1; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + if (info->flags & IEEE80211_TX_CTL_LDPC) + result |= RATE_MCS_LDPC_MSK_V1; + if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC)) + result |= RATE_MCS_STBC_MSK; + + result = iwl_new_rate_from_v1(result); + } else { + result = iwl_mld_mac80211_rate_idx_to_fw(mld, info, rate->idx); + } + + if (info->control.antennas) + result |= u32_encode_bits(info->control.antennas, + RATE_MCS_ANT_AB_MSK); + else + result |= iwl_mld_get_tx_ant(mld, info, sta, fc); + + return result; +} + +static u32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) +{ + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) + return iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + + return iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | + iwl_mld_get_tx_ant(mld, info, sta, fc); +} + +static void +iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd_gen3 *tx_cmd, + struct sk_buff *skb, bool amsdu) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_vif *vif; + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control)); + + if (!amsdu || !skb_is_gso(skb)) + return; + + /* As described in IEEE sta 802.11-2020, table 9-30 (Address + * field contents), A-MSDU address 3 should contain the BSSID + * address. + * + * In TSO, the skb header address 3 contains the original address 3 to + * correctly create all the A-MSDU subframes headers from it. + * Override now the address 3 in the command header with the BSSID. + * + * Note: we fill in the MLD address, but the firmware will do the + * necessary translation to link address after encryption. + */ + vif = info->control.vif; + switch (vif->type) { + case NL80211_IFTYPE_STATION: + ether_addr_copy(tx_cmd->hdr->addr3, vif->cfg.ap_addr); + break; + case NL80211_IFTYPE_AP: + ether_addr_copy(tx_cmd->hdr->addr3, vif->addr); + break; + default: + break; + } +} + +static void +iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_device_tx_cmd *dev_tx_cmd, + struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct iwl_mld_sta *mld_sta = sta ? iwl_mld_sta_from_mac80211(sta) : + NULL; + struct iwl_tx_cmd_gen3 *tx_cmd; + bool amsdu = ieee80211_is_data_qos(hdr->frame_control) && + (*ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_A_MSDU_PRESENT); + u32 rate_n_flags = 0; + u16 flags = 0; + + dev_tx_cmd->hdr.cmd = TX_CMD; + + if (!info->control.hw_key) + flags |= IWL_TX_FLAGS_ENCRYPT_DIS; + + /* For data and mgmt packets rate info comes from the fw. + * Only set rate/antenna for injected frames with fixed rate, or + * when no sta is given. + */ + if (unlikely(!sta || + info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { + flags |= IWL_TX_FLAGS_CMD_RATE; + rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta, + hdr->frame_control); + } else if (!ieee80211_is_data(hdr->frame_control) || + (mld_sta && + mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)) { + /* These are important frames */ + flags |= IWL_TX_FLAGS_HIGH_PRI; + } + + tx_cmd = (void *)dev_tx_cmd->payload; + + iwl_mld_fill_tx_cmd_hdr(tx_cmd, skb, amsdu); + + tx_cmd->offload_assist = iwl_mld_get_offload_assist(skb, amsdu); + + /* Total # bytes to be transmitted */ + tx_cmd->len = cpu_to_le16((u16)skb->len); + + tx_cmd->flags = cpu_to_le16(flags); + + tx_cmd->rate_n_flags = cpu_to_le32(rate_n_flags); +} + +/* Caller of this need to check that info->control.vif is not NULL */ +static struct iwl_mld_link * +iwl_mld_get_link_from_tx_info(struct ieee80211_tx_info *info) +{ + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(info->control.vif); + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + if (link_id == IEEE80211_LINK_UNSPECIFIED) { + if (info->control.vif->active_links) + link_id = ffs(info->control.vif->active_links) - 1; + else + link_id = 0; + } + + return rcu_dereference(mld_vif->link[link_id]); +} + +static int +iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + __le16 fc = hdr->frame_control; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_link *link; + + if (txq && txq->sta) + return iwl_mld_txq_from_mac80211(txq)->fw_id; + + if (!info->control.vif) + return IWL_MLD_INVALID_QUEUE; + + switch (info->control.vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + link = iwl_mld_get_link_from_tx_info(info); + + if (WARN_ON(!link)) + break; + + /* ucast disassociate/deauth frames without a station might + * happen, especially with reason 7 ("Class 3 frame received + * from nonassociated STA"). + */ + if (ieee80211_is_mgmt(fc) && + (!ieee80211_is_bufferable_mmpdu(skb) || + ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc))) + return link->bcast_sta.queue_id; + + if (is_multicast_ether_addr(hdr->addr1) && + !ieee80211_has_order(fc)) + return link->mcast_sta.queue_id; + + WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC, + "Couldn't find a TXQ. fc=0x%02x", le16_to_cpu(fc)); + return link->bcast_sta.queue_id; + case NL80211_IFTYPE_P2P_DEVICE: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + + if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) { + IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + WARN_ON(!ieee80211_is_mgmt(fc)); + + return mld_vif->deflink.aux_sta.queue_id; + default: + /* TODO: consider monitor (task=monitor) */ + WARN_ONCE(1, "Unsupported vif type\n"); + break; + } + + return IWL_MLD_INVALID_QUEUE; +} + +static void iwl_mld_probe_resp_set_noa(struct iwl_mld *mld, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_mld_link *mld_link = + &iwl_mld_vif_from_mac80211(info->control.vif)->deflink; + struct iwl_probe_resp_data *resp_data; + u8 *pos; + + if (!info->control.vif->p2p) + return; + + rcu_read_lock(); + + resp_data = rcu_dereference(mld_link->probe_resp_data); + if (!resp_data) + goto out; + + if (!resp_data->notif.noa_active) + goto out; + + if (skb_tailroom(skb) < resp_data->noa_len) { + if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) { + IWL_ERR(mld, + "Failed to reallocate probe resp\n"); + goto out; + } + } + + pos = skb_put(skb, resp_data->noa_len); + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + /* Set length of IE body (not including ID and length itself) */ + *pos++ = resp_data->noa_len - 2; + *pos++ = (WLAN_OUI_WFA >> 16) & 0xff; + *pos++ = (WLAN_OUI_WFA >> 8) & 0xff; + *pos++ = WLAN_OUI_WFA & 0xff; + *pos++ = WLAN_OUI_TYPE_WFA_P2P; + + memcpy(pos, &resp_data->notif.noa_attr, + resp_data->noa_len - sizeof(struct ieee80211_vendor_ie)); + +out: + rcu_read_unlock(); +} + +/* This function must be called with BHs disabled */ +static int iwl_mld_tx_mpdu(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = txq ? txq->sta : NULL; + struct iwl_device_tx_cmd *dev_tx_cmd; + int queue = iwl_mld_get_tx_queue_id(mld, txq, skb); + u8 tid = IWL_MAX_TID_COUNT; + + if (WARN_ONCE(queue == IWL_MLD_INVALID_QUEUE, "Invalid TX Queue id") || + queue == IWL_MLD_INVALID_DROP_TX) + return -1; + + if (unlikely(ieee80211_is_any_nullfunc(hdr->frame_control))) + return -1; + + dev_tx_cmd = iwl_trans_alloc_tx_cmd(mld->trans); + if (unlikely(!dev_tx_cmd)) + return -1; + + if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { + if (IWL_MLD_NON_TRANSMITTING_AP) + return -1; + + iwl_mld_probe_resp_set_noa(mld, skb); + } + + iwl_mld_fill_tx_cmd(mld, skb, dev_tx_cmd, sta); + + if (ieee80211_is_data(hdr->frame_control)) { + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = ieee80211_get_tid(hdr); + else + tid = IWL_TID_NON_QOS; + } + + IWL_DEBUG_TX(mld, "TX TID:%d from Q:%d len %d\n", + tid, queue, skb->len); + + /* From now on, we cannot access info->control */ + memset(&info->status, 0, sizeof(info->status)); + memset(info->driver_data, 0, sizeof(info->driver_data)); + + info->driver_data[1] = dev_tx_cmd; + + if (iwl_trans_tx(mld->trans, skb, dev_tx_cmd, queue)) + goto err; + + /* Update low-latency counter when a packet is queued instead + * of after TX, it makes sense for early low-latency detection + */ + if (sta) + iwl_mld_low_latency_update_counters(mld, hdr, sta, 0); + + return 0; + +err: + iwl_trans_free_tx_cmd(mld->trans, dev_tx_cmd); + IWL_DEBUG_TX(mld, "TX from Q:%d dropped\n", queue); + return -1; +} + +#ifdef CONFIG_INET + +/* This function handles the segmentation of a large TSO packet into multiple + * MPDUs, ensuring that the resulting segments conform to AMSDU limits and + * constraints. + */ +static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skbs) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int num_subframes, tcp_payload_len, subf_len; + u16 snap_ip_tcp, pad, max_tid_amsdu_len; + u8 tid; + + snap_ip_tcp = 8 + skb_network_header_len(skb) + tcp_hdrlen(skb); + + if (!ieee80211_is_data_qos(hdr->frame_control) || + !sta->cur->max_rc_amsdu_len) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Do not build AMSDU for IPv6 with extension headers. + * Ask stack to segment and checksum the generated MPDUs for us. + */ + if (skb->protocol == htons(ETH_P_IPV6) && + ((struct ipv6hdr *)skb_network_header(skb))->nexthdr != + IPPROTO_TCP) { + netdev_flags &= ~NETIF_F_CSUM_MASK; + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + } + + tid = ieee80211_get_tid(hdr); + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid]; + if (!max_tid_amsdu_len) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ + subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; + pad = (4 - subf_len) & 0x3; + + /* If we have N subframes in the A-MSDU, then the A-MSDU's size is + * N * subf_len + (N - 1) * pad. + */ + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad); + + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; + + tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + /* Make sure we have enough TBs for the A-MSDU: + * 2 for each subframe + * 1 more for each fragment + * 1 more for the potential data in the header + */ + if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > + mld->trans->max_skb_frags) + num_subframes = 1; + + if (num_subframes > 1) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + /* This skb fits in one single A-MSDU */ + if (tcp_payload_len <= num_subframes * mss) { + __skb_queue_tail(mpdus_skbs, skb); + return 0; + } + + /* Trick the segmentation function to make it create SKBs that can fit + * into one A-MSDU. + */ + return iwl_tx_tso_segment(skb, num_subframes, netdev_flags, mpdus_skbs); +} + +/* Manages TSO (TCP Segmentation Offload) packet transmission by segmenting + * large packets when necessary and transmitting each segment as MPDU. + */ +static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *orig_skb = skb; + struct sk_buff_head mpdus_skbs; + unsigned int payload_len; + int ret; + + if (WARN_ON(!txq || !txq->sta)) + return -1; + + payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + if (payload_len <= skb_shinfo(skb)->gso_size) + return iwl_mld_tx_mpdu(mld, skb, txq); + + if (!info->control.vif) + return -1; + + __skb_queue_head_init(&mpdus_skbs); + + ret = iwl_mld_tx_tso_segment(mld, skb, txq->sta, &mpdus_skbs); + if (ret) + return ret; + + WARN_ON(skb_queue_empty(&mpdus_skbs)); + + while (!skb_queue_empty(&mpdus_skbs)) { + skb = __skb_dequeue(&mpdus_skbs); + + ret = iwl_mld_tx_mpdu(mld, skb, txq); + if (!ret) + continue; + + /* Free skbs created as part of TSO logic that have not yet + * been dequeued + */ + __skb_queue_purge(&mpdus_skbs); + + /* skb here is not necessarily same as skb that entered + * this method, so free it explicitly. + */ + if (skb == orig_skb) + ieee80211_free_txskb(mld->hw, skb); + else + kfree_skb(skb); + + /* there was error, but we consumed skb one way or + * another, so return 0 + */ + return 0; + } + + return 0; +} +#else +static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + /* Impossible to get TSO without CONFIG_INET */ + WARN_ON(1); + + return -1; +} +#endif /* CONFIG_INET */ + +void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + if (skb_is_gso(skb)) { + if (!iwl_mld_tx_tso(mld, skb, txq)) + return; + goto err; + } + + if (likely(!iwl_mld_tx_mpdu(mld, skb, txq))) + return; + +err: + ieee80211_free_txskb(mld->hw, skb); +} + +void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + struct sk_buff *skb = NULL; + u8 zero_addr[ETH_ALEN] = {}; + + /* + * No need for threads to be pending here, they can leave the first + * taker all the work. + * + * mld_txq->tx_request logic: + * + * If 0, no one is currently TXing, set to 1 to indicate current thread + * will now start TX and other threads should quit. + * + * If 1, another thread is currently TXing, set to 2 to indicate to + * that thread that there was another request. Since that request may + * have raced with the check whether the queue is empty, the TXing + * thread should check the queue's status one more time before leaving. + * This check is done in order to not leave any TX hanging in the queue + * until the next TX invocation (which may not even happen). + * + * If 2, another thread is currently TXing, and it will already double + * check the queue, so do nothing. + */ + if (atomic_fetch_add_unless(&mld_txq->tx_request, 1, 2)) + return; + + rcu_read_lock(); + do { + while (likely(!mld_txq->status.stop_full) && + (skb = ieee80211_tx_dequeue(mld->hw, txq))) + iwl_mld_tx_skb(mld, skb, txq); + } while (atomic_dec_return(&mld_txq->tx_request)); + + IWL_DEBUG_TX(mld, "TXQ of sta %pM tid %d is now empty\n", + txq->sta ? txq->sta->addr : zero_addr, txq->tid); + + rcu_read_unlock(); +} + +static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + enum nl80211_band band = info->band; + struct ieee80211_tx_rate *tx_rate = &info->status.rates[0]; + u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK; + u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + + if (sgi) + tx_rate->flags |= IEEE80211_TX_RC_SHORT_GI; + + switch (chan_width) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + tx_rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_80: + tx_rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_160: + tx_rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + default: + break; + } + + switch (format) { + case RATE_MCS_HT_MSK: + tx_rate->flags |= IEEE80211_TX_RC_MCS; + tx_rate->idx = RATE_HT_MCS_INDEX(rate_n_flags); + break; + case RATE_MCS_VHT_MSK: + ieee80211_rate_set_vht(tx_rate, + rate_n_flags & RATE_MCS_CODE_MSK, + FIELD_GET(RATE_MCS_NSS_MSK, + rate_n_flags) + 1); + tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS; + break; + case RATE_MCS_HE_MSK: + /* mac80211 cannot do this without ieee80211_tx_status_ext() + * but it only matters for radiotap + */ + tx_rate->idx = 0; + break; + default: + tx_rate->idx = + iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags, + band); + break; + } +} + +void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tx_resp *tx_resp = (void *)pkt->data; + int txq_id = le16_to_cpu(tx_resp->tx_queue); + struct agg_tx_status *agg_status = &tx_resp->status; + u32 status = le16_to_cpu(agg_status->status); + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + size_t notif_size = sizeof(*tx_resp) + sizeof(u32); + int sta_id = IWL_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_TX_RES_GET_TID(tx_resp->ra_tid); + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + u16 ssn; + struct sk_buff_head skbs; + u8 skb_freed = 0; + bool mgmt = false; + bool tx_failure = (status & TX_STATUS_MSK) != TX_STATUS_SUCCESS; + + if (IWL_FW_CHECK(mld, tx_resp->frame_count != 1, + "Invalid tx_resp notif frame_count (%d)\n", + tx_resp->frame_count)) + return; + + /* validate the size of the variable part of the notif */ + if (IWL_FW_CHECK(mld, notif_size != pkt_len, + "Invalid tx_resp notif size (expected=%zu got=%u)\n", + notif_size, pkt_len)) + return; + + ssn = le32_to_cpup((__le32 *)agg_status + + tx_resp->frame_count) & 0xFFFF; + + __skb_queue_head_init(&skbs); + + /* we can free until ssn % q.n_bd not inclusive */ + iwl_trans_reclaim(mld->trans, txq_id, ssn, &skbs, false); + + while (!skb_queue_empty(&skbs)) { + struct sk_buff *skb = __skb_dequeue(&skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + + skb_freed++; + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + info->flags &= ~(IEEE80211_TX_STAT_ACK | IEEE80211_TX_STAT_TX_FILTERED); + + /* inform mac80211 about what happened with the frame */ + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + info->flags |= IEEE80211_TX_STAT_ACK; + break; + default: + break; + } + + /* If we are freeing multiple frames, mark all the frames + * but the first one as acked, since they were acknowledged + * before + */ + if (skb_freed > 1) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (tx_failure) { + enum iwl_fw_ini_time_point tp = + IWL_FW_INI_TIME_POINT_TX_FAILED; + + if (ieee80211_is_action(hdr->frame_control)) + tp = IWL_FW_INI_TIME_POINT_TX_WFD_ACTION_FRAME_FAILED; + else if (ieee80211_is_mgmt(hdr->frame_control)) + mgmt = true; + + iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL); + } + + iwl_mld_hwrate_to_tx_rate(le32_to_cpu(tx_resp->initial_rate), + info); + + if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1))) + ieee80211_tx_status_skb(mld->hw, skb); + } + + IWL_DEBUG_TX_REPLY(mld, + "TXQ %d status 0x%08x ssn=%d initial_rate 0x%x retries %d\n", + txq_id, status, ssn, le32_to_cpu(tx_resp->initial_rate), + tx_resp->failure_frame); + + if (tx_failure && mgmt) + iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant); + + if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations, + "Got invalid sta_id (%d)\n", sta_id)) + return; + + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (!link_sta) { + /* This can happen if the TX cmd was sent before pre_rcu_remove + * but the TX response was received after + */ + IWL_DEBUG_TX_REPLY(mld, + "Got valid sta_id (%d) but sta is NULL\n", + sta_id); + goto out; + } + + if (IS_ERR(link_sta)) + goto out; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + if (tx_failure && mld_sta->sta_state < IEEE80211_STA_AUTHORIZED) + iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant); + + if (tid < IWL_MAX_TID_COUNT) + iwl_mld_count_mpdu_tx(link_sta, 1); + +out: + rcu_read_unlock(); +} + +static void iwl_mld_tx_reclaim_txq(struct iwl_mld *mld, int txq, int index, + bool in_flush) +{ + struct sk_buff_head reclaimed_skbs; + + __skb_queue_head_init(&reclaimed_skbs); + + iwl_trans_reclaim(mld->trans, txq, index, &reclaimed_skbs, in_flush); + + while (!skb_queue_empty(&reclaimed_skbs)) { + struct sk_buff *skb = __skb_dequeue(&reclaimed_skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + if (!in_flush) + info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_skb(mld->hw, skb); + } +} + +int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id) +{ + struct iwl_tx_path_flush_cmd_rsp *rsp; + struct iwl_tx_path_flush_cmd flush_cmd = { + .sta_id = cpu_to_le32(fw_sta_id), + .tid_mask = cpu_to_le16(0xffff), + }; + struct iwl_host_cmd cmd = { + .id = TXPATH_FLUSH, + .len = { sizeof(flush_cmd), }, + .data = { &flush_cmd, }, + .flags = CMD_WANT_SKB, + }; + int ret, num_flushed_queues; + u32 resp_len; + + IWL_DEBUG_TX_QUEUES(mld, "flush for sta id %d tid mask 0x%x\n", + fw_sta_id, 0xffff); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) { + IWL_ERR(mld, "Failed to send flush command (%d)\n", ret); + return ret; + } + + resp_len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*rsp), + "Invalid TXPATH_FLUSH response len: %d\n", + resp_len)) { + ret = -EIO; + goto free_rsp; + } + + rsp = (void *)cmd.resp_pkt->data; + + if (IWL_FW_CHECK(mld, le16_to_cpu(rsp->sta_id) != fw_sta_id, + "sta_id %d != rsp_sta_id %d\n", fw_sta_id, + le16_to_cpu(rsp->sta_id))) { + ret = -EIO; + goto free_rsp; + } + + num_flushed_queues = le16_to_cpu(rsp->num_flushed_queues); + if (IWL_FW_CHECK(mld, num_flushed_queues > IWL_TX_FLUSH_QUEUE_RSP, + "num_flushed_queues %d\n", num_flushed_queues)) { + ret = -EIO; + goto free_rsp; + } + + for (int i = 0; i < num_flushed_queues; i++) { + struct iwl_flush_queue_info *queue_info = &rsp->queues[i]; + int read_after = le16_to_cpu(queue_info->read_after_flush); + int txq_id = le16_to_cpu(queue_info->queue_num); + + if (IWL_FW_CHECK(mld, + txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + continue; + + IWL_DEBUG_TX_QUEUES(mld, + "tid %d txq_id %d read-before %d read-after %d\n", + le16_to_cpu(queue_info->tid), txq_id, + le16_to_cpu(queue_info->read_before_flush), + read_after); + + iwl_mld_tx_reclaim_txq(mld, txq_id, read_after, true); + } + +free_rsp: + iwl_free_resp(&cmd); + return ret; +} + +int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (likely(mld_txq->status.allocated)) + return 0; + + ret = iwl_mld_add_txq(mld, txq); + + spin_lock_bh(&mld->add_txqs_lock); + if (!list_empty(&mld_txq->list)) + list_del_init(&mld_txq->list); + spin_unlock_bh(&mld->add_txqs_lock); + + return ret; +} + +int iwl_mld_update_sta_txqs(struct iwl_mld *mld, + struct ieee80211_sta *sta, + u32 old_sta_mask, u32 new_sta_mask) +{ + 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), + }; + + lockdep_assert_wiphy(mld->wiphy); + + for (int tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_txq *txq = + sta->txq[tid != IWL_MAX_TID_COUNT ? + tid : IEEE80211_NUM_TIDS]; + struct iwl_mld_txq *mld_txq = + iwl_mld_txq_from_mac80211(txq); + int ret; + + if (!mld_txq->status.allocated) + 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_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + &cmd); + if (ret) + return ret; + } + + return 0; +} + +void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_compressed_ba_notif *ba_res = (void *)pkt->data; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u16 tfd_cnt = le16_to_cpu(ba_res->tfd_cnt); + u8 sta_id = ba_res->sta_id; + struct ieee80211_link_sta *link_sta; + + if (!tfd_cnt) + return; + + if (IWL_FW_CHECK(mld, struct_size(ba_res, tfd, tfd_cnt) > pkt_len, + "Short BA notif (tfd_cnt=%d, size:0x%x)\n", + tfd_cnt, pkt_len)) + return; + + IWL_DEBUG_TX_REPLY(mld, + "BA notif received from sta_id=%d, flags=0x%x, sent:%d, acked:%d\n", + sta_id, le32_to_cpu(ba_res->flags), + le16_to_cpu(ba_res->txed), + le16_to_cpu(ba_res->done)); + + for (int i = 0; i < tfd_cnt; i++) { + struct iwl_compressed_ba_tfd *ba_tfd = &ba_res->tfd[i]; + int txq_id = le16_to_cpu(ba_tfd->q_num); + int index = le16_to_cpu(ba_tfd->tfd_index); + + if (IWL_FW_CHECK(mld, + txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + continue; + + iwl_mld_tx_reclaim_txq(mld, txq_id, index, false); + } + + if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations, + "Got invalid sta_id (%d)\n", sta_id)) + return; + + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (IWL_FW_CHECK(mld, IS_ERR_OR_NULL(link_sta), + "Got valid sta_id (%d) but link_sta is NULL\n", + sta_id)) + goto out; + + iwl_mld_count_mpdu_tx(link_sta, le16_to_cpu(ba_res->txed)); +out: + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.h b/drivers/net/wireless/intel/iwlwifi/mld/tx.h new file mode 100644 index 000000000000..520f15f9d33c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_tx_h__ +#define __iwl_mld_tx_h__ + +#include "mld.h" + +#define IWL_MLD_INVALID_QUEUE 0xFFFF +#define IWL_MLD_INVALID_DROP_TX 0xFFFE + +/** + * struct iwl_mld_txq - TX Queue data + * + * @fw_id: the fw id of this txq. Only valid when &status.allocated is true. + * @status: bitmap of the txq status + * @status.allocated: Indicates that the queue was allocated. + * @status.stop_full: Indicates that the queue is full and should stop TXing. + * @list: list pointer, for &mld::txqs_to_add + * @tx_request: makes sure that if there are multiple threads that want to tx + * from this txq, only one of them will do all the TXing. + * This is needed to avoid spinning the trans txq lock, which is expensive + */ +struct iwl_mld_txq { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u16 fw_id; + struct { + u8 allocated:1; + u8 stop_full:1; + } status; + ); + struct list_head list; + atomic_t tx_request; + /* And here fields that survive a fw restart */ +}; + +static inline void iwl_mld_init_txq(struct iwl_mld_txq *mld_txq) +{ + INIT_LIST_HEAD(&mld_txq->list); + atomic_set(&mld_txq->tx_request, 0); +} + +static inline struct iwl_mld_txq * +iwl_mld_txq_from_mac80211(struct ieee80211_txq *txq) +{ + return (void *)txq->drv_priv; +} + +void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq); +void iwl_mld_add_txq_list(struct iwl_mld *mld); +void +iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id); +void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq); +void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id); +int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq); + +int iwl_mld_update_sta_txqs(struct iwl_mld *mld, + struct ieee80211_sta *sta, + u32 old_sta_mask, u32 new_sta_mask); + +void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant); + +u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif); + +void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq); + +#endif /* __iwl_mld_tx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index cd347cd8176a..03f7eb46bbc7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -498,7 +498,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { /* Ma devices */ {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_trans_cfg)}, {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)}, - +#endif /* CONFIG_IWLMVM */ +#if IS_ENABLED(CONFIG_IWLMLD) /* Bz devices */ {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_trans_cfg)}, @@ -543,7 +544,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { /* Dr devices */ {IWL_PCI_DEVICE(0x272F, PCI_ANY_ID, iwl_dr_trans_cfg)}, -#endif /* CONFIG_IWLMVM */ +#endif /* CONFIG_IWLMLD */ {0} }; @@ -1109,6 +1110,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), +#endif /* CONFIG_IWLMVM */ +#if IS_ENABLED(CONFIG_IWLMLD) /* Bz */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, @@ -1230,7 +1233,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_br, iwl_br_name), -#endif /* CONFIG_IWLMVM */ +#endif /* CONFIG_IWLMLD */ }; EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table); From 5a88f649c8c79ccfcb170da07a2ae40754dfdb9b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 262/465] wifi: rtw89: fw: use struct to fill role_maintain H2C command JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 385eff211ee0efa95c8ce9d7c7b1d8d79d5d7330 Author: Po-Hao Huang Date: Thu Feb 27 21:12:25 2025 +0800 wifi: rtw89: fw: use struct to fill role_maintain H2C command The role_maintain H2C command is to align operating mode of WiFi role, such as STA or AP modes, between driver and firmware. Use a struct to fill fields of this H2C command. Don't change logic at all. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250227131228.8457-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 22 +++++++++++-------- drivers/net/wireless/realtek/rtw89/fw.h | 28 +++++++++---------------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b843d259fbfa..3e4a3200358a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3901,14 +3901,15 @@ fail: } EXPORT_SYMBOL(rtw89_fw_h2c_update_beacon_be); -#define H2C_ROLE_MAINTAIN_LEN 4 int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, enum rtw89_upd_mode upd_mode) { - struct sk_buff *skb; u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; + struct rtw89_h2c_role_maintain *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; u8 self_role; int ret; @@ -3921,21 +3922,24 @@ int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, self_role = rtwvif_link->self_role; } - skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ROLE_MAINTAIN_LEN); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for h2c join\n"); return -ENOMEM; } - skb_put(skb, H2C_ROLE_MAINTAIN_LEN); - SET_FWROLE_MAINTAIN_MACID(skb->data, mac_id); - SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, self_role); - SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode); - SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif_link->wifi_role); + skb_put(skb, len); + h2c = (struct rtw89_h2c_role_maintain *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_ROLE_MAINTAIN_W0_MACID) | + le32_encode_bits(self_role, RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE) | + le32_encode_bits(upd_mode, RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE) | + le32_encode_bits(rtwvif_link->wifi_role, + RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT, H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1, - H2C_ROLE_MAINTAIN_LEN); + len); ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 1c53c1e22439..e0faed076150 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -1591,25 +1591,17 @@ struct rtw89_h2c_bcn_upd_be { #define RTW89_H2C_BCN_UPD_BE_W7_ECSA_OFST GENMASK(30, 16) #define RTW89_H2C_BCN_UPD_BE_W7_PROTECTION_KEY_ID BIT(31) -static inline void SET_FWROLE_MAINTAIN_MACID(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); -} +struct rtw89_h2c_role_maintain { + __le32 w0; +}; -static inline void SET_FWROLE_MAINTAIN_SELF_ROLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(9, 8)); -} - -static inline void SET_FWROLE_MAINTAIN_UPD_MODE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(12, 10)); -} - -static inline void SET_FWROLE_MAINTAIN_WIFI_ROLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(16, 13)); -} +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID GENMASK(7, 0) +#define RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE GENMASK(9, 8) +#define RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE GENMASK(12, 10) +#define RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE GENMASK(16, 13) +#define RTW89_H2C_ROLE_MAINTAIN_W0_BAND GENMASK(18, 17) +#define RTW89_H2C_ROLE_MAINTAIN_W0_PORT GENMASK(21, 19) +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID_EXT GENMASK(31, 24) enum rtw89_fw_sta_type { /* value of RTW89_H2C_JOININFO_W1_STA_TYPE */ RTW89_FW_N_AC_STA = 0, From 7a079bb2b4cce63115f10f5e6fba1f1e9c514063 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 263/465] wifi: rtw89: fw: update role_maintain H2C command for roles operating on band 1 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b521af1dcdc319ec966563c2060bdf515401577f Author: Po-Hao Huang Date: Thu Feb 27 21:12:26 2025 +0800 wifi: rtw89: fw: update role_maintain H2C command for roles operating on band 1 Add new fields band and port ID to make chips operating on the band and port ID other than 0, so that multiple vif(s) can be working at the same time. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250227131228.8457-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3e4a3200358a..1965a62746c2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3934,7 +3934,10 @@ int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, le32_encode_bits(self_role, RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE) | le32_encode_bits(upd_mode, RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE) | le32_encode_bits(rtwvif_link->wifi_role, - RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE); + RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE) | + le32_encode_bits(rtwvif_link->mac_idx, + RTW89_H2C_ROLE_MAINTAIN_W0_BAND) | + le32_encode_bits(rtwvif_link->port, RTW89_H2C_ROLE_MAINTAIN_W0_PORT); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT, From 2a55893dda3adf52f00e9142f88246020067e640 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 264/465] wifi: rtw89: fw: correct debug message format in rtw89_build_txpwr_trk_tbl_from_elm() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 88b46320fc9d915aa0c1c765db5ccd2db12fe92e Author: Ping-Ke Shih Date: Thu Feb 27 21:12:27 2025 +0800 wifi: rtw89: fw: correct debug message format in rtw89_build_txpwr_trk_tbl_from_elm() The format should be "%08x". Fix the mistakes. Fixes: d60e73e5dd70 ("wifi: rtw89: fw: load TX power track tables from fw_element") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250227131228.8457-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 1965a62746c2..15d6bda1fcf0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1099,7 +1099,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev, bitmap = le32_to_cpu(elm->u.txpwr_trk.bitmap); if ((bitmap & needed_bitmap) != needed_bitmap) { - rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %0x8x\n", + rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %08x\n", needed_bitmap, bitmap); return -ENOENT; } From 380eb971edb9c8b6bb852fd6b74accb7879cf09d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:50 +0200 Subject: [PATCH 265/465] wifi: rtw89: fw: don't reject firmware in blacklist to prevent breaking users JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ad26d0dcb3bd5864c5295746bd4fef6376911da6 Author: Ping-Ke Shih Date: Thu Feb 27 21:12:28 2025 +0800 wifi: rtw89: fw: don't reject firmware in blacklist to prevent breaking users Once update driver blacklist of firmware, users' firmware might be in the list, and then driver stops working. Since breaking users is not expected, report a significant message instead of stopping. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250227131228.8457-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/fw.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 15d6bda1fcf0..d0a246f415ff 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -372,7 +372,7 @@ static int __check_secure_blacklist(struct rtw89_dev *rtwdev, return 0; if (!chip_blacklist) { - rtw89_err(rtwdev, "chip no blacklist for secure firmware\n"); + rtw89_warn(rtwdev, "chip no blacklist for secure firmware\n"); return -ENOENT; } @@ -380,14 +380,14 @@ static int __check_secure_blacklist(struct rtw89_dev *rtwdev, bit_mask = BIT(section_content->blacklist.bit_in_chip_list & 0x7); if (section_content->blacklist.ver > chip_blacklist->ver) { - rtw89_err(rtwdev, "chip blacklist out of date (%u, %u)\n", - section_content->blacklist.ver, chip_blacklist->ver); + rtw89_warn(rtwdev, "chip blacklist out of date (%u, %u)\n", + section_content->blacklist.ver, chip_blacklist->ver); return -EINVAL; } if (chip_blacklist->list[byte_idx] & bit_mask) { - rtw89_err(rtwdev, "firmware %u in chip blacklist\n", - section_content->blacklist.ver); + rtw89_warn(rtwdev, "firmware %u in chip blacklist\n", + section_content->blacklist.ver); return -EPERM; } @@ -427,7 +427,10 @@ static int __parse_security_section(struct rtw89_dev *rtwdev, info->secure_section_exist = true; } - return __check_secure_blacklist(rtwdev, info, section_info, content); + ret = __check_secure_blacklist(rtwdev, info, section_info, content); + WARN_ONCE(ret, "Current firmware in blacklist. Please update firmware.\n"); + + return 0; } static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 len, From 022e43a42976caf791f202382c674a0ba797821d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 266/465] wifi: rtw89: pci: correct ISR RDU bit for 8922AE JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3218f5bd8e2e4abc156493f8121b8db53b49ee9e Author: Ping-Ke Shih Date: Thu Feb 27 21:19:07 2025 +0800 wifi: rtw89: pci: correct ISR RDU bit for 8922AE The interrupt status (ISR) bits of RX desc unavailable (RDU) for 8922AE are B_BE_RDU_CH1_INT_V1 and B_BE_RDU_CH0_INT_V1. With wrong bits, if it happens, driver can't recognize the situation and prompt a message. Fix the definition accordingly. Fixes: aa70f76120ee ("wifi: rtw89: pci: generalize interrupt status bits of interrupt handlers") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250227131907.9864-1-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/pci.h | 56 +++++++++++---------- drivers/net/wireless/realtek/rtw89/pci_be.c | 2 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 4d11c3dd60a5..79fef5f90140 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -455,34 +455,36 @@ #define B_BE_RX0DMA_INT_EN BIT(0) #define R_BE_HAXI_HISR00 0xB0B4 -#define B_BE_RDU_CH6_INT BIT(28) -#define B_BE_RDU_CH5_INT BIT(27) -#define B_BE_RDU_CH4_INT BIT(26) -#define B_BE_RDU_CH2_INT BIT(25) -#define B_BE_RDU_CH1_INT BIT(24) -#define B_BE_RDU_CH0_INT BIT(23) -#define B_BE_RXDMA_STUCK_INT BIT(22) -#define B_BE_TXDMA_STUCK_INT BIT(21) -#define B_BE_TXDMA_CH14_INT BIT(20) -#define B_BE_TXDMA_CH13_INT BIT(19) -#define B_BE_TXDMA_CH12_INT BIT(18) -#define B_BE_TXDMA_CH11_INT BIT(17) -#define B_BE_TXDMA_CH10_INT BIT(16) -#define B_BE_TXDMA_CH9_INT BIT(15) -#define B_BE_TXDMA_CH8_INT BIT(14) -#define B_BE_TXDMA_CH7_INT BIT(13) -#define B_BE_TXDMA_CH6_INT BIT(12) -#define B_BE_TXDMA_CH5_INT BIT(11) -#define B_BE_TXDMA_CH4_INT BIT(10) -#define B_BE_TXDMA_CH3_INT BIT(9) -#define B_BE_TXDMA_CH2_INT BIT(8) -#define B_BE_TXDMA_CH1_INT BIT(7) -#define B_BE_TXDMA_CH0_INT BIT(6) -#define B_BE_RPQ1DMA_INT BIT(5) -#define B_BE_RX1P1DMA_INT BIT(4) +#define B_BE_RDU_CH5_INT_V1 BIT(30) +#define B_BE_RDU_CH4_INT_V1 BIT(29) +#define B_BE_RDU_CH3_INT_V1 BIT(28) +#define B_BE_RDU_CH2_INT_V1 BIT(27) +#define B_BE_RDU_CH1_INT_V1 BIT(26) +#define B_BE_RDU_CH0_INT_V1 BIT(25) +#define B_BE_RXDMA_STUCK_INT_V1 BIT(24) +#define B_BE_TXDMA_STUCK_INT_V1 BIT(23) +#define B_BE_TXDMA_CH14_INT_V1 BIT(22) +#define B_BE_TXDMA_CH13_INT_V1 BIT(21) +#define B_BE_TXDMA_CH12_INT_V1 BIT(20) +#define B_BE_TXDMA_CH11_INT_V1 BIT(19) +#define B_BE_TXDMA_CH10_INT_V1 BIT(18) +#define B_BE_TXDMA_CH9_INT_V1 BIT(17) +#define B_BE_TXDMA_CH8_INT_V1 BIT(16) +#define B_BE_TXDMA_CH7_INT_V1 BIT(15) +#define B_BE_TXDMA_CH6_INT_V1 BIT(14) +#define B_BE_TXDMA_CH5_INT_V1 BIT(13) +#define B_BE_TXDMA_CH4_INT_V1 BIT(12) +#define B_BE_TXDMA_CH3_INT_V1 BIT(11) +#define B_BE_TXDMA_CH2_INT_V1 BIT(10) +#define B_BE_TXDMA_CH1_INT_V1 BIT(9) +#define B_BE_TXDMA_CH0_INT_V1 BIT(8) +#define B_BE_RX1P1DMA_INT_V1 BIT(7) +#define B_BE_RX0P1DMA_INT_V1 BIT(6) +#define B_BE_RO1DMA_INT BIT(5) +#define B_BE_RP1DMA_INT BIT(4) #define B_BE_RX1DMA_INT BIT(3) -#define B_BE_RPQ0DMA_INT BIT(2) -#define B_BE_RX0P1DMA_INT BIT(1) +#define B_BE_RO0DMA_INT BIT(2) +#define B_BE_RP0DMA_INT BIT(1) #define B_BE_RX0DMA_INT BIT(0) /* TX/RX */ diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index cd39eebe8186..12e6a0cbb889 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -666,7 +666,7 @@ SIMPLE_DEV_PM_OPS(rtw89_pm_ops_be, rtw89_pci_suspend_be, rtw89_pci_resume_be); EXPORT_SYMBOL(rtw89_pm_ops_be); const struct rtw89_pci_gen_def rtw89_pci_gen_be = { - .isr_rdu = B_BE_RDU_CH1_INT | B_BE_RDU_CH0_INT, + .isr_rdu = B_BE_RDU_CH1_INT_V1 | B_BE_RDU_CH0_INT_V1, .isr_halt_c2h = B_BE_HALT_C2H_INT, .isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT, .isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1}, From d86f63c2f531c84619600cab076176b386e1a79e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 267/465] wifi: cfg80211: move link reconfig parameters into a struct JIRA: https://issues.redhat.com/browse/RHEL-89168 Conflicts: - net/wireless/nl80211.c Parallel branches, 2e85829ac7fbb and 1f860eb4cdda were already applied. In addition, 1f860eb4cdda was previously applied to disable multi-link reconfiguration, keep it still disabled because it will be enabled later in this rebase, when cd0b9da682cfe is applied. commit a096a8602f4fee42a18b7537a7467a28c44728af Author: Johannes Berg Date: Sat Mar 8 23:03:27 2025 +0200 wifi: cfg80211: move link reconfig parameters into a struct Add a new struct cfg80211_ml_reconf_req to collect the link reconfiguration parameters. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.0cf299c1fdd0.Id1a3b1092dc52d0d3731a8798522fdf2e052bf0b@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 15 ++++++++++++--- net/mac80211/cfg.c | 7 +++---- net/mac80211/ieee80211_i.h | 5 ++--- net/mac80211/mlme.c | 39 +++++++++++++++++++------------------- net/wireless/core.h | 6 +++--- net/wireless/mlme.c | 13 ++++++------- net/wireless/nl80211.c | 22 ++++++++++----------- net/wireless/rdev-ops.h | 10 ++++------ net/wireless/trace.h | 13 ++++++------- 9 files changed, 66 insertions(+), 64 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3264c228a1d8..48f055b06102 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7,7 +7,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include @@ -3082,6 +3082,16 @@ struct cfg80211_assoc_link { int error; }; +/** + * struct cfg80211_ml_reconf_req - MLO link reconfiguration request + * @add_links: data for links to add, see &struct cfg80211_assoc_link + * @rem_links: bitmap of links to remove + */ +struct cfg80211_ml_reconf_req { + struct cfg80211_assoc_link add_links[IEEE80211_MLD_MAX_NUM_LINKS]; + u16 rem_links; +}; + /** * enum cfg80211_assoc_req_flags - Over-ride default behaviour in association. * @@ -4972,8 +4982,7 @@ struct cfg80211_ops { struct cfg80211_ttlm_params *params); u32 (*get_radio_mask)(struct wiphy *wiphy, struct net_device *dev); int (*assoc_ml_reconf)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_assoc_link *add_links, - u16 rem_links); + struct cfg80211_ml_reconf_req *req); int (*set_epcs)(struct wiphy *wiphy, struct net_device *dev, bool val); }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9902baa2c48f..229b4e2bd06f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include @@ -5185,14 +5185,13 @@ ieee80211_set_ttlm(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_assoc_ml_reconf(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_assoc_link *add_links, - u16 rem_links) + struct cfg80211_ml_reconf_req *req) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ieee80211_mgd_assoc_ml_reconf(sdata, add_links, rem_links); + return ieee80211_mgd_assoc_ml_reconf(sdata, req); } static int diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 845888ac3d2c..5f92619f8584 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2015 Intel Mobile Communications GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef IEEE80211_I_H @@ -2789,8 +2789,7 @@ void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, - struct cfg80211_assoc_link *add_links, - u16 rem_links); + struct cfg80211_ml_reconf_req *req); void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9015a979649a..b2885eb80c0b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10363,8 +10363,7 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, - struct cfg80211_assoc_link *add_links, - u16 rem_links) + struct cfg80211_ml_reconf_req *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgd_assoc_data *data = NULL; @@ -10384,9 +10383,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, return -EBUSY; added_links = 0; - for (link_id = 0; add_links && link_id < IEEE80211_MLD_MAX_NUM_LINKS; - link_id++) { - if (!add_links[link_id].bss) + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + if (!req->add_links[link_id].bss) continue; added_links |= BIT(link_id); @@ -10414,7 +10412,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct ieee80211_supported_band *sband; - struct cfg80211_bss *link_cbss = add_links[link_id].bss; + struct cfg80211_bss *link_cbss = + req->add_links[link_id].bss; struct ieee80211_bss *bss; if (!link_cbss) @@ -10444,11 +10443,11 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, data->link[link_id].bss = link_cbss; data->link[link_id].disabled = - add_links[link_id].disabled; + req->add_links[link_id].disabled; data->link[link_id].elems = - (u8 *)add_links[link_id].elems; + (u8 *)req->add_links[link_id].elems; data->link[link_id].elems_len = - add_links[link_id].elems_len; + req->add_links[link_id].elems_len; if (!bss->uapsd_supported) uapsd_supported = false; @@ -10498,10 +10497,11 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, * Section 35.3.6.4 in Draft P802.11be_D7.0 the AP MLD should accept the * link removal request. */ - if (rem_links) { - u16 new_active_links = sdata->vif.active_links & ~rem_links; + if (req->rem_links) { + u16 new_active_links = + sdata->vif.active_links & ~req->rem_links; - new_valid_links = sdata->vif.valid_links & ~rem_links; + new_valid_links = sdata->vif.valid_links & ~req->rem_links; /* Should not be left with no valid links to perform the * ML reconfiguration @@ -10536,14 +10536,15 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, * is expected to send the ML reconfiguration response frame on the link * on which the request was received. */ - skb = ieee80211_build_ml_reconf_req(sdata, data, rem_links); + skb = ieee80211_build_ml_reconf_req(sdata, data, req->rem_links); if (!skb) { err = -ENOMEM; goto err_free; } - if (rem_links) { - u16 new_dormant_links = sdata->vif.dormant_links & ~rem_links; + if (req->rem_links) { + u16 new_dormant_links = + sdata->vif.dormant_links & ~req->rem_links; err = ieee80211_vif_set_links(sdata, new_valid_links, new_dormant_links); @@ -10556,7 +10557,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { - if (!(rem_links & BIT(link_id))) + if (!(req->rem_links & BIT(link_id))) continue; ieee80211_sta_remove_link(sta, link_id); @@ -10565,17 +10566,17 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, /* notify the driver and upper layers */ ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_VALID_LINKS); - cfg80211_links_removed(sdata->dev, rem_links); + cfg80211_links_removed(sdata->dev, req->rem_links); } sdata_info(sdata, "mlo: reconf: adding=0x%x, removed=0x%x\n", - added_links, rem_links); + added_links, req->rem_links); ieee80211_tx_skb(sdata, skb); sdata->u.mgd.reconf.added_links = added_links; sdata->u.mgd.reconf.add_links_data = data; - sdata->u.mgd.reconf.removed_links = rem_links; + sdata->u.mgd.reconf.removed_links = req->rem_links; wiphy_delayed_work_queue(sdata->local->hw.wiphy, &sdata->u.mgd.reconf.wk, IEEE80211_ASSOC_TIMEOUT_SHORT); diff --git a/net/wireless/core.h b/net/wireless/core.h index b094b526b05d..c56a35040caa 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -568,8 +568,8 @@ void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask); int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_assoc_link *links, - u16 rem_links); + struct cfg80211_ml_reconf_req *req); + /** * struct cfg80211_colocated_ap - colocated AP information * diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index e10f2b3b4b7f..956d33b219df 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -4,7 +4,7 @@ * * Copyright (c) 2009, Jouni Malinen * Copyright (c) 2015 Intel Deutschland GmbH - * Copyright (C) 2019-2020, 2022-2024 Intel Corporation + * Copyright (C) 2019-2020, 2022-2025 Intel Corporation */ #include @@ -1297,25 +1297,24 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev) int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_assoc_link *links, - u16 rem_links) + struct cfg80211_ml_reconf_req *req) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; lockdep_assert_wiphy(wdev->wiphy); - err = rdev_assoc_ml_reconf(rdev, dev, links, rem_links); + err = rdev_assoc_ml_reconf(rdev, dev, req); if (!err) { int link_id; for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { - if (!links[link_id].bss) + if (!req->add_links[link_id].bss) continue; - cfg80211_ref_bss(&rdev->wiphy, links[link_id].bss); - cfg80211_hold_bss(bss_from_pub(links[link_id].bss)); + cfg80211_ref_bss(&rdev->wiphy, req->add_links[link_id].bss); + cfg80211_hold_bss(bss_from_pub(req->add_links[link_id].bss)); } } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dd1b64d501f2..32c0aaf852aa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include @@ -16512,9 +16512,9 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_assoc_link links[IEEE80211_MLD_MAX_NUM_LINKS] = {}; + struct cfg80211_ml_reconf_req req = {}; unsigned int link_id; - u16 add_links, rem_links; + u16 add_links; int err; if (!wdev->valid_links) @@ -16530,7 +16530,7 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) add_links = 0; if (info->attrs[NL80211_ATTR_MLO_LINKS]) { - err = nl80211_process_links(rdev, links, + err = nl80211_process_links(rdev, req.add_links, /* mark as MLO, but not assoc */ IEEE80211_MLD_MAX_NUM_LINKS, NULL, 0, info); @@ -16539,24 +16539,22 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { - if (!links[link_id].bss) + if (!req.add_links[link_id].bss) continue; add_links |= BIT(link_id); } } if (info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS]) - rem_links = + req.rem_links = nla_get_u16(info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS]); - else - rem_links = 0; /* Validate that existing links are not added, removed links are valid * and don't allow adding and removing the same links */ - if ((add_links & rem_links) || !(add_links | rem_links) || + if ((add_links & req.rem_links) || !(add_links | req.rem_links) || (wdev->valid_links & add_links) || - ((wdev->valid_links & rem_links) != rem_links)) { + ((wdev->valid_links & req.rem_links) != req.rem_links)) { err = -EINVAL; goto out; } @@ -16564,8 +16562,8 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; out: - for (link_id = 0; link_id < ARRAY_SIZE(links); link_id++) - cfg80211_put_bss(&rdev->wiphy, links[link_id].bss); + for (link_id = 0; link_id < ARRAY_SIZE(req.add_links); link_id++) + cfg80211_put_bss(&rdev->wiphy, req.add_links[link_id].bss); return err; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 759da1623342..9f4783c2354c 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018, 2021-2024 Intel Corporation + * Copyright (C) 2018, 2021-2025 Intel Corporation */ #ifndef __CFG80211_RDEV_OPS #define __CFG80211_RDEV_OPS @@ -1551,16 +1551,14 @@ rdev_get_radio_mask(struct cfg80211_registered_device *rdev, static inline int rdev_assoc_ml_reconf(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_assoc_link *add_links, - u16 rem_links) + struct cfg80211_ml_reconf_req *req) { struct wiphy *wiphy = &rdev->wiphy; int ret = -EOPNOTSUPP; - trace_rdev_assoc_ml_reconf(wiphy, dev, add_links, rem_links); + trace_rdev_assoc_ml_reconf(wiphy, dev, req); if (rdev->ops->assoc_ml_reconf) - ret = rdev->ops->assoc_ml_reconf(wiphy, dev, add_links, - rem_links); + ret = rdev->ops->assoc_ml_reconf(wiphy, dev, req); trace_rdev_return_int(wiphy, ret); return ret; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index e1affe560a8e..40445f628fde 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018, 2020-2024 Intel Corporation + * Copyright (C) 2018, 2020-2025 Intel Corporation */ #undef TRACE_SYSTEM #define TRACE_SYSTEM cfg80211 @@ -4142,9 +4142,8 @@ TRACE_EVENT(cfg80211_mlo_reconf_add_done, TRACE_EVENT(rdev_assoc_ml_reconf, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_assoc_link *add_links, - u16 rem_links), - TP_ARGS(wiphy, netdev, add_links, rem_links), + struct cfg80211_ml_reconf_req *req), + TP_ARGS(wiphy, netdev, req), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -4157,9 +4156,9 @@ TRACE_EVENT(rdev_assoc_ml_reconf, u32 i; __entry->add_links = 0; - __entry->rem_links = rem_links; - for (i = 0; add_links && i < IEEE80211_MLD_MAX_NUM_LINKS; i++) - if (add_links[i].bss) + __entry->rem_links = req->rem_links; + for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) + if (req->add_links[i].bss) __entry->add_links |= BIT(i); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", add_links=0x%x, rem_links=0x%x", From 6b1596653454b73c1295fdf08f4a1face1fe2bd2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 268/465] wifi: cfg80211: allow setting extended MLD capa/ops JIRA: https://issues.redhat.com/browse/RHEL-89168 Conflicts: - net/wireless/nl80211.c Parallel branches, 1f860eb4cdda was previously applied to disable multi-link reconfiguration, keep it still disabled because it will be enabled later in this rebase, when cd0b9da682cfe is applied. commit 969241371f06507f922633e42a4c1a8ce00f220e Author: Johannes Berg Date: Sat Mar 8 23:03:28 2025 +0200 wifi: cfg80211: allow setting extended MLD capa/ops Some extended MLD capabilities and operations bits (currently the "BTM MLD Recommendataion For Multiple APs Support" bit) may depend on userspace capabilities. Allow userspace to pass the values for this field that it supports to the association and link reconfiguration operations. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20250308225541.bd52078b5f65.I4dd8f53b0030db7ea87a2e0920989e7e2c7b5345@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 6 ++++++ include/uapi/linux/nl80211.h | 10 +++++++++- net/wireless/nl80211.c | 12 ++++++++++++ net/wireless/trace.h | 4 ++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 48f055b06102..c15bdaffc8f6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3086,10 +3086,13 @@ struct cfg80211_assoc_link { * struct cfg80211_ml_reconf_req - MLO link reconfiguration request * @add_links: data for links to add, see &struct cfg80211_assoc_link * @rem_links: bitmap of links to remove + * @ext_mld_capa_ops: extended MLD capabilities and operations set by + * userspace for the ML reconfiguration action frame */ struct cfg80211_ml_reconf_req { struct cfg80211_assoc_link add_links[IEEE80211_MLD_MAX_NUM_LINKS]; u16 rem_links; + u16 ext_mld_capa_ops; }; /** @@ -3164,6 +3167,8 @@ enum cfg80211_assoc_req_flags { * the link on which the association request should be sent * @ap_mld_addr: AP MLD address in case of MLO association request, * valid iff @link_id >= 0 + * @ext_mld_capa_ops: extended MLD capabilities and operations set by + * userspace for the association */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; @@ -3184,6 +3189,7 @@ struct cfg80211_assoc_request { struct cfg80211_assoc_link links[IEEE80211_MLD_MAX_NUM_LINKS]; const u8 *ap_mld_addr; s8 link_id; + u16 ext_mld_capa_ops; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9d8ecf20ef0d..c59075acdb10 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -11,7 +11,7 @@ * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2893,6 +2893,12 @@ enum nl80211_commands { * @NL80211_ATTR_EPCS: Flag attribute indicating that EPCS is enabled for a * station interface. * + * @NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS: Extended MLD capabilities and + * operations that userspace implements to use during association/ML + * link reconfig, currently only "BTM MLD Recommendation For Multiple + * APs Support". Drivers may set additional flags that they support + * in the kernel or device. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3448,6 +3454,8 @@ enum nl80211_attrs { NL80211_ATTR_MLO_RECONF_REM_LINKS, NL80211_ATTR_EPCS, + NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 32c0aaf852aa..f207f9bd8769 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -850,6 +850,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NL80211_MAX_SUPP_SELECTORS), [NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 }, [NL80211_ATTR_EPCS] = { .type = NLA_FLAG }, + [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -11388,6 +11389,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; goto free; } + + if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + req.ext_mld_capa_ops = + nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); } else { if (req.link_id >= 0) return -EINVAL; @@ -11397,6 +11402,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(req.bss)) return PTR_ERR(req.bss); ap_addr = req.bss->bssid; + + if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + return -EINVAL; } err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); @@ -16559,6 +16567,10 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) goto out; } + if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + req.ext_mld_capa_ops = + nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); + err = -EOPNOTSUPP; out: diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 40445f628fde..c27fec520ed8 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1378,6 +1378,7 @@ TRACE_EVENT(rdev_assoc, __dynamic_array(u8, fils_kek, req->fils_kek_len) __dynamic_array(u8, fils_nonces, req->fils_nonces ? 2 * FILS_NONCE_LEN : 0) + __field(u16, ext_mld_capa_ops) ), TP_fast_assign( WIPHY_ASSIGN; @@ -1404,6 +1405,7 @@ TRACE_EVENT(rdev_assoc, if (req->fils_nonces) memcpy(__get_dynamic_array(fils_nonces), req->fils_nonces, 2 * FILS_NONCE_LEN); + __entry->ext_mld_capa_ops = req->ext_mld_capa_ops; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: %pM" ", previous bssid: %pM, use mfp: %s, flags: 0x%x", @@ -4149,6 +4151,7 @@ TRACE_EVENT(rdev_assoc_ml_reconf, NETDEV_ENTRY __field(u16, add_links) __field(u16, rem_links) + __field(u16, ext_mld_capa_ops) ), TP_fast_assign( WIPHY_ASSIGN; @@ -4160,6 +4163,7 @@ TRACE_EVENT(rdev_assoc_ml_reconf, for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) if (req->add_links[i].bss) __entry->add_links |= BIT(i); + __entry->ext_mld_capa_ops = req->ext_mld_capa_ops; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", add_links=0x%x, rem_links=0x%x", WIPHY_PR_ARG, NETDEV_PR_ARG, From 1196ed09f3aeacde063d701784fdf899fe094e74 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 269/465] wifi: mac80211: mlme: support extended MLD capa/ops in assoc JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 96d4311c318474e2c77d9f5b6f32c3fce437aa09 Author: Johannes Berg Date: Sat Mar 8 23:03:29 2025 +0200 wifi: mac80211: mlme: support extended MLD capa/ops in assoc Support passing the value from userspace on to the AP in the association and ML link reconfiguration requests. We may need to also add a driver value to or in with the field, but for now have no feature that is controlled by the driver. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.2e555beb0a76.I623f59023b47ec202fc0c7520f2b5f575b439927@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5f92619f8584..a36563a07b3b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -462,6 +462,8 @@ struct ieee80211_mgd_assoc_data { unsigned int assoc_link_id; + __le16 ext_mld_capa_ops; + u8 fils_nonces[2 * FILS_NONCE_LEN]; u8 fils_kek[FILS_MAX_KEK_LEN]; size_t fils_kek_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b2885eb80c0b..54994a6cb861 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1943,6 +1943,21 @@ ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, } skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops)); + /* Many APs have broken parsing of the extended MLD capa/ops field, + * dropping (re-)association request frames or replying with association + * response with a failure status if it's present. Without a clear + * indication as to whether the AP supports parsing this field or not do + * not include it in the common information unless strict mode is set. + */ + if (ieee80211_hw_check(&local->hw, STRICT) && + assoc_data->ext_mld_capa_ops) { + ml_elem->control |= + cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP); + common->len += 2; + skb_put_data(skb, &assoc_data->ext_mld_capa_ops, + sizeof(assoc_data->ext_mld_capa_ops)); + } + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { u16 link_present_elems[PRESENT_ELEMS_MAX] = {}; const u8 *extra_elems; @@ -2112,6 +2127,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) /* max common info field in basic multi-link element */ size += sizeof(struct ieee80211_mle_basic_common_info) + 2 + /* capa & op */ + 2 + /* ext capa & op */ 2; /* EML capa */ /* @@ -9367,6 +9383,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, else memcpy(assoc_data->ap_addr, cbss->bssid, ETH_ALEN); + assoc_data->ext_mld_capa_ops = cpu_to_le16(req->ext_mld_capa_ops); + if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -10137,7 +10155,7 @@ disconnect: static struct sk_buff * ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *add_links_data, - u16 removed_links) + u16 removed_links, __le16 ext_mld_capa_ops) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; @@ -10186,6 +10204,9 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, var_common_size += 2; } + if (ext_mld_capa_ops) + var_common_size += 2; + /* Add the common information length */ size += common_size + var_common_size; @@ -10270,6 +10291,12 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops)); } + if (ext_mld_capa_ops) { + ml_elem->control |= + cpu_to_le16(IEEE80211_MLC_RECONF_PRES_EXT_MLD_CAPA_OP); + skb_put_data(skb, &ext_mld_capa_ops, sizeof(ext_mld_capa_ops)); + } + if (sdata->u.mgd.flags & IEEE80211_STA_ENABLE_RRM) capab |= WLAN_CAPABILITY_RADIO_MEASURE; @@ -10536,7 +10563,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, * is expected to send the ML reconfiguration response frame on the link * on which the request was received. */ - skb = ieee80211_build_ml_reconf_req(sdata, data, req->rem_links); + skb = ieee80211_build_ml_reconf_req(sdata, data, req->rem_links, + cpu_to_le16(req->ext_mld_capa_ops)); if (!skb) { err = -ENOMEM; goto err_free; From 0baf7eca12399aedb603f27de3595f8038c1f964 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 270/465] wifi: cfg80211: allow IR in 20 MHz configurations JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cf4bd1608882792d4742e27a819493312904a680 Author: Anjaneyulu Date: Sat Mar 8 23:03:30 2025 +0200 wifi: cfg80211: allow IR in 20 MHz configurations Some regulatory bodies doesn't allow IR (initiate radioation) on a specific subband, but allows it for channels with a bandwidth of 20 MHz. Add a channel flag that indicates that, and consider it in cfg80211_reg_check_beaconing. While on it, fix the kernel doc of enum nl80211_reg_rule_flags and change it to use BIT(). Signed-off-by: Anjaneyulu Co-developed-by: Somashekhar Puttagangaiah Signed-off-by: Somashekhar Puttagangaiah Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.d3ab352a73ff.I8a8f79e1c9eb74936929463960ee2a324712fe51@changeid [fix typo] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 52 ++++++++++++++++++++---------------- net/wireless/chan.c | 8 +++++- net/wireless/nl80211.c | 4 +++ net/wireless/reg.c | 4 ++- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c15bdaffc8f6..ccaf9f17fabe 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -127,6 +127,8 @@ struct wiphy; * even if it is otherwise disabled. * @IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP: Allow using this channel for AP operation * with very low power (VLP), even if otherwise set to NO_IR. + * @IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY: Allow activity on a 20 MHz channel, + * even if otherwise set to NO_IR. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = BIT(0), @@ -155,6 +157,7 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = BIT(23), IEEE80211_CHAN_CAN_MONITOR = BIT(24), IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(25), + IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY = BIT(26), }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c59075acdb10..26cae1cc8727 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4335,6 +4335,8 @@ enum nl80211_wmm_rule { * otherwise completely disabled. * @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a * very low power (VLP) AP, despite being NO_IR. + * @NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY: This channel can be active in + * 20 MHz bandwidth, despite being NO_IR. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4379,6 +4381,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, NL80211_FREQUENCY_ATTR_CAN_MONITOR, NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP, + NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -4590,31 +4593,34 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed * @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted * despite NO_IR configuration. + * @NL80211_RRF_ALLOW_20MHZ_ACTIVITY: Allow activity in 20 MHz bandwidth, + * despite NO_IR configuration. */ enum nl80211_reg_rule_flags { - NL80211_RRF_NO_OFDM = 1<<0, - NL80211_RRF_NO_CCK = 1<<1, - NL80211_RRF_NO_INDOOR = 1<<2, - NL80211_RRF_NO_OUTDOOR = 1<<3, - NL80211_RRF_DFS = 1<<4, - NL80211_RRF_PTP_ONLY = 1<<5, - NL80211_RRF_PTMP_ONLY = 1<<6, - NL80211_RRF_NO_IR = 1<<7, - __NL80211_RRF_NO_IBSS = 1<<8, - NL80211_RRF_AUTO_BW = 1<<11, - NL80211_RRF_IR_CONCURRENT = 1<<12, - NL80211_RRF_NO_HT40MINUS = 1<<13, - NL80211_RRF_NO_HT40PLUS = 1<<14, - NL80211_RRF_NO_80MHZ = 1<<15, - NL80211_RRF_NO_160MHZ = 1<<16, - NL80211_RRF_NO_HE = 1<<17, - NL80211_RRF_NO_320MHZ = 1<<18, - NL80211_RRF_NO_EHT = 1<<19, - NL80211_RRF_PSD = 1<<20, - NL80211_RRF_DFS_CONCURRENT = 1<<21, - NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22, - NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23, - NL80211_RRF_ALLOW_6GHZ_VLP_AP = 1<<24, + NL80211_RRF_NO_OFDM = 1 << 0, + NL80211_RRF_NO_CCK = 1 << 1, + NL80211_RRF_NO_INDOOR = 1 << 2, + NL80211_RRF_NO_OUTDOOR = 1 << 3, + NL80211_RRF_DFS = 1 << 4, + NL80211_RRF_PTP_ONLY = 1 << 5, + NL80211_RRF_PTMP_ONLY = 1 << 6, + NL80211_RRF_NO_IR = 1 << 7, + __NL80211_RRF_NO_IBSS = 1 << 8, + NL80211_RRF_AUTO_BW = 1 << 11, + NL80211_RRF_IR_CONCURRENT = 1 << 12, + NL80211_RRF_NO_HT40MINUS = 1 << 13, + NL80211_RRF_NO_HT40PLUS = 1 << 14, + NL80211_RRF_NO_80MHZ = 1 << 15, + NL80211_RRF_NO_160MHZ = 1 << 16, + NL80211_RRF_NO_HE = 1 << 17, + NL80211_RRF_NO_320MHZ = 1 << 18, + NL80211_RRF_NO_EHT = 1 << 19, + NL80211_RRF_PSD = 1 << 20, + NL80211_RRF_DFS_CONCURRENT = 1 << 21, + NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1 << 22, + NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1 << 23, + NL80211_RRF_ALLOW_6GHZ_VLP_AP = 1 << 24, + NL80211_RRF_ALLOW_20MHZ_ACTIVITY = 1 << 25, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 9f918b77b40e..4cdb74a3f38c 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -6,7 +6,7 @@ * * Copyright 2009 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright 2018-2024 Intel Corporation + * Copyright 2018-2025 Intel Corporation */ #include @@ -1497,6 +1497,12 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, if (cfg->reg_power == IEEE80211_REG_VLP_AP) permitting_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP; + if ((cfg->iftype == NL80211_IFTYPE_P2P_GO || + cfg->iftype == NL80211_IFTYPE_AP) && + (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || + chandef->width == NL80211_CHAN_WIDTH_20)) + permitting_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY; + return _cfg80211_reg_can_beacon(wiphy, chandef, cfg->iftype, check_no_ir ? IEEE80211_CHAN_NO_IR : 0, permitting_flags); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f207f9bd8769..2c546ef2b7c8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1235,6 +1235,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY) && + nla_put_flag(msg, + NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 212e9561aae7..c1752b31734f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -5,7 +5,7 @@ * Copyright 2008-2011 Luis R. Rodriguez * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2024 Intel Corporation + * Copyright (C) 2018 - 2025 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1603,6 +1603,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_PSD; if (rd_flags & NL80211_RRF_ALLOW_6GHZ_VLP_AP) channel_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP; + if (rd_flags & NL80211_RRF_ALLOW_20MHZ_ACTIVITY) + channel_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY; return channel_flags; } From 570e6aa4c2f94bdd95dbc71e995dca38ab011c3d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 271/465] wifi: mac80211: fix U-APSD check in ML reconfiguration JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7a6a740be17e049a2742c76bb1dadb3d78c930d9 Author: Johannes Berg Date: Sat Mar 8 23:03:31 2025 +0200 wifi: mac80211: fix U-APSD check in ML reconfiguration If U-APSD isn't enabled by us, then IEEE80211_STA_UAPSD_ENABLED won't be set, but the AP can still support it in that case. Only require U-APSD from the AP if we enabled it, don't require it to be disabled on the AP if we didn't. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.b4674be12a38.I01959e448c6a2a3e8bc5d561bbae9e8d2cca616a@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 54994a6cb861..2fe46215fdc3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10493,12 +10493,11 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, } } - /* Require U-APSD support to be similar to the current valid - * links - */ - if (uapsd_supported != - !!(sdata->u.mgd.flags & IEEE80211_STA_UAPSD_ENABLED)) { + /* Require U-APSD support if we enabled it */ + if (sdata->u.mgd.flags & IEEE80211_STA_UAPSD_ENABLED && + !uapsd_supported) { err = -EINVAL; + sdata_info(sdata, "U-APSD on but not available on (all) new links\n"); goto err_free; } From 31f15bface78d1fcda1e2342a3514846594e4ce9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 272/465] wifi: cfg80211: improve supported_selector documentation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cf12d3d71e72330b2c0bba467cc5bdea2232347e Author: Johannes Berg Date: Sat Mar 8 23:03:32 2025 +0200 wifi: cfg80211: improve supported_selector documentation Improve the documentation for supported BSS selectors to make it more precise. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.ba402ff47314.I502b56111b62ea0be174ae76bd03684ae1d4aefb@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 4 ++-- include/uapi/linux/nl80211.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ccaf9f17fabe..310c4177d0d0 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3148,10 +3148,10 @@ enum cfg80211_assoc_req_flags { * included in the Current AP address field of the Reassociation Request * frame. * @flags: See &enum cfg80211_assoc_req_flags - * @supported_selectors: supported selectors in IEEE 802.11 format + * @supported_selectors: supported BSS selectors in IEEE 802.11 format * (or %NULL for no change). * If %NULL, then support for SAE_H2E should be assumed. - * @supported_selectors_len: Length of supported_selectors in octets. + * @supported_selectors_len: number of supported BSS selectors * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 26cae1cc8727..ddcc4cda74af 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2881,9 +2881,9 @@ enum nl80211_commands { * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32). * A value of 0 means all radios. * - * @NL80211_ATTR_SUPPORTED_SELECTORS: supported selectors, array of - * supported selectors as defined by IEEE 802.11 7.3.2.2 but without the - * length restriction (at most %NL80211_MAX_SUPP_SELECTORS). + * @NL80211_ATTR_SUPPORTED_SELECTORS: supported BSS Membership Selectors, array + * of supported selectors as defined by IEEE Std 802.11-2020 9.4.2.3 but + * without the length restriction (at most %NL80211_MAX_SUPP_SELECTORS). * This can be used to provide a list of selectors that are implemented * by the supplicant. If not given, support for SAE_H2E is assumed. * From 7910d6536e0e449e0e2ec20ecbffa7409ba23030 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 273/465] wifi: mac80211: allow 320 MHz FTM measurements JIRA: https://issues.redhat.com/browse/RHEL-89168 commit db97bb04c66d4d068be3fca5e28fb5de0301399d Author: Avraham Stern Date: Sat Mar 8 23:03:33 2025 +0200 wifi: mac80211: allow 320 MHz FTM measurements Add 320 MHz to the list of allowed bandwidths for FTM measurements. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.91f4cbe66817.I9205c585fca6a54a2c5a9e4db98c7781bd1fc4e1@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index aee4a451dfd6..29c37e2293df 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -796,6 +796,7 @@ int wiphy_register(struct wiphy *wiphy) BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_80P80) | BIT(NL80211_CHAN_WIDTH_160) | + BIT(NL80211_CHAN_WIDTH_320) | BIT(NL80211_CHAN_WIDTH_5) | BIT(NL80211_CHAN_WIDTH_10)))) return -EINVAL; From ba614a27c1f2edb925fc954f0d47d0d12e5401ee Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:51 +0200 Subject: [PATCH 274/465] wifi: mac80211_hwsim: Fix MLD address translation JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 65bff0be9b154621b617fc2e4bd89f1e18e97cdb Author: Ilan Peer Date: Sat Mar 8 23:03:34 2025 +0200 wifi: mac80211_hwsim: Fix MLD address translation Do address translations only between shared links. It is possible that while an non-AP MLD station and an AP MLD station have shared links, the frame is intended to be sent on a link which is not shared (for example when sending a probe response). Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.1aa461270bb6.Ic21592e1b1634653f02b80628cb2152f6e9de367@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/virtual/mac80211_hwsim.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 7a45f539fd49..98f8d9bad77d 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4,7 +4,7 @@ * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2024 Intel Corporation + * Copyright (C) 2018 - 2025 Intel Corporation */ /* @@ -1983,11 +1983,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (sta && sta->mlo) { - if (WARN_ON(!link_sta)) { - ieee80211_free_txskb(hw, skb); - return; - } + /* Do address translations only between shared links. It is + * possible that while an non-AP MLD station and an AP MLD + * station have shared links, the frame is intended to be sent + * on a link which is not shared (for example when sending a + * probe response). + */ + if (sta && sta->mlo && link_sta) { /* address translation to link addresses on TX */ ether_addr_copy(hdr->addr1, link_sta->addr); ether_addr_copy(hdr->addr2, bss_conf->addr); From bed3e2732a44352e642474a7747f9387d408ce88 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 275/465] wifi: mac80211: fix userspace_selectors corruption JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 700014d3ad1fd6e55c8f9ffa817514d3fbb5286e Author: Johannes Berg Date: Sat Mar 8 23:03:35 2025 +0200 wifi: mac80211: fix userspace_selectors corruption Spotted during code review, the selectors need to be large enough for a 128-bit bitmap, not a single unsigned long, otherwise we have stack corruption. We should also allow passing selectors from userspace, but that should be a separate change. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.8f1bcf96a504.Ibeb8970c82a30c97279a4cc4e68faca5df1813a5@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2fe46215fdc3..9f229e9f211f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9915,8 +9915,6 @@ EXPORT_SYMBOL(ieee80211_disable_rssi_reports); static void ieee80211_ml_reconf_selectors(unsigned long *userspace_selectors) { - *userspace_selectors = 0; - /* these selectors are mandatory for ML reconfiguration */ set_bit(BSS_MEMBERSHIP_SELECTOR_SAE_H2E, userspace_selectors); set_bit(BSS_MEMBERSHIP_SELECTOR_HE_PHY, userspace_selectors); @@ -9936,7 +9934,7 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.reconf.removed_links; u16 link_mask, valid_links; unsigned int link_id; - unsigned long userspace_selectors; + unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; size_t orig_len = len; u8 i, group_key_data_len; u8 *pos; @@ -10044,7 +10042,7 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, } ieee80211_vif_set_links(sdata, valid_links, sdata->vif.dormant_links); - ieee80211_ml_reconf_selectors(&userspace_selectors); + ieee80211_ml_reconf_selectors(userspace_selectors); link_mask = 0; for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct cfg80211_bss *cbss = add_links_data->link[link_id].bss; @@ -10090,7 +10088,7 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, link->u.mgd.conn = add_links_data->link[link_id].conn; if (ieee80211_prep_channel(sdata, link, link_id, cbss, true, &link->u.mgd.conn, - &userspace_selectors)) { + userspace_selectors)) { link_info(link, "mlo: reconf: prep_channel failed\n"); goto disconnect; } @@ -10428,14 +10426,14 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, */ if (added_links) { bool uapsd_supported; - unsigned long userspace_selectors; + unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; uapsd_supported = true; - ieee80211_ml_reconf_selectors(&userspace_selectors); + ieee80211_ml_reconf_selectors(userspace_selectors); for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct ieee80211_supported_band *sband; @@ -10511,7 +10509,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, data->link[link_id].bss, true, &data->link[link_id].conn, - &userspace_selectors); + userspace_selectors); if (err) goto err_free; } From 758ab369b774054510215d6204ebb88690788082 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 276/465] wifi: mac80211: fix warning on disconnect during failed ML reconf JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0e104aa3676d020f6c442cd7fbaeb72adaaab6fc Author: Johannes Berg Date: Sat Mar 8 23:03:36 2025 +0200 wifi: mac80211: fix warning on disconnect during failed ML reconf If multi-link reconfiguration fails, we can disconnect with a local link already allocated but the BSS entry not assigned yet, which leads to a warning in cfg80211. Add a check to avoid the warning. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.699bd9cbabe5.I599d5ff69092a65e916e2acd25137ae9df8debe8@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9f229e9f211f..329d62d05864 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4466,7 +4466,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) struct ieee80211_link_data *link; link = sdata_dereference(sdata->link[link_id], sdata); - if (!link) + if (!link || !link->conf->bss) continue; cfg80211_unlink_bss(local->hw.wiphy, link->conf->bss); link->conf->bss = NULL; From dc7182568c992a9b759f7e73b9dcdd62988ef9c8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 277/465] wifi: cfg80211: Update the link address when a link is added JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e16caea70610ed4226034dfcdaa5c43b36ff9e0a Author: Ilan Peer Date: Sat Mar 8 23:03:37 2025 +0200 wifi: cfg80211: Update the link address when a link is added When links are added, update the wireless device link addresses based on the information provided by the driver. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.d694a9125aba.I79b010ea9aab47893e4f22c266362fde30b7f9ac@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 1 + net/wireless/mlme.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 310c4177d0d0..011ca3b06807 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -9770,6 +9770,7 @@ struct cfg80211_mlo_reconf_done_data { u16 added_links; struct { struct cfg80211_bss *bss; + u8 *addr; } links[IEEE80211_MLD_MAX_NUM_LINKS]; }; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 956d33b219df..05d44a443518 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1360,6 +1360,10 @@ void cfg80211_mlo_reconf_add_done(struct net_device *dev, if (data->added_links & BIT(link_id)) { wdev->links[link_id].client.current_bss = bss_from_pub(bss); + + memcpy(wdev->links[link_id].addr, + data->links[link_id].addr, + ETH_ALEN); } else { cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_put_bss(wiphy, bss); From 4b330ffb97a3dfe6710c047996fcd4b5eeb1060d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 278/465] wifi: mac80211: Notify cfg80211 about added link addresses JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2160998ded514404140617286dd53a35983e5090 Author: Ilan Peer Date: Sat Mar 8 23:03:38 2025 +0200 wifi: mac80211: Notify cfg80211 about added link addresses When ML reconfiguration is done and new links are added, update cfg80211 with the addresses of the newly added links. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.0c6bf8b1bef3.I2aa16801f07321a580dd7dce4a074a3486f627f1@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 329d62d05864..916eaea9e0f3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10136,8 +10136,11 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, done_data.len = orig_len; done_data.added_links = link_mask; - for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { done_data.links[link_id].bss = add_links_data->link[link_id].bss; + done_data.links[link_id].addr = + add_links_data->link[link_id].addr; + } cfg80211_mlo_reconf_add_done(sdata->dev, &done_data); kfree(sdata->u.mgd.reconf.add_links_data); From b6b4dbec7fc46f33527f8568412cce369005a62f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 279/465] wifi: mac80211: fix ML reconf reset in disconnect JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 82306ea157ad5d1dc8126f81a989e61bf1001c0d Author: Johannes Berg Date: Sat Mar 8 23:03:39 2025 +0200 wifi: mac80211: fix ML reconf reset in disconnect If disconnecting while ML reconfiguration is in progress, the data isn't freed because the reset call is too late, after the vif already switches to non-MLD. Move the call to fix that. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.abaea69cde42.I7e6b35731ded94fc2d68a2d4ecf81873712c268e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 916eaea9e0f3..decc2cb57ea2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4152,16 +4152,16 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, wiphy_work_cancel(sdata->local->hw.wiphy, &ifmgd->teardown_ttlm_work); - ieee80211_vif_set_links(sdata, 0, 0); - - ifmgd->mcast_seq_last = IEEE80211_SN_MODULO; - /* if disconnection happens in the middle of the ML reconfiguration * flow, cfg80211 must called to release the BSS references obtained * when the flow started. */ ieee80211_ml_reconf_reset(sdata); + ieee80211_vif_set_links(sdata, 0, 0); + + ifmgd->mcast_seq_last = IEEE80211_SN_MODULO; + ifmgd->epcs.enabled = false; ifmgd->epcs.dialog_token = 0; } From a78a3b5f819163bfe398768a5ae81bd2a74a295a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 280/465] wifi: mac80211: don't include MLE in ML reconf per-STA profile JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c3171bed65ec323803b6b73f74017f7d0fd7aa6c Author: Johannes Berg Date: Sat Mar 8 23:03:40 2025 +0200 wifi: mac80211: don't include MLE in ML reconf per-STA profile In the multi-link reconfiguration frame, the per-STA profile for added links shouldn't include the multi-link element. Set the association ID to an invalid value, so it doesn't erroneously match the link ID if that happens to be zero. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.8e5be244c70f.I3472cd5c347814ee3600869a88488997bcd43224@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mlme.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a36563a07b3b..217e949bb756 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -460,7 +460,7 @@ struct ieee80211_mgd_assoc_data { bool s1g; bool spp_amsdu; - unsigned int assoc_link_id; + s8 assoc_link_id; __le16 ext_mld_capa_ops; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index decc2cb57ea2..27d9daa279ec 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10435,6 +10435,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, if (!data) return -ENOMEM; + data->assoc_link_id = -1; + uapsd_supported = true; ieee80211_ml_reconf_selectors(userspace_selectors); for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; From 62f4a1366cd6749962e106cca658f007cd537e46 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 281/465] wifi: mac80211: set WMM in ML reconfiguration JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ff4d8998e84dcf52ba74716ab0512217eace6734 Author: Johannes Berg Date: Sat Mar 8 23:03:41 2025 +0200 wifi: mac80211: set WMM in ML reconfiguration In the per-STA profiles for added links in multi-link reconfiguration the WMM element should be included. Fix that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308225541.59902f783420.I362c3101d3f523a8db37c16cd7b5f573d76a36e6@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 27d9daa279ec..49a35a011453 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10436,6 +10436,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, return -ENOMEM; data->assoc_link_id = -1; + data->wmm = true; uapsd_supported = true; ieee80211_ml_reconf_selectors(userspace_selectors); From 1b4d9f4a53cb5d4e3943116129f7f7eb07c05a25 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 282/465] wifi: iwlwifi: add support for BE213 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 16a8d9a739430bec9c11eda69226c5a39f3478aa Author: Emmanuel Grumbach Date: Sat Mar 8 23:19:12 2025 +0200 wifi: iwlwifi: add support for BE213 Add the BE213 device. This is just like BE211 but with a limitation on the bandwidth. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231426.f02f4d7fc73b.Idaf000dd311358e3b50a511f4efc1cc720abd58b@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 ++ .../net/wireless/intel/iwlwifi/iwl-config.h | 1 + .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 12 +++++++---- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 21 ++++++++++++++++--- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index eb56af9a8411..05cbb80ab575 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -142,6 +142,8 @@ const struct iwl_cfg_trans_params iwl_sc_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; +const char iwl_sp_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz"; + const struct iwl_cfg iwl_cfg_sc = { .fw_name_mac = "sc", IWL_DEVICE_SC, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 7e4864c00780..b9bd89bfdd74 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -550,6 +550,7 @@ extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_fm_name[]; extern const char iwl_wh_name[]; +extern const char iwl_sp_name[]; extern const char iwl_gl_name[]; extern const char iwl_mtp_name[]; extern const char iwl_dr_name[]; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 08269168b2fa..cd1b0048bb6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -944,7 +944,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: - if (!trans->reduced_cap_sku) { + if (!trans->reduced_cap_sku && + trans->bw_limit >= 320) { iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |= @@ -1098,15 +1099,18 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - if (trans->reduced_cap_sku) { + if (trans->bw_limit < 320 || trans->reduced_cap_sku) { memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0, sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320)); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; + } + + if (trans->reduced_cap_sku) { iftype_data->eht_cap.eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_mcs_nss_supp.bw._160.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[8] &= ~IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA; - iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= - ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; } } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 03f7eb46bbc7..93446c374008 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1187,8 +1187,13 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_wh_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + 160, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc, iwl_sp_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, @@ -1202,8 +1207,13 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_wh_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + 160, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2, iwl_sp_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, @@ -1217,8 +1227,13 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_wh_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + 160, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_sc2f, iwl_sp_name), /* Dr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, From c2feca0e0695497f4bc1fd1959a549322c1b70af Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:52 +0200 Subject: [PATCH 283/465] wifi: iwlwifi: fix the ECKV UEFI variable name JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3ea2970b0578011ab8402ad0cff39712255f63df Author: Emmanuel Grumbach Date: Sat Mar 8 23:19:13 2025 +0200 wifi: iwlwifi: fix the ECKV UEFI variable name This UEFI variable name was badly named. Fix its name and also use the right GUID to find it: we need to use the BT_WIFI (a.k.a. Common) GUID. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231426.78c998d0fa71.I2bc9d72c1dc2c4d7028f0265634a940c2fadbbb5@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 8 +++++--- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 78bd0eb7aa92..b4438b1f8dad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2024 Intel Corporation + * Copyright(c) 2021-2025 Intel Corporation */ #include "iwl-drv.h" @@ -681,8 +681,10 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) struct uefi_cnv_var_eckv *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME, - "ECKV", sizeof(*data), NULL); + data = iwl_uefi_get_verified_variable_guid(fwrt->trans, + &IWL_EFI_WIFI_BT_GUID, + IWL_UEFI_ECKV_NAME, + "ECKV", sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 0c8943a8bd01..eb3c05417da3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright(c) 2021-2024 Intel Corporation + * Copyright(c) 2021-2025 Intel Corporation */ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ @@ -19,7 +19,7 @@ #define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" #define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" #define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" -#define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" +#define IWL_UEFI_ECKV_NAME L"UefiCnvCommonECKV" #define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg" #define IWL_UEFI_WBEM_NAME L"UefiCnvWlanWBEM" #define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing" From c5ed8f562aae10f59b6d8d42144451baa839393d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 284/465] wifi: iwlwifi: fix print for ECKV JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8d4aaec6608298451f55c65f532a8ec98436bec1 Author: Emmanuel Grumbach Date: Sat Mar 8 23:19:14 2025 +0200 wifi: iwlwifi: fix print for ECKV The print was obviously wrong. We are handling ECKV and not WRDD in this function. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231426.e006c6b21432.I318fed724709f9ee7a0c369e1cf5e1038ddd546a@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index b4438b1f8dad..386aadbce2a2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -690,7 +690,7 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) if (data->revision != IWL_UEFI_ECKV_REVISION) { ret = -EINVAL; - IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI ECKV revision:%d\n", data->revision); goto out; } From 86d1029889a8c5c72f7d76fac451fe166ef725b9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 285/465] wifi: iwlwifi: mvm: cleanup of TAS structure and enums JIRA: https://issues.redhat.com/browse/RHEL-89168 commit abecf9ba94aa6784a33a5526bb83a43c977322e4 Author: Pagadala Yesu Anjaneyulu Date: Sat Mar 8 23:19:15 2025 +0200 wifi: iwlwifi: mvm: cleanup of TAS structure and enums Removed mvm prefix from the following structures and enum names: 1. struct iwl_tas_status_per_mac 2. struct iwl_tas_status_resp 3. enum iwl_tas_dyna_status 4. enum iwl_tas_statically_disabled_reason As these structures and enums are not specific to mvm. Replaced TAS_LMAC_BAND_LB, TAS_LMAC_BAND_HB, and TAS_LMAC_BAND_UHB macros with a generic BAND macro, as these macros are not specific to TAS. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.59144bee9987.Ic2678dcb5afdacc2ca234d4aa4901e7e1f6e8dbb@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/fw/api/debug.h | 39 ++++++++----------- .../net/wireless/intel/iwlwifi/mvm/debugfs.c | 28 +++++-------- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index aa88e91d117e..e1b6795c1f64 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -51,7 +51,7 @@ enum iwl_debug_cmds { /** * @GET_TAS_STATUS: * sends command to fw to get TAS status - * the response is &struct iwl_mvm_tas_status_resp + * the response is &struct iwl_tas_status_resp */ GET_TAS_STATUS = 0xA, /** @@ -439,25 +439,20 @@ struct iwl_dbg_dump_complete_cmd { __le32 tp_data; } __packed; /* FW_DUMP_COMPLETE_CMD_API_S_VER_1 */ -#define TAS_LMAC_BAND_HB 0 -#define TAS_LMAC_BAND_LB 1 -#define TAS_LMAC_BAND_UHB 2 -#define TAS_LMAC_BAND_INVALID 3 - /** - * struct iwl_mvm_tas_status_per_mac - tas status per lmac + * struct iwl_tas_status_per_mac - tas status per lmac * @static_status: tas statically enabled or disabled per lmac - TRUE/FALSE * @static_dis_reason: TAS static disable reason, uses - * &enum iwl_mvm_tas_statically_disabled_reason + * &enum iwl_tas_statically_disabled_reason * @dynamic_status: Current TAS status. uses - * &enum iwl_mvm_tas_dyna_status + * &enum iwl_tas_dyna_status * @near_disconnection: is TAS currently near disconnection per lmac? - TRUE/FALSE * @max_reg_pwr_limit: Regulatory power limits in dBm * @sar_limit: SAR limits per lmac in dBm * @band: Band per lmac * @reserved: reserved */ -struct iwl_mvm_tas_status_per_mac { +struct iwl_tas_status_per_mac { u8 static_status; u8 static_dis_reason; u8 dynamic_status; @@ -466,35 +461,35 @@ struct iwl_mvm_tas_status_per_mac { __le16 sar_limit; u8 band; u8 reserved[3]; -} __packed; /*DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1*/ +} __packed; /* DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1 */ /** - * struct iwl_mvm_tas_status_resp - Response to GET_TAS_STATUS + * struct iwl_tas_status_resp - Response to GET_TAS_STATUS * @tas_fw_version: TAS FW version * @is_uhb_for_usa_enable: is UHB enabled in USA? - TRUE/FALSE * @curr_mcc: current mcc * @block_list: country block list * @tas_status_mac: TAS status per lmac, uses - * &struct iwl_mvm_tas_status_per_mac + * &struct iwl_tas_status_per_mac * @in_dual_radio: is TAS in dual radio? - TRUE/FALSE * @uhb_allowed_flags: see &enum iwl_tas_uhb_allowed_flags. * This member is valid only when fw has * %IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT capability. * @reserved: reserved */ -struct iwl_mvm_tas_status_resp { +struct iwl_tas_status_resp { u8 tas_fw_version; u8 is_uhb_for_usa_enable; __le16 curr_mcc; __le16 block_list[16]; - struct iwl_mvm_tas_status_per_mac tas_status_mac[2]; + struct iwl_tas_status_per_mac tas_status_mac[2]; u8 in_dual_radio; u8 uhb_allowed_flags; u8 reserved[2]; -} __packed; /*DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3*/ +} __packed; /* DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3 */ /** - * enum iwl_mvm_tas_dyna_status - TAS current running status + * enum iwl_tas_dyna_status - TAS current running status * @TAS_DYNA_INACTIVE: TAS status is inactive * @TAS_DYNA_INACTIVE_MVM_MODE: TAS is disabled due because FW is in MVM mode * or is in softap mode. @@ -507,7 +502,7 @@ struct iwl_mvm_tas_status_resp { * @TAS_DYNA_ACTIVE: TAS is currently active * @TAS_DYNA_STATUS_MAX: TAS status max value */ -enum iwl_mvm_tas_dyna_status { +enum iwl_tas_dyna_status { TAS_DYNA_INACTIVE, TAS_DYNA_INACTIVE_MVM_MODE, TAS_DYNA_INACTIVE_TRIGGER_MODE, @@ -516,16 +511,16 @@ enum iwl_mvm_tas_dyna_status { TAS_DYNA_ACTIVE, TAS_DYNA_STATUS_MAX, -}; /*_TAS_DYNA_STATUS_E*/ +}; /** - * enum iwl_mvm_tas_statically_disabled_reason - TAS statically disabled reason + * enum iwl_tas_statically_disabled_reason - TAS statically disabled reason * @TAS_DISABLED_DUE_TO_BIOS: TAS is disabled because TAS is disabled in BIOS * @TAS_DISABLED_DUE_TO_SAR_6DBM: TAS is disabled because SAR limit is less than 6 Dbm * @TAS_DISABLED_REASON_INVALID: TAS disable reason is invalid * @TAS_DISABLED_REASON_MAX: TAS disable reason max value */ -enum iwl_mvm_tas_statically_disabled_reason { +enum iwl_tas_statically_disabled_reason { TAS_DISABLED_DUE_TO_BIOS, TAS_DISABLED_DUE_TO_SAR_6DBM, TAS_DISABLED_REASON_INVALID, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 55d035b896e9..e032f1fb4022 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -542,7 +542,7 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_mvm_tas_status_resp *rsp = NULL; + struct iwl_tas_status_resp *rsp = NULL; static const size_t bufsz = 1024; char *buff, *pos, *endpos; const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { @@ -598,23 +598,19 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "TAS Conclusion:\n"); for (i = 0; i < rsp->in_dual_radio + 1; i++) { - if (rsp->tas_status_mac[i].band != TAS_LMAC_BAND_INVALID && - rsp->tas_status_mac[i].dynamic_status & BIT(TAS_DYNA_ACTIVE)) { + if (rsp->tas_status_mac[i].dynamic_status & + BIT(TAS_DYNA_ACTIVE)) { pos += scnprintf(pos, endpos - pos, "\tON for "); switch (rsp->tas_status_mac[i].band) { - case TAS_LMAC_BAND_HB: + case PHY_BAND_5: pos += scnprintf(pos, endpos - pos, "HB\n"); break; - case TAS_LMAC_BAND_LB: + case PHY_BAND_24: pos += scnprintf(pos, endpos - pos, "LB\n"); break; - case TAS_LMAC_BAND_UHB: + case PHY_BAND_6: pos += scnprintf(pos, endpos - pos, "UHB\n"); break; - case TAS_LMAC_BAND_INVALID: - pos += scnprintf(pos, endpos - pos, - "INVALID BAND\n"); - break; default: pos += scnprintf(pos, endpos - pos, "Unsupported band (%d)\n", @@ -668,20 +664,16 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "TAS status for "); switch (rsp->tas_status_mac[i].band) { - case TAS_LMAC_BAND_HB: + case PHY_BAND_5: pos += scnprintf(pos, endpos - pos, "High band\n"); break; - case TAS_LMAC_BAND_LB: + case PHY_BAND_24: pos += scnprintf(pos, endpos - pos, "Low band\n"); break; - case TAS_LMAC_BAND_UHB: + case PHY_BAND_6: pos += scnprintf(pos, endpos - pos, "Ultra high band\n"); break; - case TAS_LMAC_BAND_INVALID: - pos += scnprintf(pos, endpos - pos, - "INVALID band\n"); - break; default: pos += scnprintf(pos, endpos - pos, "Unsupported band (%d)\n", From 8d2c2f93ec862ad0b06344cd999c3221024e9e4f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 286/465] wifi: iwlwifi: Add new TAS disable reason for invalid table source JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8e3c9e6a5200c01f79d95f442999719bbed0e196 Author: Pagadala Yesu Anjaneyulu Date: Sat Mar 8 23:19:16 2025 +0200 wifi: iwlwifi: Add new TAS disable reason for invalid table source The new reason is added to the iwl_tas_statically_disabled_reason enum and the corresponding message is updated in the iwl_dbgfs_tas_get_status_read(). Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.5e1272ef3508.I24f668ae716bee20cba15fdc73c3363693bbaf73@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/fw/api/debug.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index e1b6795c1f64..0cf1e5124fba 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -518,12 +518,15 @@ enum iwl_tas_dyna_status { * @TAS_DISABLED_DUE_TO_BIOS: TAS is disabled because TAS is disabled in BIOS * @TAS_DISABLED_DUE_TO_SAR_6DBM: TAS is disabled because SAR limit is less than 6 Dbm * @TAS_DISABLED_REASON_INVALID: TAS disable reason is invalid + * @TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID: TAS is disabled due to + * table source invalid * @TAS_DISABLED_REASON_MAX: TAS disable reason max value */ enum iwl_tas_statically_disabled_reason { TAS_DISABLED_DUE_TO_BIOS, TAS_DISABLED_DUE_TO_SAR_6DBM, TAS_DISABLED_REASON_INVALID, + TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID, TAS_DISABLED_REASON_MAX, }; /*_TAS_STATICALLY_DISABLED_REASON_E*/ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index e032f1fb4022..12826563c2fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -552,6 +552,8 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, "Due To SAR Limit Less Than 6 dBm", [TAS_DISABLED_REASON_INVALID] = "N/A", + [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] = + "Due to table source invalid", }; const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { [TAS_DYNA_INACTIVE] = "INACTIVE", From 087c3da3f6d2ed3428859240e14a282d3b32428d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 287/465] wifi: iwlwifi: mark Br device not integrated JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5f0ab2f35a43773a0dfe1297129a8dbff906b932 Author: Johannes Berg Date: Sat Mar 8 23:19:17 2025 +0200 wifi: iwlwifi: mark Br device not integrated This is a discrete device, don't mark it as integrated. This also means we cannot set the LTR delay. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.9bb69393fcc9.I197129383e5441c8139cbb0e810ae0b71198a37c@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 69176b59b4de..48b487f6e366 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -148,11 +148,9 @@ const struct iwl_cfg_trans_params iwl_br_trans_cfg = { .mq_rx_supported = true, .rf_id = true, .gen2 = true, - .integrated = true, .umac_prph_offset = 0x300000, .xtal_latency = 12000, .low_latency_xtal = true, - .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; const char iwl_br_name[] = "Intel(R) TBD Br device"; From a650a14802595d014165e05e09ba9432d7e2e081 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 288/465] wifi: iwlwifi: fix debug actions order JIRA: https://issues.redhat.com/browse/RHEL-89168 commit eb29b4ffafb20281624dcd2cbb768d6f30edf600 Author: Johannes Berg Date: Sat Mar 8 23:19:18 2025 +0200 wifi: iwlwifi: fix debug actions order The order of actions taken for debug was implemented incorrectly. Now we implemented the dump split and do the FW reset only in the middle of the dump (rather than the FW killing itself on error.) As a result, some of the actions taken when applying the config will now crash the device, so we need to fix the order. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.6de7fa8e63ed.I40632c48e2a67a8aca05def572a934b88ce7934b@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 08d990ba8a79..ce787326aa69 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include "iwl-drv.h" @@ -1372,15 +1372,15 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt, switch (tp_id) { case IWL_FW_INI_TIME_POINT_EARLY: iwl_dbg_tlv_init_cfg(fwrt); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_update_drams(fwrt); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; case IWL_FW_INI_TIME_POINT_AFTER_ALIVE: iwl_dbg_tlv_apply_buffers(fwrt); iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; case IWL_FW_INI_TIME_POINT_PERIODIC: iwl_dbg_tlv_set_periodic_trigs(fwrt); @@ -1390,14 +1390,14 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_TIME_POINT_MISSED_BEACONS: case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION: iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, iwl_dbg_tlv_check_fw_pkt); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; default: iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; } } From d0a344c5e499b8f0e4fc0ab899b8ec5d9777b531 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 289/465] wifi: iwlwifi: w/a FW SMPS mode selection JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b2e709805ce955f80803b7cab3421813c79e1df4 Author: Daniel Gabay Date: Sat Mar 8 23:19:19 2025 +0200 wifi: iwlwifi: w/a FW SMPS mode selection The FW is now responsible of determining the SMPS mode. If the user disabled power save in a certain vif, we send the vif-level power command to clear out the POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK bit for that vif. But erroneously, the FW checks DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK in the device-level command to determine the SMPS mode. To W/A this, send also the device-level command when the power save of a vif changes, and disable power save if there is any vif that has power save disabled. Signed-off-by: Daniel Gabay Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.7bf205efa027.I2c793ff1fc2a6779a95faaee1ded348100fd97f1@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 15 +++++++++++++++ .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 3 ++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 20aaef913e3f..1e916a0ce082 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4100,6 +4100,20 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, return 0; } +void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool update) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!iwl_mvm_has_rlc_offload(mvm)) + return; + + mvmvif->ps_disabled = !vif->cfg.ps; + + if (update) + iwl_mvm_power_update_mac(mvm); +} + /* Common part for MLD and non-MLD modes */ int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -4192,6 +4206,7 @@ int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, new_state == IEEE80211_STA_AUTHORIZED) { ret = iwl_mvm_sta_state_assoc_to_authorized(mvm, vif, sta, callbacks); + iwl_mvm_smps_workaround(mvm, vif, true); } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { ret = iwl_mvm_sta_state_authorized_to_assoc(mvm, vif, sta, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 341a2a7a49ec..78d7153a0cfc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2024 Intel Corporation + * Copyright (C) 2022-2025 Intel Corporation */ #include "mvm.h" @@ -887,6 +887,7 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, } if (changes & BSS_CHANGED_PS) { + iwl_mvm_smps_workaround(mvm, vif, false); ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index b6e0b63cbd3c..f6391c7a3e29 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -3031,4 +3031,7 @@ iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, bool is_ap); + +void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool update); #endif /* __IWL_MVM_H__ */ From 8c08a17aee8da199c6527cafff297d8aaf541f51 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 290/465] wifi: iwlwifi: mvm: Fix bit size calculation in iwl_dbgfs_tas_get_status_read JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9002ba67fb1e275f9cba38b4af42ebe43638fb5c Author: Pagadala Yesu Anjaneyulu Date: Sat Mar 8 23:19:20 2025 +0200 wifi: iwlwifi: mvm: Fix bit size calculation in iwl_dbgfs_tas_get_status_read Corrected the bit size calculation in the for_each_set_bit macro in the iwl_dbgfs_tas_get_status_read(). The previous implementation used sizeof(dyn_status), which only accounts for the number of bytes. This has been updated to TAS_DYNA_STATUS_MAX to ensure the loop iterates over the correct number of valid bits in dyn_status. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.65d373e4a10e.If2cea63035333b07849e5a2c2a4f5dc5c5239595@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 12826563c2fe..671d3f8d79c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -698,11 +698,9 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "Dynamic status:\n"); dyn_status = (rsp->tas_status_mac[i].dynamic_status); - for_each_set_bit(tmp, &dyn_status, sizeof(dyn_status)) { - if (tmp >= 0 && tmp < TAS_DYNA_STATUS_MAX) - pos += scnprintf(pos, endpos - pos, - "\t%s (%d)\n", - tas_current_status[tmp], tmp); + for_each_set_bit(tmp, &dyn_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(pos, endpos - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); } pos += scnprintf(pos, endpos - pos, From b92375420f01b2903928e7a51e452363040789ee Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 291/465] wifi: iwlwifi: bump FW API to 98 for BZ/SC/DR devices JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d8bc6f24c65a1b8216717856cbc46ed904c2bb16 Author: Miri Korenblit Date: Sat Mar 8 23:19:21 2025 +0200 wifi: iwlwifi: bump FW API to 98 for BZ/SC/DR devices Start supporting API versions 97 and 98 for those devices. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.55b5455795aa.I333f1e7fa31f9da1d40f668660c1e25b93ff469e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 36e8e3a991dc..dbd303385157 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 96 +#define IWL_BZ_UCODE_API_MAX 98 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 92 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 48b487f6e366..282b9b846c3a 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include #include @@ -9,7 +9,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_DR_UCODE_API_MAX 96 +#define IWL_DR_UCODE_API_MAX 98 /* Lowest firmware API version supported */ #define IWL_DR_UCODE_API_MIN 96 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 05cbb80ab575..a9fc9e8ec431 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include #include @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 96 +#define IWL_SC_UCODE_API_MAX 98 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 92 From 14ce02d2f3d70db845e024b05fcf69fa105cfd4f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:53 +0200 Subject: [PATCH 292/465] wifi: iwlwifi: bump minimum API version in BZ/SC to 93 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 96a3598ba82edb5d2498b9de298aed04caec4b6c Author: Miri Korenblit Date: Sat Mar 8 23:19:22 2025 +0200 wifi: iwlwifi: bump minimum API version in BZ/SC to 93 Stop supporting older FWs. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.73e962ca3e6c.I942fa8cafc1791b9330ad18e2599fae11d7b3336@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index dbd303385157..f055255a7c93 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -13,7 +13,7 @@ #define IWL_BZ_UCODE_API_MAX 98 /* Lowest firmware API version supported */ -#define IWL_BZ_UCODE_API_MIN 92 +#define IWL_BZ_UCODE_API_MIN 93 /* NVM versions */ #define IWL_BZ_NVM_VERSION 0x0a1d diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index a9fc9e8ec431..670031fd60dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -13,7 +13,7 @@ #define IWL_SC_UCODE_API_MAX 98 /* Lowest firmware API version supported */ -#define IWL_SC_UCODE_API_MIN 92 +#define IWL_SC_UCODE_API_MIN 93 /* NVM versions */ #define IWL_SC_NVM_VERSION 0x0a1d From 2e61a0828ff43e0e19f4c30e07e34e7ea4335dc3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 293/465] wifi: iwlwifi: don't warn when if there is a FW error JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c7f50d0433a016d43681592836a3d484817bfb34 Author: Miri Korenblit Date: Sat Mar 8 23:19:23 2025 +0200 wifi: iwlwifi: don't warn when if there is a FW error iwl_trans_reclaim is warning if it is called when the FW is not alive. But if it is called when there is a pending restart, i.e. after a FW error, there is no need to warn, instead - return silently. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.ba3d90b22c25.I9332506af1997faefcf0bdb51d98d5e874051722@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 2d0e09aa9cbe..c1607b6d0759 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021, 2023-2024 Intel Corporation + * Copyright (C) 2019-2021, 2023-2025 Intel Corporation */ #include #include @@ -641,6 +641,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_tx); void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs, bool is_flush) { + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return; + if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "bad state = %d\n", trans->state)) return; From 5b3a5791fe952289c36f6e73df836688e91045d3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 294/465] wifi: iwlwifi: mvm: fix setting the TK when associated JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3ad61970ac9e164be1b09b46c01aa942e8966132 Author: Avraham Stern Date: Sat Mar 8 23:19:25 2025 +0200 wifi: iwlwifi: mvm: fix setting the TK when associated When running secured ranging and the initiator is associated with the responder, the TK was not set in the range request command. Fix it. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250308231427.603dc31579d9.Icd19d797e56483c08dd22c55b96fee481c4d2f3d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index dfb25b964f0e..a493ef6bedc3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -672,7 +672,11 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, target.bssid = bssid; target.cipher = cipher; + target.tk = NULL; ieee80211_iter_keys(mvm->hw, vif, iter, &target); + + if (!WARN_ON(!target.tk)) + memcpy(tk, target.tk, TK_11AZ_LEN); } else { memcpy(tk, entry->tk, sizeof(entry->tk)); } From 538df20ab9e71e2a2cc3691545150f8617d81afa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 295/465] wifi: iwlwifi: mld: fix build with CONFIG_PM_SLEEP undefined JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 834bfc698bf7cee3bf61c0209b410c7f97a54108 Author: Miri Korenblit Date: Sun Mar 9 07:36:39 2025 +0200 wifi: iwlwifi: mld: fix build with CONFIG_PM_SLEEP undefined fw_status.in_d3 is only defined under CONFIG_PM_SLEEP. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.6f7e44a27b87.I78b9311019b59477a1961cddc4640b255ceda651@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/hcmd.h | 4 +++- drivers/net/wireless/intel/iwlwifi/mld/mld.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h index 773bc1b09392..64a8d4248324 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #ifndef __iwl_mld_hcmd_h__ #define __iwl_mld_hcmd_h__ @@ -10,8 +10,10 @@ static inline int iwl_mld_send_cmd(struct iwl_mld *mld, struct iwl_host_cmd *cmd /* No commands, including the d3 related commands, should be sent * after entering d3 */ +#ifdef CONFIG_PM_SLEEP if (WARN_ON(mld->fw_status.in_d3)) return -EIO; +#endif if (!(cmd->flags & CMD_ASYNC)) lockdep_assert_wiphy(mld->wiphy); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 6eda6081c8b4..1ab98ba42c76 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -296,7 +296,9 @@ iwl_cleanup_mld(struct iwl_mld *mld) CLEANUP_STRUCT(mld); CLEANUP_STRUCT(&mld->scan); +#ifdef CONFIG_PM_SLEEP mld->fw_status.in_d3 = false; +#endif iwl_mld_low_latency_restart_cleanup(mld); From d1fb17dd51a1fe2484b884fbd56ab818c9311b1b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 296/465] wifi: iwlwifi: mld: fix SMPS W/A JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cb9716eae2dfac2f70fea19df928e61bed314ce9 Author: Miri Korenblit Date: Sun Mar 9 07:36:40 2025 +0200 wifi: iwlwifi: mld: fix SMPS W/A If the user disables power save of a vif that didn't have it enabled (for example before association), mac80211 will not notify the driver with BSS_CHANGED_PS. This will cause the driver to not update the device-level power save to disabled. Fix this by checking the vif's power save status upon authorization, and stop considering the vif's power save status on disassociation. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.1cdeb78b19ba.I58fe02c062524029071b04b093a1b09c5e46f4ef@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/iface.h | 2 ++ .../net/wireless/intel/iwlwifi/mld/mac80211.c | 25 +++++++++++++++---- .../net/wireless/intel/iwlwifi/mld/power.c | 3 ++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 2c928113f680..57910660ed18 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -134,6 +134,7 @@ struct iwl_mld_emlsr { * @beacon_inject_active: indicates an active debugfs beacon ie injection * @low_latency_causes: bit flags, indicating the causes for low-latency, * see @iwl_mld_low_latency_cause. + * @ps_disabled: indicates that PS is disabled for this interface * @mld: pointer to the mld structure. * @deflink: default link data, for use in non-MLO, * @link: reference to link data for each valid link, for use in MLO. @@ -159,6 +160,7 @@ struct iwl_mld_vif { bool beacon_inject_active; #endif u8 low_latency_causes; + bool ps_disabled; ); /* And here fields that survive a fw restart */ struct iwl_mld *mld; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index d73bd0179f7d..ba149581e25d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1262,6 +1262,23 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower); } +static void +iwl_mld_smps_wa(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* Send the device-level power commands since the + * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to + * determine SMPS mode. + */ + if (mld_vif->ps_disabled == !enable) + return; + + mld_vif->ps_disabled = !enable; + + iwl_mld_update_device_power(mld, false); +} + static void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1295,11 +1312,7 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, } if (changes & BSS_CHANGED_PS) { - /* Send both device-level and MAC-level power commands since the - * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to - * determine SMPS mode. - */ - iwl_mld_update_device_power(mld, false); + iwl_mld_smps_wa(mld, vif, vif->cfg.ps); iwl_mld_update_mac_power(mld, vif, false); } @@ -1716,6 +1729,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, FW_CTXT_ACTION_MODIFY); if (ret) return ret; + iwl_mld_smps_wa(mld, vif, vif->cfg.ps); } /* MFP is set by default before the station is authorized. @@ -1758,6 +1772,7 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, &mld_vif->emlsr.check_tpt_wk); iwl_mld_reset_cca_40mhz_workaround(mld, vif); + iwl_mld_smps_wa(mld, vif, true); } /* once we move into assoc state, need to update the FW to diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index ed7c0319f239..2f16c174b57e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -15,11 +15,12 @@ static void iwl_mld_vif_ps_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { bool *ps_enable = (bool *)data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION) return; - *ps_enable &= vif->cfg.ps; + *ps_enable &= !mld_vif->ps_disabled; } int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3) From 9f804a0af7ad0895d876162d0cb1933c53ca7123 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 297/465] wifi: iwlwifi: mld: track channel_load_not_by_us JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e8670620b07725d38f7c8a6f751a1173deeafb8a Author: Miri Korenblit Date: Sun Mar 9 07:36:41 2025 +0200 wifi: iwlwifi: mld: track channel_load_not_by_us For each channel context, track the avarage channel load by others in the driver specific phy data, to be used by EMLSR. Due to FW limitations, this value is incorrect in EMLSR, so it is shouldn't be used in EMLSR. On EMLSR exit, clear it so the wrong value won't be used. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.dd443fc5b178.I68b2fed197aae14888159b7a73bf40c2f346f41f@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 21 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/phy.h | 5 +++++ .../net/wireless/intel/iwlwifi/mld/stats.c | 10 ++++++++- .../wireless/intel/iwlwifi/mld/tests/utils.c | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index ba149581e25d..b01a41f9b013 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -847,6 +847,7 @@ int iwl_mld_add_chanctx(struct ieee80211_hw *hw, if (fw_id < 0) return fw_id; + phy->mld = mld; phy->fw_id = fw_id; phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 99c8501129b8..b68092be9aed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -3,6 +3,7 @@ * Copyright (C) 2024-2025 Intel Corporation */ #include "mlo.h" +#include "phy.h" /* Block reasons helper */ #define HANDLE_EMLSR_BLOCKED_REASONS(HOW) \ @@ -177,6 +178,19 @@ static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld, &mld_vif->emlsr.prevent_done_wk, delay); } +static void iwl_mld_clear_avg_chan_load_iter(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + void *dat) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + + /* It is ok to do it for all chanctx (and not only for the ones that + * belong to the EMLSR vif) since EMLSR is not allowed if there is + * another vif. + */ + phy->avg_channel_load_not_by_us = 0; +} + static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, enum iwl_mld_emlsr_exit exit, u8 link_to_keep, bool sync) @@ -215,6 +229,13 @@ static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, /* Update latest exit reason and check EMLSR prevention */ iwl_mld_check_emlsr_prevention(mld, mld_vif, exit); + /* channel_load_not_by_us is invalid when in EMLSR. + * Clear it so wrong values won't be used. + */ + ieee80211_iter_chan_contexts_atomic(mld->hw, + iwl_mld_clear_avg_chan_load_iter, + NULL); + return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h index 3dfb8ca994e2..357bc9fe9624 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -15,6 +15,9 @@ * with. Used to detect a no-op when the chanctx changes. * @channel_load_by_us: channel load on this channel caused by * the NIC itself, as indicated by firmware + * @avg_channel_load_not_by_us: averaged channel load on this channel caused by + * others. This value is invalid when in EMLSR (due to FW limitations) + * @mld: pointer to the MLD context */ struct iwl_mld_phy { /* Add here fields that need clean up on hw restart */ @@ -24,6 +27,8 @@ struct iwl_mld_phy { ); /* And here fields that survive a hw restart */ u32 channel_load_by_us; + u32 avg_channel_load_not_by_us; + struct iwl_mld *mld; }; static inline struct iwl_mld_phy * diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 842b9b9fdd8c..5633885c49ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -461,6 +461,7 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, { struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); const struct iwl_stats_ntfy_per_phy *per_phy = data; + u32 new_load; if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL)) return; @@ -468,7 +469,14 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, phy->channel_load_by_us = le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us); - /* TODO: channel load not by us (task=statistics) */ + new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); + if (IWL_FW_CHECK(phy->mld, new_load > 100, "Invalid channel load %u\n", + new_load)) + return; + + /* give a weight of 0.5 for the old value */ + phy->avg_channel_load_not_by_us = + (new_load >> 1) + (phy->avg_channel_load_not_by_us >> 1); } static void diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index a8c1e6c72138..b6049918b5db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -168,6 +168,7 @@ iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def) KUNIT_ASSERT_GE(test, fw_id, 0); phy->fw_id = fw_id; + phy->mld = mld; phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); return ctx; From 9a42219ed4d620a1f52142a8f03dd15a40e78ec3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 298/465] wifi: iwlwifi: mld: refactor iwl_mld_valid_emlsr_pair JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 380038bb839de262f5ad84b41b16f3f69afd5a91 Author: Miri Korenblit Date: Sun Mar 9 07:36:42 2025 +0200 wifi: iwlwifi: mld: refactor iwl_mld_valid_emlsr_pair - Change reasons enum to a bitmask and rename it - Don't use 'else if' so all reasons will be set in the reasons bitmask Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.0a3b2f88fbbf.I0152bc39e828488451e85135feb044ce1f7a85d3@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index b68092be9aed..bbaf9ad0e9eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -748,23 +748,23 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); struct iwl_mld *mld = mld_vif->mld; - enum iwl_mld_emlsr_exit ret = 0; + u32 reason_mask = 0; /* Per-link considerations */ if (iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true) || iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false)) return false; - if (a->chandef->chan->band == b->chandef->chan->band) { - ret |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; - } else if (a->chandef->width != b->chandef->width) { + if (a->chandef->chan->band == b->chandef->chan->band) + reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + if (a->chandef->width != b->chandef->width) { /* TODO: task=EMLSR task=statistics * replace BANDWIDTH exit reason with channel load criteria */ - ret |= IWL_MLD_EMLSR_EXIT_BANDWIDTH; + reason_mask |= IWL_MLD_EMLSR_EXIT_BANDWIDTH; } - if (ret) { + if (reason_mask) { IWL_DEBUG_INFO(mld, "Links %d and %d are not a valid pair for EMLSR\n", a->link_id, b->link_id); @@ -772,7 +772,7 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, "Links bandwidth are: %d and %d\n", nl80211_chan_width_to_mhz(a->chandef->width), nl80211_chan_width_to_mhz(b->chandef->width)); - iwl_mld_print_emlsr_exit(mld, ret); + iwl_mld_print_emlsr_exit(mld, reason_mask); return false; } From 202fd0a60e9ddb3881a2a31ac6af982ba5dbc82f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 299/465] wifi: iwlwifi: mld: assume wiphy is locked when getting BSS ifaces JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8d006c92132ed8bafc95feb314bcd3c5dd2e6bce Author: Benjamin Berg Date: Sun Mar 9 07:36:43 2025 +0200 wifi: iwlwifi: mld: assume wiphy is locked when getting BSS ifaces The code was calling ieee80211_iterate_interfaces, however that takes a lock of iflist_mtx, which must not be taken recursively. Fix this by using the appropriate _mtx version that asserts that the wiphy mutex is already held. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.6ce298d6a44f.Ibc862dfdd6cb2da63781c791b9dc601bd5ce4bdc@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/iface.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 691062b44461..e49e2260ac05 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -465,10 +465,10 @@ u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld) { u8 fw_id_bitmap = 0; - ieee80211_iterate_interfaces(mld->hw, - IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, - iwl_mld_get_fw_id_bss_bitmap_iter, - &fw_id_bitmap); + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, + iwl_mld_get_fw_id_bss_bitmap_iter, + &fw_id_bitmap); return fw_id_bitmap; } From 54c075de68218dbfa0c978534a9793ce545ded4d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 300/465] wifi: iwlwifi: mld: initialize regulatory early JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6895d74c11d80c8c44cd14eade856c26a30902ad Author: Johannes Berg Date: Sun Mar 9 07:36:44 2025 +0200 wifi: iwlwifi: mld: initialize regulatory early Since iwlmld claims wiphys to be self-managed, it needs to have a regdomain registered before the wiphy is registered to avoid issues when trying to get the regdomain, e.g. via "iw phy phy0 reg get". Move the initialization early, on every FW start not just when starting to really operate it. This also requires the self-managed flag to be set early. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.10ab8fed94e9.I7c8dee3d14c7427a56882739f82546c6492f3b10@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/fw.c | 8 ++++---- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 9 --------- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index ec93af362cfc..9c1dce0d5979 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -339,6 +339,10 @@ int iwl_mld_load_fw(struct iwl_mld *mld) if (ret) return ret; + ret = iwl_mld_init_mcc(mld); + if (ret) + return ret; + mld->fw_status.running = true; return 0; @@ -481,10 +485,6 @@ static int iwl_mld_config_fw(struct iwl_mld *mld) if (ret) return ret; - ret = iwl_mld_init_mcc(mld); - if (ret) - return ret; - if (mld->fw_status.in_hw_restart) { iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB); iwl_mld_time_sync_fw_config(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index b01a41f9b013..27ef41d69479 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -164,14 +164,6 @@ static void iwl_mld_hw_set_security(struct iwl_mld *mld) NL80211_EXT_FEATURE_BEACON_PROTECTION); } -static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) -{ - struct wiphy *wiphy = mld->wiphy; - - wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; - wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; -} - static void iwl_mld_hw_set_antennas(struct iwl_mld *mld) { struct wiphy *wiphy = mld->wiphy; @@ -415,7 +407,6 @@ int iwl_mld_register_hw(struct iwl_mld *mld) iwl_mld_hw_set_addresses(mld); iwl_mld_hw_set_channels(mld); iwl_mld_hw_set_security(mld); - iwl_mld_hw_set_regulatory(mld); iwl_mld_hw_set_pm(mld); iwl_mld_hw_set_antennas(mld); iwl_mac_hw_set_radiotap(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index d8499a05e724..5e1fcd952da0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2024-2025 Intel Corporation */ - +#include #include #include "fw/api/rx.h" @@ -50,6 +50,14 @@ static void __exit iwl_mld_exit(void) } module_exit(iwl_mld_exit); +static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; +} + VISIBLE_IF_IWLWIFI_KUNIT void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, @@ -67,7 +75,6 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, /* Setup async RX handling */ spin_lock_init(&mld->async_handlers_lock); - INIT_LIST_HEAD(&mld->async_handlers_list); wiphy_work_init(&mld->async_handlers_wk, iwl_mld_async_handlers_wk); @@ -387,9 +394,13 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_bios_setup_step(trans, &mld->fwrt); mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); + iwl_mld_hw_set_regulatory(mld); + /* Configure transport layer with the opmode specific params */ iwl_mld_configure_trans(op_mode); + /* needed for regulatory init */ + rtnl_lock(); /* Needed for sending commands */ wiphy_lock(mld->wiphy); @@ -401,6 +412,7 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (ret) { wiphy_unlock(mld->wiphy); + rtnl_unlock(); iwl_fw_flush_dumps(&mld->fwrt); goto free_hw; } @@ -408,6 +420,7 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mld_stop_fw(mld); wiphy_unlock(mld->wiphy); + rtnl_unlock(); ret = iwl_mld_leds_init(mld); if (ret) From 474ecc2304a2065e67a3f1725e00f8526a50aa09 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 301/465] wifi: iwlwifi: mld: use the right iface iterator in low_latency JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 29b0ca82c1ee731691085ce8e8aac9e5bcd5d2b1 Author: Miri Korenblit Date: Sun Mar 9 07:36:45 2025 +0200 wifi: iwlwifi: mld: use the right iface iterator in low_latency We used ieee80211_iterate_active_interfaces instead of ieee80211_iterate_active_interfaces_mtx, which is the one to use when the wiphy lock is held. Signed-off-by: Miri Korenblit Reviewed-by: Daniel Gabay Reviewed-by: Benjamin Berg Link: https://patch.msgid.link/20250309073442.925cdca61ed0.I34f5c52d27414cb4c301bbd24df7c3530a43fa1d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/low_latency.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c index 439fc10a4a41..e74e66735f52 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -120,9 +120,9 @@ static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk) wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, MLD_LL_ACTIVE_WK_PERIOD); - ieee80211_iterate_active_interfaces(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_low_latency_iter, mld); + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_low_latency_iter, mld); } int iwl_mld_low_latency_init(struct iwl_mld *mld) From 7f8813565738b911545b41b23413c02e52a7447e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:54 +0200 Subject: [PATCH 302/465] wifi: iwlwifi: mld: fix OMI time protection logic JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b807dec3e82ae623e0ec45a26e485548d91bbe9c Author: Johannes Berg Date: Sun Mar 9 07:36:46 2025 +0200 wifi: iwlwifi: mld: fix OMI time protection logic We're allowed to enter OMI only 5 seconds after the last exit, so the logic needs to be inverted. Fix that. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.58efb4c91655.Id596fcda2fb28f5945548d780be9ff90aee76b7e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 64ebafc35c9b..f6d482817f1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -760,8 +760,8 @@ void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld) return; } - if (time_is_before_jiffies(mld_link->rx_omi.exit_ts + - msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) + if (time_is_after_jiffies(mld_link->rx_omi.exit_ts + + msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) return; /* reduce bandwidth to 80 MHz to save power */ From 92447b8aabf0334b752e8b6a907c936d9005a507 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 303/465] wifi: iwlwifi: mld: enable OMI bandwidth reduction on 6 GHz JIRA: https://issues.redhat.com/browse/RHEL-89168 commit fd04fbee7f0f8ec986772d41a1e1717f5bcf941c Author: Johannes Berg Date: Sun Mar 9 07:36:47 2025 +0200 wifi: iwlwifi: mld: enable OMI bandwidth reduction on 6 GHz Due to the iwl_mld_get_chandef_from_chanctx() logic, even after the OMI handshake to reduce bandwidth the driver wouldn't apply that to the PHY context, since it always uses the normal, not the reduced, configuration on 6 GHz (not strictly always, but OMI will only apply if the original bandwidth is > 80 MHz.) Fix this by making that selection contingent on AP mode. Refactor the code a bit to also make it clearer why the min_def isn't used in that case (for FILS.) Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.2706cbd0b100.Ic34636b1aee81a140eb690fca8139909a58f8e8b@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 2 +- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 4 +- drivers/net/wireless/intel/iwlwifi/mld/phy.c | 51 ++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mld/phy.h | 3 +- .../wireless/intel/iwlwifi/mld/tests/utils.c | 2 +- 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index f6d482817f1b..1db69aee4e9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -67,7 +67,7 @@ static void iwl_mld_fill_rates(struct iwl_mld *mld, __le32 *cck_rates, __le32 *ofdm_rates) { struct cfg80211_chan_def *chandef = - iwl_mld_get_chandef_from_chanctx(chan_ctx); + iwl_mld_get_chandef_from_chanctx(mld, chan_ctx); struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[chandef->chan->band]; unsigned long basic = link->basic_rates; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 27ef41d69479..91e201fde72a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -840,7 +840,7 @@ int iwl_mld_add_chanctx(struct ieee80211_hw *hw, phy->mld = mld; phy->fw_id = fw_id; - phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); + phy->chandef = *iwl_mld_get_chandef_from_chanctx(mld, ctx); ret = iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_ADD); if (ret) { @@ -872,7 +872,7 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw, struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); struct cfg80211_chan_def *chandef = - iwl_mld_get_chandef_from_chanctx(ctx); + iwl_mld_get_chandef_from_chanctx(mld, ctx); /* We don't care about these */ if (!(changed & ~(IEEE80211_CHANCTX_CHANGE_RX_CHAINS | diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index c38f101628de..2fbc8090088b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -22,16 +22,55 @@ int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld) } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id); -struct cfg80211_chan_def * -iwl_mld_get_chandef_from_chanctx(struct ieee80211_chanctx_conf *ctx) +struct iwl_mld_chanctx_usage_data { + struct iwl_mld *mld; + struct ieee80211_chanctx_conf *ctx; + bool use_def; +}; + +static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) { - bool use_def = cfg80211_channel_is_psc(ctx->def.chan) || + if (vif->type != NL80211_IFTYPE_AP) + return false; + + return cfg80211_channel_is_psc(ctx->def.chan) || (ctx->def.chan->band == NL80211_BAND_6GHZ && ctx->def.width >= NL80211_CHAN_WIDTH_80); - - return use_def ? &ctx->def : &ctx->min_def; } -EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_chandef_from_chanctx); + +static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_chanctx_usage_data *data = _data; + struct ieee80211_bss_conf *link_conf; + int link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) + continue; + + if (iwl_mld_chanctx_fils_enabled(vif, data->ctx)) + data->use_def = true; + } +} + +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld_chanctx_usage_data data = { + .mld = mld, + .ctx = ctx, + }; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_chanctx_usage_iter, + &data); + + return data.use_def ? &ctx->def : &ctx->min_def; +} static u8 iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h index 357bc9fe9624..2212a89321b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -48,7 +48,8 @@ int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld); int iwl_mld_phy_fw_action(struct iwl_mld *mld, struct ieee80211_chanctx_conf *ctx, u32 action); struct cfg80211_chan_def * -iwl_mld_get_chandef_from_chanctx(struct ieee80211_chanctx_conf *ctx); +iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx); u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef); #endif /* __iwl_mld_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index b6049918b5db..6331cd91cdf6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -169,7 +169,7 @@ iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def) phy->fw_id = fw_id; phy->mld = mld; - phy->chandef = *iwl_mld_get_chandef_from_chanctx(ctx); + phy->chandef = *def; return ctx; } From c69172903907c15c127cd6f30271ec725b778a5c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 304/465] wifi: iwlwifi: mld: move the ftm initiator data to ftm-initiator.h JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 32c33a4dd2a9f80ded554551a172fcb351970b1c Author: Avraham Stern Date: Sun Mar 9 07:36:48 2025 +0200 wifi: iwlwifi: mld: move the ftm initiator data to ftm-initiator.h Move the FTM initiator data to the relevant header file and document its fields. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.92830fd553ec.Icbbd0eba34c9ba318801074f7705f6d1e5af5482@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/ftm-initiator.h | 14 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/mld.h | 7 ++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h index e98fac34beba..3fab25a52508 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h @@ -5,6 +5,20 @@ #ifndef __iwl_mld_ftm_initiator_h__ #define __iwl_mld_ftm_initiator_h__ +/** + * struct ftm_initiator_data - FTM initiator data + * + * @req: a pointer to cfg80211 FTM request + * @req_wdev: a pointer to the wdev that requested the current FTM request + * @responses: the number of responses received for the current FTM session. + * Used for tracking the burst index in a periodic request. + */ +struct ftm_initiator_data { + struct cfg80211_pmsr_request *req; + struct wireless_dev *req_wdev; + int responses[IWL_TOF_MAX_APS]; +}; + int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, struct cfg80211_pmsr_request *req); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 1ab98ba42c76..38f1d1bc5a24 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -34,6 +34,7 @@ #include "constants.h" #include "ptp.h" #include "time_sync.h" +#include "ftm-initiator.h" /** * DOC: Introduction @@ -277,11 +278,7 @@ struct iwl_mld { struct iwl_mld_time_sync_data __rcu *time_sync; - struct { - struct cfg80211_pmsr_request *req; - struct wireless_dev *req_wdev; - int responses[IWL_TOF_MAX_APS]; - } ftm_initiator; + struct ftm_initiator_data ftm_initiator; }; /* memset the part of the struct that requires cleanup on restart */ From 1e66e4a233f6ff2590f042ee0135ddba57a3d49c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 305/465] wifi: iwlwifi: mld: remove AP keys only for AP STA JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 67128af05c6419e1f56ea7f10a29fec8b8ec4add Author: Johannes Berg Date: Sun Mar 9 07:36:49 2025 +0200 wifi: iwlwifi: mld: remove AP keys only for AP STA On station interfaces we don't only have the AP STA, but also TDLS stations. Don't try to remove AP keys for them. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Tested-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.f06a4d6eed2b.Icd20af668a22bfae5328eb0ea00ce10a72ce3539@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index f266a81dd29b..994d4561518b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -793,7 +793,7 @@ void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta) * removed, but FW expects all the keys to be removed before * the STA is, so remove them all here. */ - if (vif->type == NL80211_IFTYPE_STATION) + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) iwl_mld_remove_ap_keys(mld, vif, sta, link_id); /* Remove the link_sta */ From 8b29ff793eded8e485eab96850a9470b52a47c7b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 306/465] wifi: iwlwifi: mld: Correctly configure the A-MSDU max lengths JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 36b79cb091ad89f939d50a4f3af06534ae0fbdaa Author: Ilan Peer Date: Sun Mar 9 07:36:50 2025 +0200 wifi: iwlwifi: mld: Correctly configure the A-MSDU max lengths Refactor the setting of the A-MSDU maximal lengths as follows: - Move the setting of the maximal A-MSDU length in case of HT from TLC logic to the station logic as it is not related to TLC. - As long as the station is not associated, set RC A-MSDU maximal lengths to 1, to prevent iwlmld and mac80211 from building A-MSDUs. - Update the RC and the TID specific A-MSDU maximal lengths based on the FW TLC notifications. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250309073442.afc842633002.I68153b6b0c5d976f2c7525009631f8fa28e9987c@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 24 +++++++++++ drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 43 ++++++-------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 994d4561518b..332a7aecec2d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -606,6 +606,25 @@ iwl_mld_remove_link_sta(struct iwl_mld *mld, kfree_rcu(mld_link_sta, rcu_head); } +static void iwl_mld_set_max_amsdu_len(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + + /* For EHT, HE and VHT we can use the value as it was calculated by + * mac80211. For HT, mac80211 doesn't enforce to 4095, so force it + * here + */ + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he || + link_sta->vht_cap.vht_supported || + !ht_cap->ht_supported || + !(ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU)) + return; + + link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; + ieee80211_sta_recalc_aggregates(link_sta->sta); +} + int iwl_mld_update_all_link_stations(struct iwl_mld *mld, struct ieee80211_sta *sta) { @@ -618,6 +637,9 @@ int iwl_mld_update_all_link_stations(struct iwl_mld *mld, if (ret) return ret; + + if (mld_sta->sta_state == IEEE80211_STA_ASSOC) + iwl_mld_set_max_amsdu_len(mld, link_sta); } return 0; } @@ -1222,6 +1244,8 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld, link = link_conf_dereference_protected(mld_sta->vif, link_sta->link_id); + + iwl_mld_set_max_amsdu_len(mld, link_sta); iwl_mld_config_tlc_link(mld, vif, link, link_sta); sta_mask_added |= BIT(mld_link_sta->fw_id); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index 85ec358f1417..f054cc921d9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include @@ -467,7 +467,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, own_he_cap, own_eht_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_rc_amsdu_len), + .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len), }; int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); int ret; @@ -493,30 +493,6 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret); } -static void iwl_mld_recalc_amsdu_len(struct iwl_mld *mld, - struct ieee80211_link_sta *link_sta) -{ - const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; - - /* For EHT, HE and VHT - we can use the value as it was calculated by - * mac80211. - */ - if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he || - link_sta->vht_cap.vht_supported) - goto recalc; - - /* But for HT, mac80211 doesn't enforce to 4095, so force it here */ - if (ht_cap->ht_supported && ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU) - /* Agg is offloaded, so we need to assume that agg are enabled - * and max mpdu in ampdu is 4095 (spec 802.11-2016 9.3.2.1) - */ - link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; - -recalc: - link_sta->agg.max_rc_amsdu_len = link_sta->agg.max_amsdu_len; - ieee80211_sta_recalc_aggregates(link_sta->sta); -} - int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data) { struct { @@ -547,15 +523,22 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); enum nl80211_band band; if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) return; + /* Before we have information about a station, configure the A-MSDU RC + * limit such that iwlmd and mac80211 would not be allowed to build + * A-MSDUs. + */ + if (mld_sta->sta_state < IEEE80211_STA_ASSOC) { + link_sta->agg.max_rc_amsdu_len = 1; + ieee80211_sta_recalc_aggregates(link_sta->sta); + } + band = link_conf->chanreq.oper.chan->band; - - iwl_mld_recalc_amsdu_len(mld, link_sta); - iwl_mld_send_tlc_cmd(mld, vif, link_sta, band); } @@ -706,7 +689,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, link_sta->agg.max_tid_amsdu_len[i] = iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i); else - link_sta->agg.max_tid_amsdu_len[i] = 0; + link_sta->agg.max_tid_amsdu_len[i] = 1; } ieee80211_sta_recalc_aggregates(link_sta->sta); From b61427507a47bd9a0ed45e0758036b9438bfdabe Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 307/465] wifi: iwlwifi: mld: always do MLO scan before link selection JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f31d666f0b710818603a8b6a03cff23ea4a2ad0e Author: Miri Korenblit Date: Sun Mar 9 07:36:51 2025 +0200 wifi: iwlwifi: mld: always do MLO scan before link selection According to the requirements, if the last scan isn't older than 20 seconds, we can use its results and do the link selection without scanning before. But this applies only when trying to get back to EMLSR, not if the link has bad RSSI/missed beacons. Since an MLO scan is cheap anyway, and results from 20 seconds before are really old, always scan before links switching. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.a4c96e5c49d4.Ie55697af49435c2c45dccf7c607de5857b370f7a@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 2 +- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 20 ++----------------- drivers/net/wireless/intel/iwlwifi/mld/mlo.h | 3 --- .../net/wireless/intel/iwlwifi/mld/stats.c | 2 +- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 1db69aee4e9f..6db8b5305349 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -913,7 +913,7 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); /* try to switch links, no-op if we don't have MLO */ - iwl_mld_trigger_link_selection(mld, vif); + iwl_mld_int_mlo_scan(mld, vif); } /* no more logic if we're not in EMLSR */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index bbaf9ad0e9eb..99ba4018fb24 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -289,22 +289,6 @@ int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, static void _iwl_mld_select_links(struct iwl_mld *mld, struct ieee80211_vif *vif); -void iwl_mld_trigger_link_selection(struct iwl_mld *mld, - struct ieee80211_vif *vif) -{ - bool last_scan_was_recent = - time_before(jiffies, mld->scan.last_mlo_scan_jiffies + - IWL_MLD_SCAN_EXPIRE_TIME); - - if (last_scan_was_recent) { - IWL_DEBUG_EHT(mld, "MLO scan was recent, skip.\n"); - _iwl_mld_select_links(mld, vif); - } else { - IWL_DEBUG_EHT(mld, "Doing link selection after MLO scan\n"); - iwl_mld_int_mlo_scan(mld, vif); - } -} - void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, enum iwl_mld_emlsr_blocked reason) { @@ -334,7 +318,7 @@ void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, return; IWL_DEBUG_INFO(mld, "EMLSR is unblocked\n"); - iwl_mld_trigger_link_selection(mld, vif); + iwl_mld_int_mlo_scan(mld, vif); } static void @@ -1011,5 +995,5 @@ void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) mld_vif->emlsr.blocked_reasons) return; - iwl_mld_trigger_link_selection(mld, vif); + iwl_mld_int_mlo_scan(mld, vif); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index fd1abe8e6084..0f1b18f61c75 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -141,9 +141,6 @@ void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld); -void iwl_mld_trigger_link_selection(struct iwl_mld *mld, - struct ieee80211_vif *vif); - /** * iwl_mld_retry_emlsr - Retry entering EMLSR * @mld: MLD context diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 5633885c49ff..a9d3860d8f9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -384,7 +384,7 @@ static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, /* Handle inactive EMLSR, check whether to switch links */ if (!iwl_mld_emlsr_active(vif)) { if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) - iwl_mld_trigger_link_selection(mld, vif); + iwl_mld_int_mlo_scan(mld, vif); return; } From 46bb2b73c96503ddd4f30d4daf6d9fa0057aa3db Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 308/465] wifi: iwlwifi: mld: fix bad RSSI handling JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d414ff7a733819f78a49b832fad6d4e7cb1f9150 Author: Miri Korenblit Date: Sun Mar 9 07:36:52 2025 +0200 wifi: iwlwifi: mld: fix bad RSSI handling If the RSSI is dropping to below the threshold, we need to do a MLO scan to try select a better link. This is true also if the connection doesn't have EMLSR capability, and also if we are in EMLSR. Fix the logic to always check the RSSI (and do a MLO scan if needed). Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.a31b95888244.If6dca30d657658fa902b19e07b6fbc86c48d69cb@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/stats.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index a9d3860d8f9c..75cb204c2419 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -378,15 +378,11 @@ static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, /* TODO: task=statistics handle CQM notifications */ - if (!iwl_mld_vif_has_emlsr_cap(vif)) - return; + if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) + iwl_mld_int_mlo_scan(mld, vif); - /* Handle inactive EMLSR, check whether to switch links */ - if (!iwl_mld_emlsr_active(vif)) { - if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) - iwl_mld_int_mlo_scan(mld, vif); + if (!iwl_mld_emlsr_active(vif)) return; - } /* We are in EMLSR, check if we need to exit */ exit_emlsr_thresh = From 2bcf4c2bbef711fdbe646370a28196ca80bde6ce Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 309/465] wifi: iwlwifi: mld: avoid selecting bad links JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9324731b9985478faf7f77713cc5e5fee811716e Author: Miri Korenblit Date: Sun Mar 9 07:36:53 2025 +0200 wifi: iwlwifi: mld: avoid selecting bad links Currently, we don't select a link that wasn't heared in the last 5 seconds. But if the link started to suffer from missed beacons more recent than that, we might select this link even we really shouldn't, leading to a disconnection instead of a link switch. Fix this by checking if a link was heared in the last MLO scan, if not - don't include it in the link selection. Since we do an MLO scan on missed beacons, we will not hear that link in that scan, and won't select it. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250309073442.8f950497219e.I51306021fe9231a8184e89c23707be47d3c05241@changeid [replace cast with ULL constant] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 10 +++++----- drivers/net/wireless/intel/iwlwifi/mld/scan.c | 4 +++- drivers/net/wireless/intel/iwlwifi/mld/scan.h | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 99ba4018fb24..a5b1d373922a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -704,10 +704,9 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld, if (WARN_ON_ONCE(!link_conf)) continue; - /* Ignore any BSS that was not seen in the last 5 seconds */ + /* Ignore any BSS that was not seen in the last MLO scan */ if (ktime_before(link_conf->bss->ts_boottime, - ktime_sub_ns(ktime_get_boottime_ns(), - (u64)5 * NSEC_PER_SEC))) + mld->scan.last_mlo_scan_time)) continue; data[n_data].link_id = link_id; @@ -819,8 +818,9 @@ static void _iwl_mld_select_links(struct iwl_mld *mld, if (!mld_vif->authorized || hweight16(usable_links) <= 1) return; - if (WARN(time_before(mld->scan.last_mlo_scan_jiffies, - jiffies - IWL_MLD_SCAN_EXPIRE_TIME), + if (WARN(ktime_before(mld->scan.last_mlo_scan_time, + ktime_sub_ns(ktime_get_boottime_ns(), + 5ULL * NSEC_PER_SEC)), "Last MLO scan was too long ago, can't select links\n")) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index 5f4ede92028b..7ec04318ec2f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -1794,6 +1794,9 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, IWL_MLD_SCAN_INT_MLO); + if (!ret) + mld->scan.last_mlo_scan_time = ktime_get_boottime_ns(); + IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); } @@ -1922,7 +1925,6 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) { IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n"); - mld->scan.last_mlo_scan_jiffies = jiffies; /* * We limit link selection to internal MLO scans as otherwise diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h index cb589e002319..3ae940d55065 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -108,7 +108,8 @@ enum iwl_mld_traffic_load { * in jiffies. * @last_start_time_jiffies: stores the last start time in jiffies * (interface up/reset/resume). - * @last_mlo_scan_jiffies: end time of the last MLO scan in jiffies. + * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since + * boot. */ struct iwl_mld_scan { /* Add here fields that need clean up on restart */ @@ -129,7 +130,7 @@ struct iwl_mld_scan { void *cmd; unsigned long last_6ghz_passive_jiffies; unsigned long last_start_time_jiffies; - unsigned long last_mlo_scan_jiffies; + unsigned long last_mlo_scan_time; }; #endif /* __iwl_mld_scan_h__ */ From 708f77610fafb709ac92567f122b4c2ae0b8ba41 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 310/465] wifi: mwifiex: Add __nonstring annotations for unterminated strings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c04d96913c88fdc11400ca6dd7f436bb6ecc764c Author: Kees Cook Date: Mon Mar 10 15:23:32 2025 -0700 wifi: mwifiex: Add __nonstring annotations for unterminated strings When a character array without a terminating NUL character has a static initializer, GCC 15's -Wunterminated-string-initialization will only warn if the array lacks the "nonstring" attribute[1]. Mark the arrays with __nonstring to and correctly identify the char array as "not a C string" and thereby eliminate the warning. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1] Cc: Brian Norris Cc: Francesco Dolcini Cc: Johannes Berg Cc: Thomas Gleixner Cc: Greg Kroah-Hartman Cc: Allison Randal Cc: linux-wireless@vger.kernel.org Signed-off-by: Kees Cook Link: https://patch.msgid.link/20250310222332.work.202-kees@kernel.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/cfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index d39092b99212..d7fd79214bcf 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -150,7 +150,7 @@ static const u16 ac_mcs_rate_nss2[8][10] = { struct region_code_mapping { u8 code; - u8 region[IEEE80211_COUNTRY_STRING_LEN]; + u8 region[IEEE80211_COUNTRY_STRING_LEN] __nonstring; }; static struct region_code_mapping region_code_mapping_t[] = { From 70aec4158072f10e042bf9e34e9a300f5c5d19a1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:55 +0200 Subject: [PATCH 311/465] wifi: virt_wifi: Add __nonstring annotations for unterminated strings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit adb1ee4de04d496f3da409bb6791cfdd744ab2c3 Author: Kees Cook Date: Tue Mar 11 15:56:05 2025 -0700 wifi: virt_wifi: Add __nonstring annotations for unterminated strings When a character array without a terminating NUL character has a static initializer, GCC 15's -Wunterminated-string-initialization will only warn if the array lacks the "nonstring" attribute[1]. Mark the arrays with __nonstring to and correctly identify the char array as "not a C string" and thereby eliminate the warning. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1] Signed-off-by: Kees Cook Link: https://patch.msgid.link/20250311225604.it.926-kees@kernel.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/virtual/virt_wifi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c index 4ee374080466..fc122b79301a 100644 --- a/drivers/net/wireless/virtual/virt_wifi.c +++ b/drivers/net/wireless/virtual/virt_wifi.c @@ -146,7 +146,7 @@ static void virt_wifi_inform_bss(struct wiphy *wiphy) static const struct { u8 tag; u8 len; - u8 ssid[8]; + u8 ssid[8] __nonstring; } __packed ssid = { .tag = WLAN_EID_SSID, .len = VIRT_WIFI_SSID_LEN, From c55c9b53b815eeb11fd6ff7dd5184195de351744 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 312/465] wifi: zd1211rw: Add __nonstring annotations for unterminated strings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8300f2504afeceac234027d433a2f71d4a39c7fe Author: Kees Cook Date: Tue Mar 11 15:55:20 2025 -0700 wifi: zd1211rw: Add __nonstring annotations for unterminated strings When a character array without a terminating NUL character has a static initializer, GCC 15's -Wunterminated-string-initialization will only warn if the array lacks the "nonstring" attribute[1]. Mark the arrays with __nonstring to and correctly identify the char array as "not a C string" and thereby eliminate the warning. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1] Signed-off-by: Kees Cook Link: https://patch.msgid.link/20250311225513.it.620-kees@kernel.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index f90c33d19b39..9653dbaac3c0 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -21,7 +21,7 @@ struct zd_reg_alpha2_map { u32 reg; - char alpha2[2]; + char alpha2[2] __nonstring; }; static struct zd_reg_alpha2_map reg_alpha2_map[] = { From 9c969be3a8f2410b1f4718ed5b692d51622c9bbd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 313/465] wifi: mac80211: remove SSID from ML reconf JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 899da1830db112e6bd54ed4573ace753eae6ef22 Author: Johannes Berg Date: Tue Mar 11 12:10:03 2025 +0100 wifi: mac80211: remove SSID from ML reconf The ML reconfiguration frame shouldn't contain an SSID, remove it. Fixes: 36e05b0b8390 ("wifi: mac80211: Support dynamic link addition and removal") Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20250311121004.fdf08f90bc30.I07f88d3a6f592a0df65d48f55d65c46a4d261007@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 49a35a011453..81812369e4b6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10234,8 +10234,8 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, size += 2 + sizeof(struct ieee80211_mle_per_sta_profile) + ETH_ALEN; - /* SSID element + WMM */ - size += 2 + sdata->vif.cfg.ssid_len + 9; + /* WMM */ + size += 9; size += ieee80211_link_common_elems_size(sdata, iftype, cbss, elems_len); } @@ -10347,11 +10347,6 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, capab_pos = skb_put(skb, 2); - skb_put_u8(skb, WLAN_EID_SSID); - skb_put_u8(skb, sdata->vif.cfg.ssid_len); - skb_put_data(skb, sdata->vif.cfg.ssid, - sdata->vif.cfg.ssid_len); - extra_used = ieee80211_add_link_elems(sdata, skb, &capab, NULL, add_links_data->link[link_id].elems, From 3e5f6bbda3e4d16a64fc26806909505a38c0c267 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 314/465] wifi: mac80211: use supported selectors from assoc in ML reconf JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 80834e7d857932553dc22b0addbec744060aaf9f Author: Johannes Berg Date: Tue Mar 11 12:10:04 2025 +0100 wifi: mac80211: use supported selectors from assoc in ML reconf For multi-link reconfiguration, we shouldn't have any BSS membership selectors that are different from the association. Track the association selectors and use them to check the new link(s) added during reconfiguration. Fixes: 36e05b0b8390 ("wifi: mac80211: Support dynamic link addition and removal") Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20250311121004.771de0c36a75.I72f87d048c8693919b99dd9d4eee39833f06d15f@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/mlme.c | 29 +++++++++++------------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 217e949bb756..fb05f3cd37ec 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -444,8 +444,6 @@ struct ieee80211_mgd_assoc_data { const u8 *supp_rates; u8 supp_rates_len; - unsigned long userspace_selectors[BITS_TO_LONGS(128)]; - unsigned long timeout; int tries; @@ -524,6 +522,8 @@ struct ieee80211_if_managed { struct ieee80211_mgd_auth_data *auth_data; struct ieee80211_mgd_assoc_data *assoc_data; + unsigned long userspace_selectors[BITS_TO_LONGS(128)]; + bool powersave; /* powersave requested for this iface */ bool broken_ap; /* AP is broken -- turn off powersave */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 81812369e4b6..311a31601637 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4164,6 +4164,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->epcs.enabled = false; ifmgd->epcs.dialog_token = 0; + + memset(ifmgd->userspace_selectors, 0, + sizeof(ifmgd->userspace_selectors)); } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) @@ -6137,7 +6140,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, err = ieee80211_prep_channel(sdata, link, link_id, cbss, true, &link->u.mgd.conn, - assoc_data->userspace_selectors); + sdata->u.mgd.userspace_selectors); if (err) { link_info(link, "prep_channel failed\n"); goto out_err; @@ -9401,7 +9404,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, false); } - ieee80211_parse_cfg_selectors(assoc_data->userspace_selectors, + memset(sdata->u.mgd.userspace_selectors, 0, + sizeof(sdata->u.mgd.userspace_selectors)); + ieee80211_parse_cfg_selectors(sdata->u.mgd.userspace_selectors, req->supported_selectors, req->supported_selectors_len); @@ -9652,7 +9657,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, err = ieee80211_prep_channel(sdata, NULL, i, assoc_data->link[i].bss, true, &assoc_data->link[i].conn, - assoc_data->userspace_selectors); + sdata->u.mgd.userspace_selectors); if (err) { req->links[i].error = err; goto err_clear; @@ -9669,7 +9674,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, req->ap_mld_addr, true, &assoc_data->link[assoc_link_id].conn, override, - assoc_data->userspace_selectors); + sdata->u.mgd.userspace_selectors); if (err) goto err_clear; @@ -9913,14 +9918,6 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); -static void ieee80211_ml_reconf_selectors(unsigned long *userspace_selectors) -{ - /* these selectors are mandatory for ML reconfiguration */ - set_bit(BSS_MEMBERSHIP_SELECTOR_SAE_H2E, userspace_selectors); - set_bit(BSS_MEMBERSHIP_SELECTOR_HE_PHY, userspace_selectors); - set_bit(BSS_MEMBERSHIP_SELECTOR_EHT_PHY, userspace_selectors); -} - void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { @@ -9934,7 +9931,6 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.reconf.removed_links; u16 link_mask, valid_links; unsigned int link_id; - unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; size_t orig_len = len; u8 i, group_key_data_len; u8 *pos; @@ -10042,7 +10038,6 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, } ieee80211_vif_set_links(sdata, valid_links, sdata->vif.dormant_links); - ieee80211_ml_reconf_selectors(userspace_selectors); link_mask = 0; for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct cfg80211_bss *cbss = add_links_data->link[link_id].bss; @@ -10088,7 +10083,7 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, link->u.mgd.conn = add_links_data->link[link_id].conn; if (ieee80211_prep_channel(sdata, link, link_id, cbss, true, &link->u.mgd.conn, - userspace_selectors)) { + sdata->u.mgd.userspace_selectors)) { link_info(link, "mlo: reconf: prep_channel failed\n"); goto disconnect; } @@ -10424,7 +10419,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, */ if (added_links) { bool uapsd_supported; - unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -10434,7 +10428,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, data->wmm = true; uapsd_supported = true; - ieee80211_ml_reconf_selectors(userspace_selectors); for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct ieee80211_supported_band *sband; @@ -10510,7 +10503,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, data->link[link_id].bss, true, &data->link[link_id].conn, - userspace_selectors); + sdata->u.mgd.userspace_selectors); if (err) goto err_free; } From d3d65adc7cc7f924592bea896bda6aa0d21fefd6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 315/465] wifi: cfg80211: expose cfg80211_chandef_get_width() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b5c1622762f0937a66b69b7f15466c28fe85dcf1 Author: Johannes Berg Date: Tue Mar 11 12:25:34 2025 +0100 wifi: cfg80211: expose cfg80211_chandef_get_width() This can be just a trivial inline, to simplify some code. Expose it, and also use it in util.c where it wasn't previously available. Reviewed-by: Miriam Rachel Korenblit Link: https://patch.msgid.link/20250311122534.c5c3b4af9a74.Ib25cf60f634dc359961182113214e5cdc3504e9c@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/net/cfg80211.h | 11 +++++++++++ net/wireless/chan.c | 5 ----- net/wireless/util.c | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 011ca3b06807..bc83649b700b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1008,6 +1008,17 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, */ int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width); +/** + * cfg80211_chandef_get_width - return chandef width in MHz + * @c: chandef to return bandwidth for + * Return: channel width in MHz for the given chandef; note that it returns + * 80 for 80+80 configurations + */ +static inline int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) +{ + return nl80211_chan_width_to_mhz(c->width); +} + /** * cfg80211_chandef_valid - check if a channel definition is valid * @chandef: the channel definition to check diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 4cdb74a3f38c..193734b7f9dc 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -55,11 +55,6 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, } EXPORT_SYMBOL(cfg80211_chandef_create); -static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) -{ - return nl80211_chan_width_to_mhz(c->width); -} - static u32 cfg80211_get_start_freq(const struct cfg80211_chan_def *chandef, u32 cf) { diff --git a/net/wireless/util.c b/net/wireless/util.c index f5d0d8ef6b51..5a4329ef063d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -5,7 +5,7 @@ * Copyright 2007-2009 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2023, 2025 Intel Corporation */ #include #include @@ -2908,7 +2908,7 @@ bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio, u32 freq, width; freq = ieee80211_chandef_to_khz(chandef); - width = nl80211_chan_width_to_mhz(chandef->width); + width = cfg80211_chandef_get_width(chandef); if (!ieee80211_radio_freq_range_valid(radio, freq, width)) return false; From d5ef4685507a98929f11561b2eb58f4636a9c686 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 316/465] wifi: mac80211: use cfg80211_chandef_get_width() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 34670beb481e5e1a40448fabd312d17a1fbf0a73 Author: Johannes Berg Date: Tue Mar 11 12:25:35 2025 +0100 wifi: mac80211: use cfg80211_chandef_get_width() We can now use this helper here and simplify some code. Reviewed-by: Miriam Rachel Korenblit Link: https://patch.msgid.link/20250311122534.0a1d24a1a763.I51a52a67587a7eee65c80b9c5cf132820ebb9dd9@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/mlme.c | 2 +- net/mac80211/wbrf.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 311a31601637..6d564d7e2aa6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -788,7 +788,7 @@ static int ieee80211_chandef_num_subchans(const struct cfg80211_chan_def *c) if (c->width == NL80211_CHAN_WIDTH_80P80) return 4 + 4; - return nl80211_chan_width_to_mhz(c->width) / 20; + return cfg80211_chandef_get_width(c) / 20; } static int ieee80211_chandef_num_widths(const struct cfg80211_chan_def *c) diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c index 3a8612309137..478b34b81919 100644 --- a/net/mac80211/wbrf.c +++ b/net/mac80211/wbrf.c @@ -2,6 +2,7 @@ /* * Wifi Band Exclusion Interface for WLAN * Copyright (C) 2023 Advanced Micro Devices + * Copyright (C) 2025 Intel Corporation * */ @@ -45,7 +46,7 @@ static void get_ranges_from_chandef(struct cfg80211_chan_def *chandef, u64 start_freq2, end_freq2; int bandwidth; - bandwidth = nl80211_chan_width_to_mhz(chandef->width); + bandwidth = cfg80211_chandef_get_width(chandef); get_chan_freq_boundary(chandef->center_freq1, bandwidth, &start_freq1, &end_freq1); From 355c53486b12234a77f035dd832705356480626d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 317/465] wifi: iwlwifi: Fix uninitialized variable with __free() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 54be64fdf3ba6dbad2f5c48e466e1db43ad74bca Author: Dan Carpenter Date: Wed Mar 12 11:31:40 2025 +0300 wifi: iwlwifi: Fix uninitialized variable with __free() Pointers declared with the __free(kfree) attribute need to be initialized because they will be passed to kfree() on every return path. There are two return statement before the "cmd" pointer is initialized so this leads to an uninitialized variable bug. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Dan Carpenter Acked-by: Miri Korenblit Link: https://patch.msgid.link/f7c17a7f-f173-43bf-bc39-316b8adde349@stanley.mountain Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index c759c5c68dc0..1d4b2ad5d388 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -556,8 +556,8 @@ iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count, }; struct ieee80211_vif *vif = data; struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_dhc_cmd *cmd __free(kfree) = NULL; struct iwl_dhc_twt_operation *dhc_twt_cmd; - struct iwl_dhc_cmd *cmd __free(kfree); u64 target_wake_time; u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration; u8 trigger, flow_type, flow_id, protection, tenth_param; From 0dca6b1bed187bccf87f8b5fc33734bcc5a0dcbb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 318/465] wifi: rtw89: add support for negative values of dBm to linear conversion JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3df4583ae0cf3c93e4e3d6990566e1f7fe669bdf Author: Kuan-Chung Chen Date: Thu Mar 6 10:11:40 2025 +0800 wifi: rtw89: add support for negative values of dBm to linear conversion Enhanced the dBm to linear conversion function to accommodate negative dBm values and improved the precision from 1 dBm to 0.25 dBm. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250306021144.12854-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/realtek/rtw89/rtw8852b_common.c | 6 +- drivers/net/wireless/realtek/rtw89/util.c | 223 +++++++++++------- drivers/net/wireless/realtek/rtw89/util.h | 8 +- 3 files changed, 141 insertions(+), 96 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c index 0e094ce9c9b0..99c9505b3cbd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c @@ -621,9 +621,9 @@ static void rtw8852bt_ext_loss_avg_update(struct rtw89_dev *rtwdev, if (ext_loss_a == ext_loss_b) { ext_loss_avg = ext_loss_a; } else { - linear = rtw89_db_2_linear(abs(ext_loss_a - ext_loss_b)) + 1; - linear = DIV_ROUND_CLOSEST_ULL(linear / 2, 1 << RTW89_LINEAR_FRAC_BITS); - ext_loss_avg = rtw89_linear_2_db(linear); + linear = rtw89_db_to_linear(abs(ext_loss_a - ext_loss_b)) + 1; + linear /= 2; + ext_loss_avg = rtw89_linear_to_db(linear); ext_loss_avg += min(ext_loss_a, ext_loss_b); } diff --git a/drivers/net/wireless/realtek/rtw89/util.c b/drivers/net/wireless/realtek/rtw89/util.c index 33be26873473..073714db26f2 100644 --- a/drivers/net/wireless/realtek/rtw89/util.c +++ b/drivers/net/wireless/realtek/rtw89/util.c @@ -4,106 +4,151 @@ #include "util.h" -#define FRAC_ROWS 3 -#define FRAC_ROW_MAX (FRAC_ROWS - 1) -#define NORM_ROW_MIN FRAC_ROWS +#define RTW89_DBM_QUARTER_FACTOR 2 +#define RTW89_MIN_DBM (-41.25 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_MAX_DBM (96 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_DB_INVERT_TABLE_OFFSET (-RTW89_MIN_DBM) -static const u32 db_invert_table[12][8] = { - /* rows 0~2 in unit of U(32,3) */ - {10, 13, 16, 20, 25, 32, 40, 50}, - {64, 80, 101, 128, 160, 201, 256, 318}, - {401, 505, 635, 800, 1007, 1268, 1596, 2010}, - /* rows 3~11 in unit of U(32,0) */ - {316, 398, 501, 631, 794, 1000, 1259, 1585}, - {1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000}, - {12589, 15849, 19953, 25119, 31623, 39811, 50119, 63098}, - {79433, 100000, 125893, 158489, 199526, 251189, 316228, 398107}, - {501187, 630957, 794328, 1000000, 1258925, 1584893, 1995262, 2511886}, - {3162278, 3981072, 5011872, 6309573, 7943282, 1000000, 12589254, - 15848932}, - {19952623, 25118864, 31622777, 39810717, 50118723, 63095734, 79432823, - 100000000}, - {125892541, 158489319, 199526232, 251188643, 316227766, 398107171, - 501187234, 630957345}, - {794328235, 1000000000, 1258925412, 1584893192, 1995262315, 2511886432U, - 3162277660U, 3981071706U}, +static const u64 db_invert_table[] = { + /* in unit of 0.000001 */ + 75, 79, 84, 89, 94, 100, 106, 112, 119, 126, 133, 141, 150, 158, 168, 178, 188, + 200, 211, 224, 237, 251, 266, 282, 299, 316, 335, 355, 376, 398, 422, 447, 473, + 501, 531, 562, 596, 631, 668, 708, 750, 794, 841, 891, 944, 1000, 1059, 1122, 1189, + 1259, 1334, 1413, 1496, 1585, 1679, 1778, 1884, 1995, 2113, 2239, 2371, 2512, 2661, + 2818, 2985, 3162, 3350, 3548, 3758, 3981, 4217, 4467, 4732, 5012, 5309, 5623, 5957, + 6310, 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, 10593, 11220, 11885, 12589, + 13335, 14125, 14962, 15849, 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, + 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, 42170, 44668, 47315, 50119, + 53088, 56234, 59566, 63096, 66834, 70795, 74989, 79433, 84140, 89125, 94406, 100000, + 105925, 112202, 118850, 125893, 133352, 141254, 149624, 158489, 167880, 177828, + 188365, 199526, 211349, 223872, 237137, 251189, 266073, 281838, 298538, 316228, + 334965, 354813, 375837, 398107, 421697, 446684, 473151, 501187, 530884, 562341, + 595662, 630957, 668344, 707946, 749894, 794328, 841395, 891251, 944061, 1000000, + 1059254, 1122018, 1188502, 1258925, 1333521, 1412538, 1496236, 1584893, 1678804, + 1778279, 1883649, 1995262, 2113489, 2238721, 2371374, 2511886, 2660725, 2818383, + 2985383, 3162278, 3349654, 3548134, 3758374, 3981072, 4216965, 4466836, 4731513, + 5011872, 5308844, 5623413, 5956621, 6309573, 6683439, 7079458, 7498942, 7943282, + 8413951, 8912509, 9440609, 10000000, 10592537, 11220185, 11885022, 12589254, + 13335214, 14125375, 14962357, 15848932, 16788040, 17782794, 18836491, 19952623, + 21134890, 22387211, 23713737, 25118864, 26607251, 28183829, 29853826, 31622777, + 33496544, 35481339, 37583740, 39810717, 42169650, 44668359, 47315126, 50118723, + 53088444, 56234133, 59566214, 63095734, 66834392, 70794578, 74989421, 79432823, + 84139514, 89125094, 94406088, 100000000, 105925373, 112201845, 118850223, 125892541, + 133352143, 141253754, 149623566, 158489319, 167880402, 177827941, 188364909, 199526231, + 211348904, 223872114, 237137371, 251188643, 266072506, 281838293, 298538262, 316227766, + 334965439, 354813389, 375837404, 398107171, 421696503, 446683592, 473151259, 501187234, + 530884444, 562341325, 595662144, 630957344, 668343918, 707945784, 749894209, 794328235, + 841395142, 891250938, 944060876, 1000000000, 1059253725, 1122018454, 1188502227, + 1258925412, 1333521432, 1412537545, 1496235656, 1584893192, 1678804018, 1778279410, + 1883649089, 1995262315, 2113489040, 2238721139, 2371373706, 2511886432, 2660725060, + 2818382931, 2985382619, 3162277660, 3349654392, 3548133892, 3758374043, 3981071706, + 4216965034, 4466835922ULL, 4731512590ULL, 5011872336ULL, 5308844442ULL, 5623413252ULL, + 5956621435ULL, 6309573445ULL, 6683439176ULL, 7079457844ULL, 7498942093ULL, + 7943282347ULL, 8413951416ULL, 8912509381ULL, 9440608763ULL, 10000000000ULL, + 10592537252ULL, 11220184543ULL, 11885022274ULL, 12589254118ULL, 13335214322ULL, + 14125375446ULL, 14962356561ULL, 15848931925ULL, 16788040181ULL, 17782794100ULL, + 18836490895ULL, 19952623150ULL, 21134890398ULL, 22387211386ULL, 23713737057ULL, + 25118864315ULL, 26607250598ULL, 28183829313ULL, 29853826189ULL, 31622776602ULL, + 33496543916ULL, 35481338923ULL, 37583740429ULL, 39810717055ULL, 42169650343ULL, + 44668359215ULL, 47315125896ULL, 50118723363ULL, 53088444423ULL, 56234132519ULL, + 59566214353ULL, 63095734448ULL, 66834391757ULL, 70794578438ULL, 74989420933ULL, + 79432823472ULL, 84139514165ULL, 89125093813ULL, 94406087629ULL, 100000000000ULL, + 105925372518ULL, 112201845430ULL, 118850222744ULL, 125892541179ULL, 133352143216ULL, + 141253754462ULL, 149623565609ULL, 158489319246ULL, 167880401812ULL, 177827941004ULL, + 188364908949ULL, 199526231497ULL, 211348903984ULL, 223872113857ULL, 237137370566ULL, + 251188643151ULL, 266072505980ULL, 281838293126ULL, 298538261892ULL, 316227766017ULL, + 334965439158ULL, 354813389234ULL, 375837404288ULL, 398107170553ULL, 421696503429ULL, + 446683592151ULL, 473151258961ULL, 501187233627ULL, 530884444231ULL, 562341325190ULL, + 595662143529ULL, 630957344480ULL, 668343917569ULL, 707945784384ULL, 749894209332ULL, + 794328234724ULL, 841395141645ULL, 891250938134ULL, 944060876286ULL, 1000000000000ULL, + 1059253725177ULL, 1122018454302ULL, 1188502227437ULL, 1258925411794ULL, + 1333521432163ULL, 1412537544623ULL, 1496235656094ULL, 1584893192461ULL, + 1678804018123ULL, 1778279410039ULL, 1883649089490ULL, 1995262314969ULL, + 2113489039837ULL, 2238721138568ULL, 2371373705662ULL, 2511886431510ULL, + 2660725059799ULL, 2818382931264ULL, 2985382618918ULL, 3162277660168ULL, + 3349654391578ULL, 3548133892336ULL, 3758374042884ULL, 3981071705535ULL, + 4216965034286ULL, 4466835921510ULL, 4731512589615ULL, 5011872336273ULL, + 5308844442310ULL, 5623413251904ULL, 5956621435290ULL, 6309573444802ULL, + 6683439175686ULL, 7079457843841ULL, 7498942093325ULL, 7943282347243ULL, + 8413951416452ULL, 8912509381337ULL, 9440608762859ULL, 10000000000000ULL, + 10592537251773ULL, 11220184543020ULL, 11885022274370ULL, 12589254117942ULL, + 13335214321633ULL, 14125375446228ULL, 14962356560944ULL, 15848931924611ULL, + 16788040181226ULL, 17782794100389ULL, 18836490894898ULL, 19952623149689ULL, + 21134890398367ULL, 22387211385683ULL, 23713737056617ULL, 25118864315096ULL, + 26607250597988ULL, 28183829312645ULL, 29853826189180ULL, 31622776601684ULL, + 33496543915783ULL, 35481338923358ULL, 37583740428845ULL, 39810717055350ULL, + 42169650342858ULL, 44668359215096ULL, 47315125896148ULL, 50118723362727ULL, + 53088444423099ULL, 56234132519035ULL, 59566214352901ULL, 63095734448019ULL, + 66834391756862ULL, 70794578438414ULL, 74989420933246ULL, 79432823472428ULL, + 84139514164520ULL, 89125093813375ULL, 94406087628593ULL, 100000000000000ULL, + 105925372517729ULL, 112201845430197ULL, 118850222743702ULL, 125892541179417ULL, + 133352143216332ULL, 141253754462276ULL, 149623565609444ULL, 158489319246111ULL, + 167880401812256ULL, 177827941003893ULL, 188364908948981ULL, 199526231496888ULL, + 211348903983664ULL, 223872113856834ULL, 237137370566166ULL, 251188643150958ULL, + 266072505979882ULL, 281838293126446ULL, 298538261891796ULL, 316227766016838ULL, + 334965439157829ULL, 354813389233577ULL, 375837404288444ULL, 398107170553497ULL, + 421696503428583ULL, 446683592150964ULL, 473151258961482ULL, 501187233627272ULL, + 530884444230989ULL, 562341325190350ULL, 595662143529011ULL, 630957344480196ULL, + 668343917568615ULL, 707945784384138ULL, 749894209332456ULL, 794328234724284ULL, + 841395141645198ULL, 891250938133745ULL, 944060876285923ULL, 1000000000000000ULL, + 1059253725177290ULL, 1122018454301970ULL, 1188502227437020ULL, 1258925411794170ULL, + 1333521432163330ULL, 1412537544622760ULL, 1496235656094440ULL, 1584893192461110ULL, + 1678804018122560ULL, 1778279410038920ULL, 1883649089489810ULL, 1995262314968890ULL, + 2113489039836650ULL, 2238721138568340ULL, 2371373705661660ULL, 2511886431509590ULL, + 2660725059798820ULL, 2818382931264460ULL, 2985382618917960ULL, 3162277660168380ULL, + 3349654391578280ULL, 3548133892335770ULL, 3758374042884440ULL, 3981071705534970ULL }; -u32 rtw89_linear_2_db(u64 val) +s32 rtw89_linear_to_db_quarter(u64 val) { - u8 i, j; - u32 dB; + int r = ARRAY_SIZE(db_invert_table) - 1; + int l = 0; + int m; - for (i = 0; i < 12; i++) { - for (j = 0; j < 8; j++) { - if (i <= FRAC_ROW_MAX && - (val << RTW89_LINEAR_FRAC_BITS) <= db_invert_table[i][j]) - goto cnt; - else if (i > FRAC_ROW_MAX && val <= db_invert_table[i][j]) - goto cnt; - } + while (l <= r) { + m = l + (r - l) / 2; + + if (db_invert_table[m] == val) + return m - (s32)RTW89_DB_INVERT_TABLE_OFFSET; + + if (db_invert_table[m] > val) + r = m - 1; + else + l = m + 1; } - return 96; /* maximum 96 dB */ - -cnt: - /* special cases */ - if (j == 0 && i == 0) - goto end; - - if (i == NORM_ROW_MIN && j == 0) { - if (db_invert_table[NORM_ROW_MIN][0] - val > - val - (db_invert_table[FRAC_ROW_MAX][7] >> RTW89_LINEAR_FRAC_BITS)) { - i = FRAC_ROW_MAX; - j = 7; - } - goto end; - } - - if (i <= FRAC_ROW_MAX) - val <<= RTW89_LINEAR_FRAC_BITS; - - /* compare difference to get precise dB */ - if (j == 0) { - if (db_invert_table[i][j] - val > - val - db_invert_table[i - 1][7]) { - i--; - j = 7; - } - } else { - if (db_invert_table[i][j] - val > - val - db_invert_table[i][j - 1]) { - j--; - } - } -end: - dB = (i << 3) + j + 1; - - return dB; + if (l >= ARRAY_SIZE(db_invert_table)) + return RTW89_MAX_DBM; + else if (r < 0) + return RTW89_MIN_DBM; + else if (val - db_invert_table[r] <= db_invert_table[l] - val) + return r - (s32)RTW89_DB_INVERT_TABLE_OFFSET; + else + return l - (s32)RTW89_DB_INVERT_TABLE_OFFSET; } -EXPORT_SYMBOL(rtw89_linear_2_db); +EXPORT_SYMBOL(rtw89_linear_to_db_quarter); -u64 rtw89_db_2_linear(u32 db) +s32 rtw89_linear_to_db(u64 val) { - u64 linear; - u8 i, j; - - if (db > 96) - db = 96; - else if (db < 1) - return 1; - - i = (db - 1) >> 3; - j = (db - 1) & 0x7; - - linear = db_invert_table[i][j]; - - if (i >= NORM_ROW_MIN) - linear = linear << RTW89_LINEAR_FRAC_BITS; - - return linear; + return rtw89_linear_to_db_quarter(val) >> RTW89_DBM_QUARTER_FACTOR; } -EXPORT_SYMBOL(rtw89_db_2_linear); +EXPORT_SYMBOL(rtw89_linear_to_db); + +u64 rtw89_db_quarter_to_linear(s32 db) +{ + /* supported range -41.25 to 96 dBm, in unit of 0.25 dBm */ + db = clamp_t(s32, db, RTW89_MIN_DBM, RTW89_MAX_DBM); + db += (s32)RTW89_DB_INVERT_TABLE_OFFSET; + + return db_invert_table[db]; +} +EXPORT_SYMBOL(rtw89_db_quarter_to_linear); + +u64 rtw89_db_to_linear(s32 db) +{ + return rtw89_db_quarter_to_linear(db << RTW89_DBM_QUARTER_FACTOR); +} +EXPORT_SYMBOL(rtw89_db_to_linear); void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used) { diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index 80441e8da60b..bd08495301e4 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -6,8 +6,6 @@ #include "core.h" -#define RTW89_LINEAR_FRAC_BITS 3 - #define rtw89_iterate_vifs_bh(rtwdev, iterator, data) \ ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) @@ -75,8 +73,10 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask) } } -u32 rtw89_linear_2_db(u64 linear); -u64 rtw89_db_2_linear(u32 db); +s32 rtw89_linear_to_db_quarter(u64 val); +s32 rtw89_linear_to_db(u64 val); +u64 rtw89_db_quarter_to_linear(s32 db); +u64 rtw89_db_to_linear(s32 db); void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used); #endif From c90eb12693a1a0f51d2c2bbbf01dfd0a4f6d9482 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 319/465] wifi: rtw89: refine mechanism of TAS JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8ef675fc797b69aee6d729c6e191b7d3a6e2714e Author: Kuan-Chung Chen Date: Thu Mar 6 10:11:41 2025 +0800 wifi: rtw89: refine mechanism of TAS TAS state switching mechanism now incorporates the TX ratio as a decision parameter. The average power calculation has been improved by using a higher resolution conversion from dBm to linear. During scan or MCC operations, TAS state is forced to static SAR and suspend the average power calculation. Additionally, TAS window size depends on the regulatory domain and band to ensure compliance. TAS is enabled when permitted by the regulatory domain and is currently supported on the 8852CE. For debugging, add a flag to disable_dm that can stop TAS mechanism. Co-developed-by: Zong-Zhe Yang Signed-off-by: Zong-Zhe Yang Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250306021144.12854-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/chan.c | 10 +- drivers/net/wireless/realtek/rtw89/core.c | 4 +- drivers/net/wireless/realtek/rtw89/core.h | 28 +- drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/fw.h | 6 + drivers/net/wireless/realtek/rtw89/phy.c | 24 ++ drivers/net/wireless/realtek/rtw89/regd.c | 13 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 3 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/sar.c | 383 ++++++++++++++---- drivers/net/wireless/realtek/rtw89/sar.h | 5 +- 15 files changed, 384 insertions(+), 98 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index be6d73273910..f60e93870b09 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -8,6 +8,7 @@ #include "fw.h" #include "mac.h" #include "ps.h" +#include "sar.h" #include "util.h" static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev, @@ -2673,6 +2674,7 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; struct rtw89_entity_weight w = {}; + int ret; rtwvif_link->chanctx_idx = cfg->idx; rtwvif_link->chanctx_assigned = true; @@ -2692,7 +2694,13 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0); out: - return rtw89_set_channel(rtwdev); + ret = rtw89_set_channel(rtwdev); + if (ret) + return ret; + + rtw89_tas_reset(rtwdev, true); + + return 0; } void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 4d286182e21d..cc9b014457ac 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4601,8 +4601,6 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) rtw89_mac_cfg_phy_rpt_bands(rtwdev, true); rtw89_mac_update_rts_threshold(rtwdev); - rtw89_tas_reset(rtwdev); - ret = rtw89_hci_start(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to start hci\n"); @@ -4956,6 +4954,7 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true); rtw89_hci_recalc_int_mit(rtwdev); rtw89_phy_config_edcca(rtwdev, bb, true); + rtw89_tas_scan(rtwdev, true); rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr); } @@ -4982,6 +4981,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx); bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); rtw89_phy_config_edcca(rtwdev, bb, false); + rtw89_tas_scan(rtwdev, false); rtwdev->scanning = false; rtw89_for_each_active_bb(rtwdev, bb) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 3f62df657e1f..074c865e6d03 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4240,6 +4240,7 @@ enum rtw89_chanctx_state { enum rtw89_chanctx_callbacks { RTW89_CHANCTX_CALLBACK_PLACEHOLDER, RTW89_CHANCTX_CALLBACK_RFK, + RTW89_CHANCTX_CALLBACK_TAS, NUM_OF_RTW89_CHANCTX_CALLBACKS, }; @@ -4281,6 +4282,7 @@ struct rtw89_chip_info { bool support_unii4; bool support_rnr; bool support_ant_gain; + bool support_tas; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; bool rx_freq_frome_ie; @@ -4679,18 +4681,29 @@ struct rtw89_6ghz_span { enum rtw89_tas_state { RTW89_TAS_STATE_DPR_OFF, RTW89_TAS_STATE_DPR_ON, - RTW89_TAS_STATE_DPR_FORBID, + RTW89_TAS_STATE_STATIC_SAR, }; -#define RTW89_TAS_MAX_WINDOW 50 +#define RTW89_TAS_TX_RATIO_WINDOW 6 +#define RTW89_TAS_TXPWR_WINDOW 180 struct rtw89_tas_info { - s16 txpwr_history[RTW89_TAS_MAX_WINDOW]; - s32 total_txpwr; - u8 cur_idx; - s8 dpr_gap; - s8 delta; + u16 tx_ratio_history[RTW89_TAS_TX_RATIO_WINDOW]; + u64 txpwr_history[RTW89_TAS_TXPWR_WINDOW]; + u8 txpwr_head_idx; + u8 txpwr_tail_idx; + u8 tx_ratio_idx; + u16 total_tx_ratio; + u64 total_txpwr; + u64 instant_txpwr; + u32 window_size; + s8 dpr_on_threshold; + s8 dpr_off_threshold; + enum rtw89_tas_state backup_state; enum rtw89_tas_state state; + bool keep_history; + bool block_regd; bool enable; + bool pause; }; struct rtw89_chanctx_cfg { @@ -4748,6 +4761,7 @@ struct rtw89_edcca_bak { enum rtw89_dm_type { RTW89_DM_DYNAMIC_EDCCA, RTW89_DM_THERMAL_PROTECT, + RTW89_DM_TAS, }; #define RTW89_THERMAL_PROT_LV_MAX 5 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 885a5ebeb6cd..f2c5753fd386 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4154,6 +4154,7 @@ static const struct rtw89_disabled_dm_info { } rtw89_disabled_dm_infos[] = { DM_INFO(DYNAMIC_EDCCA), DM_INFO(THERMAL_PROTECT), + DM_INFO(TAS), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index e0faed076150..55255b48bdb7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4543,6 +4543,12 @@ struct rtw89_c2h_rfk_report { u8 version; } __packed; +struct rtw89_c2h_rf_tas_info { + struct rtw89_c2h_hdr hdr; + __le32 cur_idx; + __le16 txpwr_history[20]; +} __packed; + #define RTW89_FW_RSVD_PLE_SIZE 0x800 #define RTW89_FW_BACKTRACE_INFO_SIZE 8 diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e4b690a5febc..abc2a7e989eb 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3459,6 +3459,30 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3 static void rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + const struct rtw89_c2h_rf_tas_info *rf_tas = + (const struct rtw89_c2h_rf_tas_info *)c2h->data; + const enum rtw89_sar_sources src = rtwdev->sar.src; + struct rtw89_tas_info *tas = &rtwdev->tas; + u64 linear = 0; + u32 i, cur_idx; + s16 txpwr; + + if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) + return; + + cur_idx = le32_to_cpu(rf_tas->cur_idx); + for (i = 0; i < cur_idx; i++) { + txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]); + linear += rtw89_db_quarter_to_linear(txpwr); + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: index: %u, txpwr: %d\n", i, txpwr); + } + + if (cur_idx == 0) + tas->instant_txpwr = rtw89_db_to_linear(0); + else + tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx); } static diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 0e67d0f128dd..d31403f9009e 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -721,6 +721,18 @@ static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev, sband->channels[i].flags |= IEEE80211_CHAN_DISABLED; } +static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev) +{ + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd *regd = regulatory->regd; + struct rtw89_tas_info *tas = &rtwdev->tas; + + if (!tas->enable) + return; + + tas->block_regd = !test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap); +} + static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, struct wiphy *wiphy, struct regulatory_request *request) @@ -738,6 +750,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, rtw89_regd_apply_policy_unii4(rtwdev, wiphy); rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); + rtw89_regd_apply_policy_tas(rtwdev); } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index b709dd71b06a..0d482cd57f6e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2498,6 +2498,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = false, + .support_tas = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 638236bffebb..286334e26c84 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2216,6 +2216,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = false, .support_ant_gain = false, + .support_tas = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 8bec74716f84..eceb4fb9880d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -852,6 +852,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = true, + .support_tas = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index e62810660005..bbf37442c492 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -785,6 +785,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = true, + .support_tas = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 5d3e105dc87f..08bcdf246382 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -12,6 +12,7 @@ #include "rtw8852c.h" #include "rtw8852c_rfk.h" #include "rtw8852c_table.h" +#include "sar.h" #include "util.h" #define RTW8852C_FW_FORMAT_MAX 1 @@ -2880,6 +2881,7 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chanctx_listener rtw8852c_chanctx_listener = { .callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852c_rfk_chanctx_cb, + .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb, }; #ifdef CONFIG_PM @@ -3011,6 +3013,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { BIT(NL80211_CHAN_WIDTH_160), .support_unii4 = true, .support_ant_gain = true, + .support_tas = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_frome_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 210fa818ffe8..d90af2daebcf 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2770,6 +2770,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { BIT(NL80211_CHAN_WIDTH_160), .support_unii4 = true, .support_ant_gain = false, + .support_tas = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 24a32dc4e67d..0b5af9528702 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -7,10 +7,16 @@ #include "phy.h" #include "reg.h" #include "sar.h" +#include "util.h" #define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ +#define RTW89_TAS_SAR_GAP (1 << RTW89_TAS_FACTOR) #define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR) #define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) +#define RTW89_TAS_TX_RATIO_THRESHOLD 70 +#define RTW89_TAS_DFLT_TX_RATIO 80 +#define RTW89_TAS_DPR_ON_OFFSET (RTW89_TAS_DELTA + RTW89_TAS_SAR_GAP) +#define RTW89_TAS_DPR_OFF_OFFSET (4 << RTW89_TAS_FACTOR) static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, u32 center_freq) @@ -117,8 +123,8 @@ static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) RTW89_SAR_TXPWR_MAC_MAX); } -static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, - s8 cfg) +static s32 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, + s32 cfg) { const u8 fct = sar_hdl->txpwr_factor_sar; @@ -128,8 +134,8 @@ static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, return cfg >> (RTW89_TAS_FACTOR - fct); } -static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, - s8 cfg) +static s32 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, + s32 cfg) { const u8 fct = sar_hdl->txpwr_factor_sar; @@ -139,13 +145,43 @@ static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, return cfg << (RTW89_TAS_FACTOR - fct); } +static bool rtw89_tas_is_active(struct rtw89_dev *rtwdev) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + struct rtw89_vif *rtwvif; + + if (!tas->enable) + return false; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (ieee80211_vif_is_mld(rtwvif_to_vif(rtwvif))) + return false; + } + + return true; +} + +static const char *rtw89_tas_state_str(enum rtw89_tas_state state) +{ + switch (state) { + case RTW89_TAS_STATE_DPR_OFF: + return "DPR OFF"; + case RTW89_TAS_STATE_DPR_ON: + return "DPR ON"; + case RTW89_TAS_STATE_STATIC_SAR: + return "STATIC SAR"; + default: + return NULL; + } +} + s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; struct rtw89_tas_info *tas = &rtwdev->tas; - s8 delta; + s32 offset; int ret; s32 cfg; u8 fct; @@ -159,15 +195,17 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) if (ret) return RTW89_SAR_TXPWR_MAC_MAX; - if (tas->enable) { + if (rtw89_tas_is_active(rtwdev)) { switch (tas->state) { case RTW89_TAS_STATE_DPR_OFF: - return RTW89_SAR_TXPWR_MAC_MAX; - case RTW89_TAS_STATE_DPR_ON: - delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta); - cfg -= delta; + offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_OFF_OFFSET); + cfg += offset; break; - case RTW89_TAS_STATE_DPR_FORBID: + case RTW89_TAS_STATE_DPR_ON: + offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_ON_OFFSET); + cfg -= offset; + break; + case RTW89_TAS_STATE_STATIC_SAR: default: break; } @@ -223,13 +261,23 @@ int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_tas_info *tas = &rtwdev->tas; char *p = buf, *end = buf + bufsz; - if (!tas->enable) { + if (!rtw89_tas_is_active(rtwdev)) { p += scnprintf(p, end - p, "no TAS is applied\n"); goto out; } - p += scnprintf(p, end - p, "DPR gap: %d\n", tas->dpr_gap); - p += scnprintf(p, end - p, "TAS delta: %d\n", tas->delta); + p += scnprintf(p, end - p, "State: %s\n", + rtw89_tas_state_str(tas->state)); + p += scnprintf(p, end - p, "Average time: %d\n", + tas->window_size * 2); + p += scnprintf(p, end - p, "SAR gap: %d dBm\n", + RTW89_TAS_SAR_GAP >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR gap: %d dBm\n", + RTW89_TAS_DPR_GAP >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR ON offset: %d dBm\n", + RTW89_TAS_DPR_ON_OFFSET >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR OFF offset: %d dBm\n", + RTW89_TAS_DPR_OFF_OFFSET >> RTW89_TAS_FACTOR); out: return p - buf; @@ -250,6 +298,7 @@ static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); rtw89_core_set_chip_txpwr(rtwdev); + rtw89_tas_reset(rtwdev, false); return 0; } @@ -314,65 +363,174 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, return rtw89_apply_sar_common(rtwdev, &sar_common); } -static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) +static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg) { + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; - struct rtw89_tas_info *tas = &rtwdev->tas; - s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; - s32 dpr_on_threshold, dpr_off_threshold, cfg; - enum rtw89_tas_state state = tas->state; - const struct rtw89_chan *chan; int ret; - lockdep_assert_wiphy(rtwdev->hw->wiphy); - if (src == RTW89_SAR_SOURCE_NONE) - return; + return false; - chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); - ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); + ret = sar_hdl->query_sar_config(rtwdev, chan->freq, cfg); if (ret) - return; + return false; - cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); + *cfg = rtw89_txpwr_sar_to_tas(sar_hdl, *cfg); - if (tas->delta >= cfg) { - rtw89_debug(rtwdev, RTW89_DBG_SAR, - "TAS delta exceed SAR limit\n"); - state = RTW89_TAS_STATE_DPR_FORBID; - goto out; - } + return true; +} - dpr_on_threshold = cfg; - dpr_off_threshold = cfg - tas->dpr_gap; - rtw89_debug(rtwdev, RTW89_DBG_SAR, - "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n", - dpr_on_threshold, dpr_off_threshold, txpwr_avg); +static void rtw89_tas_state_update(struct rtw89_dev *rtwdev, + enum rtw89_tas_state state) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; - if (txpwr_avg >= dpr_on_threshold) - state = RTW89_TAS_STATE_DPR_ON; - else if (txpwr_avg < dpr_off_threshold) - state = RTW89_TAS_STATE_DPR_OFF; - -out: if (tas->state == state) return; - rtw89_debug(rtwdev, RTW89_DBG_SAR, - "TAS old state: %d, new state: %d\n", tas->state, state); + rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: switch state: %s -> %s\n", + rtw89_tas_state_str(tas->state), rtw89_tas_state_str(state)); + tas->state = state; rtw89_core_set_chip_txpwr(rtwdev); } +static u32 rtw89_tas_get_window_size(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + u8 band = chan->band_type; + u8 regd = rtw89_regd_get(rtwdev, band); + + switch (regd) { + default: + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: regd: %u is unhandled\n", regd); + fallthrough; + case RTW89_IC: + case RTW89_KCC: + return 180; + case RTW89_FCC: + switch (band) { + case RTW89_BAND_2G: + return 50; + case RTW89_BAND_5G: + return 30; + case RTW89_BAND_6G: + default: + return 15; + } + break; + } +} + +static void rtw89_tas_window_update(struct rtw89_dev *rtwdev) +{ + u32 window_size = rtw89_tas_get_window_size(rtwdev); + struct rtw89_tas_info *tas = &rtwdev->tas; + u64 total_txpwr = 0; + u8 head_idx; + u32 i, j; + + WARN_ON_ONCE(tas->window_size > RTW89_TAS_TXPWR_WINDOW); + + if (tas->window_size == window_size) + return; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: window update: %u -> %u\n", + tas->window_size, window_size); + + head_idx = (tas->txpwr_tail_idx - window_size + 1 + RTW89_TAS_TXPWR_WINDOW) % + RTW89_TAS_TXPWR_WINDOW; + for (i = 0; i < window_size; i++) { + j = (head_idx + i) % RTW89_TAS_TXPWR_WINDOW; + total_txpwr += tas->txpwr_history[j]; + } + + tas->window_size = window_size; + tas->total_txpwr = total_txpwr; + tas->txpwr_head_idx = head_idx; +} + +static void rtw89_tas_history_update(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0); + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_tas_info *tas = &rtwdev->tas; + u8 tx_ratio = env->ifs_clm_tx_ratio; + u64 instant_txpwr, txpwr; + + /* txpwr in unit of linear(mW) multiply by percentage */ + if (tx_ratio == 0) { + /* special case: idle tx power + * use -40 dBm * 100 tx ratio + */ + instant_txpwr = rtw89_db_to_linear(-40); + txpwr = instant_txpwr * 100; + } else { + instant_txpwr = tas->instant_txpwr; + txpwr = instant_txpwr * tx_ratio; + } + + tas->total_txpwr += txpwr - tas->txpwr_history[tas->txpwr_head_idx]; + tas->total_tx_ratio += tx_ratio - tas->tx_ratio_history[tas->tx_ratio_idx]; + tas->tx_ratio_history[tas->tx_ratio_idx] = tx_ratio; + + tas->txpwr_head_idx = (tas->txpwr_head_idx + 1) % RTW89_TAS_TXPWR_WINDOW; + tas->txpwr_tail_idx = (tas->txpwr_tail_idx + 1) % RTW89_TAS_TXPWR_WINDOW; + tas->tx_ratio_idx = (tas->tx_ratio_idx + 1) % RTW89_TAS_TX_RATIO_WINDOW; + tas->txpwr_history[tas->txpwr_tail_idx] = txpwr; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: instant_txpwr: %d, tx_ratio: %u, txpwr: %d\n", + rtw89_linear_to_db_quarter(instant_txpwr), tx_ratio, + rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT))); +} + +static void rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + s32 dpr_on_threshold, dpr_off_threshold; + enum rtw89_tas_state state; + u16 tx_ratio_avg; + s32 txpwr_avg; + u64 linear; + + linear = DIV_ROUND_DOWN_ULL(tas->total_txpwr, tas->window_size * PERCENT); + txpwr_avg = rtw89_linear_to_db_quarter(linear); + tx_ratio_avg = tas->total_tx_ratio / RTW89_TAS_TX_RATIO_WINDOW; + dpr_on_threshold = tas->dpr_on_threshold; + dpr_off_threshold = tas->dpr_off_threshold; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: DPR_ON: %d, DPR_OFF: %d, txpwr_avg: %d, tx_ratio_avg: %u\n", + dpr_on_threshold, dpr_off_threshold, txpwr_avg, tx_ratio_avg); + + if (tx_ratio_avg >= RTW89_TAS_TX_RATIO_THRESHOLD) + state = RTW89_TAS_STATE_STATIC_SAR; + else if (txpwr_avg >= dpr_on_threshold) + state = RTW89_TAS_STATE_DPR_ON; + else if (txpwr_avg < dpr_off_threshold) + state = RTW89_TAS_STATE_DPR_OFF; + else + return; + + rtw89_tas_state_update(rtwdev, state); +} + void rtw89_tas_init(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; struct rtw89_acpi_dsm_result res = {}; int ret; u8 val; + if (!chip->support_tas) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_SAR, @@ -396,65 +554,116 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n"); return; } - - tas->dpr_gap = RTW89_TAS_DPR_GAP; - tas->delta = RTW89_TAS_DELTA; } -void rtw89_tas_reset(struct rtw89_dev *rtwdev) +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force) { + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); struct rtw89_tas_info *tas = &rtwdev->tas; + u64 linear; + s32 cfg; + int i; - if (!tas->enable) + if (!rtw89_tas_is_active(rtwdev)) return; - memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); - tas->total_txpwr = 0; - tas->cur_idx = 0; - tas->state = RTW89_TAS_STATE_DPR_OFF; -} + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) + return; -static const struct rtw89_reg_def txpwr_regs[] = { - {R_PATH0_TXPWR, B_PATH0_TXPWR}, - {R_PATH1_TXPWR, B_PATH1_TXPWR}, -}; + tas->dpr_on_threshold = cfg - RTW89_TAS_SAR_GAP; + tas->dpr_off_threshold = cfg - RTW89_TAS_SAR_GAP - RTW89_TAS_DPR_GAP; + + /* avoid history reset after new SAR apply */ + if (!force && tas->keep_history) + return; + + linear = rtw89_db_quarter_to_linear(cfg) * RTW89_TAS_DFLT_TX_RATIO; + for (i = 0; i < RTW89_TAS_TXPWR_WINDOW; i++) + tas->txpwr_history[i] = linear; + + for (i = 0; i < RTW89_TAS_TX_RATIO_WINDOW; i++) + tas->tx_ratio_history[i] = RTW89_TAS_DFLT_TX_RATIO; + + tas->total_tx_ratio = RTW89_TAS_DFLT_TX_RATIO * RTW89_TAS_TX_RATIO_WINDOW; + tas->total_txpwr = linear * RTW89_TAS_TXPWR_WINDOW; + tas->window_size = RTW89_TAS_TXPWR_WINDOW; + tas->txpwr_head_idx = 0; + tas->txpwr_tail_idx = RTW89_TAS_TXPWR_WINDOW - 1; + tas->tx_ratio_idx = 0; + tas->state = RTW89_TAS_STATE_DPR_OFF; + tas->backup_state = RTW89_TAS_STATE_DPR_OFF; + tas->keep_history = true; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: band: %u, freq: %u\n", chan->band_type, chan->freq); +} void rtw89_tas_track(struct rtw89_dev *rtwdev) { - struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0); - struct rtw89_env_monitor_info *env = &bb->env_monitor; - const enum rtw89_sar_sources src = rtwdev->sar.src; - u8 max_nss_num = rtwdev->chip->rf_path_num; struct rtw89_tas_info *tas = &rtwdev->tas; - s16 tmp, txpwr, instant_txpwr = 0; - u32 val; - int i; + struct rtw89_hal *hal = &rtwdev->hal; + s32 cfg; - if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS)) return; - if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) + if (!rtw89_tas_is_active(rtwdev)) return; - for (i = 0; i < max_nss_num; i++) { - val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr, - txpwr_regs[i].mask); - tmp = sign_extend32(val, 8); - if (tmp <= 0) - return; - instant_txpwr += tmp; + if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) { + rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); + return; } - instant_txpwr /= max_nss_num; - /* in unit of 0.25 dBm multiply by percentage */ - txpwr = instant_txpwr * env->ifs_clm_tx_ratio; - tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; - tas->txpwr_history[tas->cur_idx] = txpwr; - rtw89_debug(rtwdev, RTW89_DBG_SAR, - "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n", - instant_txpwr, env->ifs_clm_tx_ratio, txpwr); + if (tas->pause) + return; - tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; - - rtw89_tas_state_update(rtwdev); + rtw89_tas_window_update(rtwdev); + rtw89_tas_history_update(rtwdev); + rtw89_tas_rolling_average(rtwdev); } + +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + s32 cfg; + + if (!rtw89_tas_is_active(rtwdev)) + return; + + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) + return; + + if (start) { + tas->backup_state = tas->state; + rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); + } else { + rtw89_tas_state_update(rtwdev, tas->backup_state); + } +} + +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, + enum rtw89_chanctx_state state) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + s32 cfg; + + if (!rtw89_tas_is_active(rtwdev)) + return; + + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) + return; + + switch (state) { + case RTW89_CHANCTX_STATE_MCC_START: + tas->pause = true; + rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); + break; + case RTW89_CHANCTX_STATE_MCC_STOP: + tas->pause = false; + break; + default: + break; + } +} +EXPORT_SYMBOL(rtw89_tas_chanctx_cb); diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 095277df5a71..0df1661db9a8 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -25,7 +25,10 @@ int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); void rtw89_tas_init(struct rtw89_dev *rtwdev); -void rtw89_tas_reset(struct rtw89_dev *rtwdev); +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force); void rtw89_tas_track(struct rtw89_dev *rtwdev); +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start); +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, + enum rtw89_chanctx_state state); #endif From 0c67dd6ea1796156e8bb93d90c83ff639dd75ba9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:56 +0200 Subject: [PATCH 320/465] wifi: rtw89: enable dynamic antenna gain based on country JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9c225e119866a20eb76fd30ceed482cd363841ed Author: Kuan-Chung Chen Date: Thu Mar 6 10:11:42 2025 +0800 wifi: rtw89: enable dynamic antenna gain based on country The dynamic antenna gain (DAG) considers the country, meaning DAG can be activated only when countries and regulatory domains allow it. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250306021144.12854-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 11 ++++++++--- drivers/net/wireless/realtek/rtw89/regd.c | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 074c865e6d03..b8f46207141e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4666,6 +4666,7 @@ enum rtw89_ant_gain_domain_type { struct rtw89_ant_gain_info { s8 offset[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR]; u32 regd_enabled; + bool block_country; }; struct rtw89_6ghz_span { diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index abc2a7e989eb..bd736417f467 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2044,7 +2044,7 @@ static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 cente if (!chip->support_ant_gain) return 0; - if (!(ant_gain->regd_enabled & BIT(regd))) + if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd))) return 0; offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); @@ -2057,10 +2057,14 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan) { struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; + const struct rtw89_chip_info *chip = rtwdev->chip; u8 regd = rtw89_regd_get(rtwdev, chan->band_type); s8 offset_patha, offset_pathb; - if (!(ant_gain->regd_enabled & BIT(regd))) + if (!chip->support_ant_gain) + return 0; + + if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd))) return 0; offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); @@ -2079,7 +2083,8 @@ int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, char *p = buf, *end = buf + bufsz; s8 offset_patha, offset_pathb; - if (!chip->support_ant_gain || !(ant_gain->regd_enabled & BIT(regd))) { + if (!(chip->support_ant_gain && (ant_gain->regd_enabled & BIT(regd))) || + ant_gain->block_country) { p += scnprintf(p, end - p, "no DAG is applied\n"); goto out; } diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index d31403f9009e..655323a79608 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -733,6 +733,19 @@ static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev) tas->block_regd = !test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap); } +static void rtw89_regd_apply_policy_ant_gain(struct rtw89_dev *rtwdev) +{ + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_regd *regd = regulatory->regd; + + if (!chip->support_ant_gain) + return; + + ant_gain->block_country = !test_bit(RTW89_REGD_FUNC_DAG, regd->func_bitmap); +} + static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, struct wiphy *wiphy, struct regulatory_request *request) @@ -751,6 +764,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, rtw89_regd_apply_policy_unii4(rtwdev, wiphy); rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); rtw89_regd_apply_policy_tas(rtwdev); + rtw89_regd_apply_policy_ant_gain(rtwdev); } static From f0de87c9698c6c59b48c862f3e91e670b844b614 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 321/465] wifi: rtw89: 8922a: enable dynamic antenna gain JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6f039d9ba9cb1ebab9e790c1f4d3fa9e35d0b4fa Author: Kuan-Chung Chen Date: Thu Mar 6 10:11:43 2025 +0800 wifi: rtw89: 8922a: enable dynamic antenna gain The 8922A now supports dynamic antenna gain. However, in firmware before v0.35.64.0, different transmit powers cannot be applied to each RF path. To comply with regulatory limits in these older firmware, the lower of the two requested transmit powers will be used for both paths when they differ. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250306021144.12854-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 6 +++ drivers/net/wireless/realtek/rtw89/phy.h | 7 +++ drivers/net/wireless/realtek/rtw89/reg.h | 28 +++++++--- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 54 ++++++++++++++++++- 6 files changed, 88 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b8f46207141e..c32b74df1443 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4492,6 +4492,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_CH_INFO_BE_V0, RTW89_FW_FEATURE_LPS_CH_INFO, RTW89_FW_FEATURE_NO_PHYCAP_P1, + RTW89_FW_FEATURE_NO_POWER_DIFFERENCE, }; struct rtw89_fw_suit { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d0a246f415ff..8643b17866f8 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -849,6 +849,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 47, 0, CH_INFO_BE_V0), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 49, 0, RFK_PRE_NOTIFY_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 51, 0, NO_PHYCAP_P1), + __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 64, 0, NO_POWER_DIFFERENCE), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index bd736417f467..f4eee642e5ce 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2050,6 +2050,9 @@ static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 cente offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq); + if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw)) + return min(offset_patha, offset_pathb); + return max(offset_patha, offset_pathb); } @@ -2067,6 +2070,9 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd))) return 0; + if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw)) + return 0; + offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, chan->freq); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index e20fff9bac3e..518a100375fb 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -914,6 +914,13 @@ static inline s8 rtw89_phy_txpwr_rf_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_rf) return txpwr_rf << (chip->txpwr_factor_bb - chip->txpwr_factor_rf); } +static inline s8 rtw89_phy_txpwr_bb_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_bb) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_bb >> (chip->txpwr_factor_bb - chip->txpwr_factor_rf); +} + static inline s8 rtw89_phy_txpwr_rf_to_mac(struct rtw89_dev *rtwdev, s8 txpwr_rf) { const struct rtw89_chip_info *chip = rtwdev->chip; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index ec06f5bd0fab..44201efee01c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -9181,6 +9181,16 @@ #define B_IQKINF2_FCNT GENMASK(23, 16) #define B_IQKINF2_KCNT GENMASK(15, 8) #define B_IQKINF2_NCTLV GENMASK(7, 0) +#define R_TXAGC_REF_DBM_RF1_P0 0xBC04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P0 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P0 GENMASK(19, 11) +#define R_TSSI_K_RF1_P0 0xBC28 +#define B_TSSI_K_OFDM_RF1_P0 GENMASK(9, 0) +#define R_TXAGC_REF_DBM_RF1_P1 0xBD04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P1 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P1 GENMASK(19, 11) +#define R_TSSI_K_RF1_P1 0xBD28 +#define B_TSSI_K_OFDM_RF1_P1 GENMASK(9, 0) #define R_RFK_ST 0xBFF8 #define R_DCOF0 0xC000 #define B_DCOF0_RST BIT(17) @@ -9350,16 +9360,18 @@ #define R_TSSI_MAP_OFST_P1 0xE720 #define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9) #define B_TSSI_MAP_OFST_CCK GENMASK(26, 18) -#define R_TXAGC_REF0_P0 0xE628 -#define R_TXAGC_REF0_P1 0xE728 -#define B_TXAGC_REF0_OFDM_DBM GENMASK(8, 0) -#define B_TXAGC_REF0_CCK_DBM GENMASK(17, 9) -#define B_TXAGC_REF0_OFDM_CW GENMASK(26, 18) -#define R_TXAGC_REF1_P0 0xE62C -#define R_TXAGC_REF1_P1 0xE72C -#define B_TXAGC_REF1_CCK_CW GENMASK(8, 0) +#define R_TXAGC_REF_DBM_P0 0xE628 +#define B_TXAGC_OFDM_REF_DBM_P0 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P0 GENMASK(17, 9) +#define R_TSSI_K_P0 0xE6A0 +#define B_TSSI_K_OFDM_P0 GENMASK(29, 20) #define R_TXPWR_RSTB 0xE70C #define B_TXPWR_RSTB BIT(16) +#define R_TXAGC_REF_DBM_P1 0xE728 +#define B_TXAGC_OFDM_REF_DBM_P1 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P1 GENMASK(17, 9) +#define R_TSSI_K_P1 0xE7A0 +#define B_TSSI_K_OFDM_P1 GENMASK(29, 20) /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index d90af2daebcf..8082592db84a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2156,6 +2156,56 @@ static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev, B_BE_PWR_REF_CTRL_CCK, ref_cck); } +static const struct rtw89_reg_def rtw8922a_txpwr_ref[][3] = { + {{ .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_OFDM_REF_DBM_P0}, + { .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_CCK_REF_DBM_P0}, + { .addr = R_TSSI_K_P0, .mask = B_TSSI_K_OFDM_P0} + }, + {{ .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_OFDM_REF_DBM_RF1_P0}, + { .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_CCK_REF_DBM_RF1_P0}, + { .addr = R_TSSI_K_RF1_P0, .mask = B_TSSI_K_OFDM_RF1_P0} + }, +}; + +static void rtw8922a_set_txpwr_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s16 pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); + const struct rtw89_chip_info *chip = rtwdev->chip; + static const u32 path_ofst[] = {0x0, 0x100}; + const struct rtw89_reg_def *txpwr_ref; + static const s16 tssi_k_base = 0x12; + s16 tssi_k_ofst = abs(pwr_ofst) + tssi_k_base; + s16 ofst_dec[RF_PATH_NUM_8922A]; + s16 tssi_k[RF_PATH_NUM_8922A]; + s16 pwr_ref_ofst; + s16 pwr_ref = 0; + u8 i; + + if (rtwdev->hal.cv == CHIP_CAV) + pwr_ref = 16; + + pwr_ref <<= chip->txpwr_factor_rf; + pwr_ref_ofst = pwr_ref - rtw89_phy_txpwr_bb_to_rf(rtwdev, abs(pwr_ofst)); + + ofst_dec[RF_PATH_A] = pwr_ofst > 0 ? pwr_ref : pwr_ref_ofst; + ofst_dec[RF_PATH_B] = pwr_ofst > 0 ? pwr_ref_ofst : pwr_ref; + tssi_k[RF_PATH_A] = pwr_ofst > 0 ? tssi_k_base : tssi_k_ofst; + tssi_k[RF_PATH_B] = pwr_ofst > 0 ? tssi_k_ofst : tssi_k_base; + + for (i = 0; i < RF_PATH_NUM_8922A; i++) { + txpwr_ref = rtw8922a_txpwr_ref[phy_idx]; + + rtw89_phy_write32_mask(rtwdev, txpwr_ref[0].addr + path_ofst[i], + txpwr_ref[0].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[1].addr + path_ofst[i], + txpwr_ref[1].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[2].addr + path_ofst[i], + txpwr_ref[2].mask, tssi_k[i]); + } +} + static void rtw8922a_bb_tx_triangular(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx) { @@ -2192,6 +2242,8 @@ static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev, rtw8922a_set_tx_shape(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); + rtw8922a_set_txpwr_diff(rtwdev, chan, phy_idx); + rtw8922a_set_txpwr_ref(rtwdev, phy_idx); } static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2769,7 +2821,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), .support_unii4 = true, - .support_ant_gain = false, + .support_ant_gain = true, .support_tas = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, From c0d962cd7d0e3097e5b1032ecae4bd6652d8f553 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 322/465] wifi: rtw89: set force HE TB mode when connecting to 11ax AP JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a9b56f219a0fa550f92e65ac58443a7892380e09 Author: Dian-Syuan Yang Date: Thu Mar 6 10:11:44 2025 +0800 wifi: rtw89: set force HE TB mode when connecting to 11ax AP Some of 11ax AP set the UL HE-SIG-A2 reserved subfield to all 0s, which will cause the 11be chip to recognize trigger frame as EHT. We propose a method to bypass the "UL HE-SIG-A2 reserved subfield" and always uses HE TB in response to the AP's trigger frame. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250306021144.12854-6-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/mac.c | 26 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 2 ++ drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 4 +++ 4 files changed, 33 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 513c317b286c..b4841f948ec1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4837,6 +4837,32 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, rtw89_write32_set(rtwdev, reg, mac->narrow_bw_ru_dis.mask); } +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + struct ieee80211_bss_conf *bss_conf; + bool set; + u32 reg; + + if (rtwdev->chip->chip_gen != RTW89_CHIP_BE) + return; + + rcu_read_lock(); + + bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + set = bss_conf->he_support && !bss_conf->eht_support; + + rcu_read_unlock(); + + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_CLIENT_OM_CTRL, + rtwvif_link->mac_idx); + + if (set) + rtw89_write32_set(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); + else + rtw89_write32_clr(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); +} + void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { rtw89_mac_port_cfg_func_sw(rtwdev, rtwvif_link); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 4d5a06a2fe9b..fd7935d24501 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1188,6 +1188,8 @@ void rtw89_mac_port_cfg_rx_sync(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool en); void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link); void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en); int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 778ca8589284..4fded07d0bee 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -656,6 +656,7 @@ static void __rtw89_ops_bss_link_assoc(struct rtw89_dev *rtwdev, rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, rtwvif_link); rtw89_mac_port_update(rtwdev, rtwvif_link); rtw89_mac_set_he_obss_narrow_bw_ru(rtwdev, rtwvif_link); + rtw89_mac_set_he_tb(rtwdev, rtwvif_link); } static void __rtw89_ops_bss_assoc(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 44201efee01c..c776954ad360 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7102,6 +7102,10 @@ #define B_BE_MACLBK_RDY_NUM_MASK GENMASK(7, 3) #define B_BE_MACLBK_EN BIT(0) +#define R_BE_CLIENT_OM_CTRL 0x11040 +#define R_BE_CLIENT_OM_CTRL_C1 0x15040 +#define B_BE_TRIG_DIS_EHTTB BIT(24) + #define R_BE_WMAC_NAV_CTL 0x11080 #define R_BE_WMAC_NAV_CTL_C1 0x15080 #define B_BE_WMAC_NAV_UPPER_EN BIT(26) From a237e0ce99ef7d3c6f264aed8b36a64b5976cb18 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 323/465] wifi: rtw89: coex: RTL8852BT coexistence Wi-Fi firmware support for 0.29.122.0 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bb76dd94b7c7dd1a6fd04226af89f3130f506b84 Author: Ching-Te Ku Date: Sat Mar 8 10:58:29 2025 +0800 wifi: rtw89: coex: RTL8852BT coexistence Wi-Fi firmware support for 0.29.122.0 Add format version of Wi-Fi firmware commands/events for firmware version 0.29.122.0. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250308025832.10400-2-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 858ff0cd1a23..f1a811c8713d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -132,6 +132,14 @@ static const u32 cxtbl[] = { static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { /* firmware version must be in decreasing order for each chip */ + {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0), + .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, + .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, + .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, + .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, + .max_role_num = 6, + }, {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 90, 0), .fcxbtcrpt = 7, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, From 033dffc65a2b8813fd8cb3847e7c7a2162862085 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 324/465] wifi: rtw89: coex: Fix coexistence report not show as expected JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a36230aa5f5efceaf5f81682673732a921b91518 Author: Ching-Te Ku Date: Sat Mar 8 10:58:30 2025 +0800 wifi: rtw89: coex: Fix coexistence report not show as expected This report will feedback some basic information from firmware(PTA counter, report counter, mailbox counter etc). And the report version need to match driver & firmware both side. The original logic break the switch case logic before driver update the report version. It made the report can not be parsed correctly. Delete the break at the version 7 and 8. Add logic to count C2H event report. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250308025832.10400-3-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index f1a811c8713d..94445c1d64d1 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1380,11 +1380,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } else if (ver->fcxbtcrpt == 8) { pfinfo = &pfwinfo->rpt_ctrl.finfo.v8; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v8); - break; } else if (ver->fcxbtcrpt == 7) { pfinfo = &pfwinfo->rpt_ctrl.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7); - break; } else { goto err; } @@ -8108,6 +8106,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, return; func = rtw89_btc_c2h_get_index_by_ver(rtwdev, func); + pfwinfo->cnt_c2h++; switch (func) { case BTF_EVNT_BUF_OVERFLOW: From 5a1216b62db7ac4b623b4752f3398a610afbc702 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 325/465] wifi: rtw89: coex: Add parser for Bluetooth channel map report version 7 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6db476db57cac11d943dc3a60576d9ce0bdf8781 Author: Ching-Te Ku Date: Sat Mar 8 10:58:31 2025 +0800 wifi: rtw89: coex: Add parser for Bluetooth channel map report version 7 In order to rearrange the structure member, the format update to version 7. And to parse the report correctly, add the related logic. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250308025832.10400-4-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 3 +++ drivers/net/wireless/realtek/rtw89/core.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 94445c1d64d1..659b92b14d88 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1540,6 +1540,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } else if (ver->fcxbtafh == 2) { pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v2; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v2); + } else if (ver->fcxbtafh == 7) { + pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v7; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v7); } else { goto err; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c32b74df1443..4be05d6cad18 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3044,6 +3044,7 @@ struct rtw89_btc_rpt_cmn_info { union rtw89_btc_fbtc_btafh_info { struct rtw89_btc_fbtc_btafh v1; struct rtw89_btc_fbtc_btafh_v2 v2; + struct rtw89_btc_fbtc_btafh_v7 v7; }; struct rtw89_btc_report_ctrl_state { From 0b113e514713cd8de8e9d3275096d7ebab200678 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 326/465] wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.4 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e5c45671d996f36f6f3c33486662adba5d86f8b1 Author: Ching-Te Ku Date: Sat Mar 8 10:58:32 2025 +0800 wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 7.0.4 RTL8852BE-VT support for firmware 29.122. Add parser for Bluetooth channel map report version 7. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250308025832.10400-5-pkshih@realtek.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 659b92b14d88..5ccf0cbaed2f 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -10,7 +10,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x07000313 +#define RTW89_COEX_VERSION 0x07000413 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 From df38ecb62f9f64bc0ae5cefa672f73b055f2370f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 327/465] wifi: rtw88: Add some definitions for RTL8814AU JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 679ec431477cdb68d1cab068c008da0de7f842ef Author: Bitterblue Smith Date: Fri Mar 7 02:22:17 2025 +0200 wifi: rtw88: Add some definitions for RTL8814AU Add various register definitions which will be used by the new driver. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1dcb5abb-26f8-4db5-be36-057de56465e5@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/reg.h | 66 ++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h index 209b6fc08a73..08e9494977e0 100644 --- a/drivers/net/wireless/realtek/rtw88/reg.h +++ b/drivers/net/wireless/realtek/rtw88/reg.h @@ -8,6 +8,7 @@ #define REG_SYS_FUNC_EN 0x0002 #define BIT_FEN_EN_25_1 BIT(13) #define BIT_FEN_ELDR BIT(12) +#define BIT_FEN_PCIEA BIT(6) #define BIT_FEN_CPUEN BIT(2) #define BIT_FEN_USBA BIT(2) #define BIT_FEN_BB_GLB_RST BIT(1) @@ -39,6 +40,9 @@ #define BIT_RF_RSTB BIT(1) #define BIT_RF_EN BIT(0) +#define REG_RF_CTRL1 0x0020 +#define REG_RF_CTRL2 0x0021 + #define REG_AFE_CTRL1 0x0024 #define BIT_MAC_CLK_SEL (BIT(20) | BIT(21)) #define REG_EFUSE_CTRL 0x0030 @@ -73,6 +77,8 @@ #define BIT_BT_PTA_EN BIT(5) #define BIT_WLRFE_4_5_EN BIT(2) +#define REG_GPIO_PIN_CTRL 0x0044 + #define REG_LED_CFG 0x004C #define BIT_LNAON_SEL_EN BIT(26) #define BIT_PAPE_SEL_EN BIT(25) @@ -110,6 +116,7 @@ #define BIT_SDIO_PAD_E5 BIT(18) #define REG_RF_B_CTRL 0x76 +#define REG_RF_CTRL3 0x0076 #define REG_AFE_CTRL_4 0x0078 #define BIT_CK320M_AFE_EN BIT(4) @@ -603,15 +610,25 @@ #define REG_CCA2ND 0x0838 #define REG_L1PKTH 0x0848 #define REG_CLKTRK 0x0860 +#define REG_CSI_MASK_SETTING1 0x0874 +#define REG_NBI_SETTING 0x087c +#define BIT_NBI_ENABLE BIT(13) +#define REG_CSI_FIX_MASK0 0x0880 +#define REG_CSI_FIX_MASK1 0x0884 +#define REG_CSI_FIX_MASK6 0x0898 +#define REG_CSI_FIX_MASK7 0x089c #define REG_ADCCLK 0x08AC #define REG_HSSI_READ 0x08B0 #define REG_FPGA0_XCD_RF_PARA 0x08B4 #define REG_RX_MCS_LIMIT 0x08BC #define REG_ADC160 0x08C4 +#define REG_DBGSEL 0x08fc #define REG_ANTSEL_SW 0x0900 #define REG_DAC_RSTB 0x090c +#define REG_PSD 0x0910 +#define BIT_PSD_INI GENMASK(23, 22) #define REG_SINGLE_TONE_CONT_TX 0x0914 - +#define REG_AGC_TABLE 0x0958 #define REG_RFE_CTRL_E 0x0974 #define REG_2ND_CCA_CTRL 0x0976 #define REG_IQK_COM00 0x0978 @@ -621,10 +638,18 @@ #define REG_FAS 0x09a4 #define REG_RXSB 0x0a00 +#define BIT_RXSB_ANA_DIV BIT(15) #define REG_CCK_RX 0x0a04 #define REG_CCK_PD_TH 0x0a0a - -#define REG_CCK0_FAREPORT 0xa2c +#define REG_PRECTRL 0x0a14 +#define BIT_DIS_CO_PATHSEL BIT(7) +#define BIT_IQ_WGT GENMASK(9, 8) +#define REG_CCA_MF 0x0a20 +#define BIT_MBC_WIN GENMASK(5, 4) +#define REG_CCK0_TX_FILTER1 0x0a20 +#define REG_CCK0_TX_FILTER2 0x0a24 +#define REG_CCK0_DEBUG_PORT 0x0a28 +#define REG_CCK0_FAREPORT 0x0a2c #define BIT_CCK0_2RX BIT(18) #define BIT_CCK0_MRC BIT(22) #define REG_FA_CCK 0x0a5c @@ -643,10 +668,18 @@ #define DIS_DPD_RATEVHT2SS_MCS1 BIT(9) #define DIS_DPD_RATEALL GENMASK(9, 0) +#define REG_CCA 0x0a70 +#define BIT_CCA_CO BIT(7) +#define REG_ANTSEL 0x0a74 +#define BIT_ANT_BYCO BIT(8) +#define REG_CCKTX 0x0a84 +#define BIT_CMB_CCA_2R BIT(28) + #define REG_CNTRST 0x0b58 #define REG_3WIRE_SWA 0x0c00 #define REG_RX_IQC_AB_A 0x0c10 +#define REG_RX_IQC_CD_A 0x0c14 #define REG_TXSCALE_A 0x0c1c #define BB_SWING_MASK GENMASK(31, 21) #define REG_TX_AGC_A_CCK_11_CCK_1 0xc20 @@ -674,7 +707,7 @@ #define REG_LSSI_WRITE_A 0x0c90 #define REG_PREDISTA 0x0c90 #define REG_TXAGCIDX 0x0c94 - +#define REG_TX_AGC_A 0x0c94 #define REG_RFE_PINMUX_A 0x0cb0 #define REG_RFE_INV_A 0x0cb4 #define REG_RFE_CTRL8 0x0cb4 @@ -683,6 +716,7 @@ #define DPDT_CTRL_PIN 0x77 #define RFE_INV_MASK 0x3ff00000 #define REG_RFECTL_A 0x0cb8 +#define REG_RFE_INV0 0x0cbc #define REG_RFE_INV8 0x0cbd #define BIT_MASK_RFE_INV89 GENMASK(1, 0) #define REG_RFE_INV16 0x0cbe @@ -703,6 +737,7 @@ #define REG_3WIRE_SWB 0x0e00 #define REG_RX_IQC_AB_B 0x0e10 +#define REG_RX_IQC_CD_B 0x0e14 #define REG_TXSCALE_B 0x0e1c #define REG_TX_AGC_B_CCK_11_CCK_1 0xe20 #define REG_TX_AGC_B_OFDM18_OFDM6 0xe24 @@ -729,6 +764,7 @@ #define REG_LSSI_WRITE_B 0x0e90 #define REG_PREDISTB 0x0e90 #define REG_INIDLYB 0x0e94 +#define REG_TX_AGC_B 0x0e94 #define REG_RFE_PINMUX_B 0x0eb0 #define REG_RFE_INV_B 0x0eb4 #define REG_RFECTL_B 0x0eb8 @@ -744,8 +780,11 @@ #define REG_CRC_HT 0x0f10 #define REG_CRC_OFDM 0x0f14 #define REG_FA_OFDM 0x0f48 +#define REG_DBGRPT 0x0fa0 #define REG_CCA_CCK 0x0fcc +#define REG_SYS_CFG3_8814A 0x1000 + #define REG_ANAPARSW_MAC_0 0x1010 #define BIT_CF_L_V2 GENMASK(29, 28) @@ -863,9 +902,27 @@ #define LTECOEX_WRITE_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_WRITE_DATA_V1 #define LTECOEX_READ_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_READ_DATA_V1 +#define REG_RX_IQC_AB_C 0x1810 +#define REG_RX_IQC_CD_C 0x1814 +#define REG_TXSCALE_C 0x181c +#define REG_CK_MONHC 0x185c +#define REG_AFE_PWR1_C 0x1860 #define REG_IGN_GNT_BT1 0x1860 +#define REG_TX_AGC_C 0x1894 +#define REG_RFE_PINMUX_C 0x18b4 #define REG_RFESEL_CTRL 0x1990 +#define REG_AGC_TBL 0x1998 + +#define REG_RX_IQC_AB_D 0x1a10 +#define REG_RX_IQC_CD_D 0x1a14 +#define REG_TXSCALE_D 0x1a1c +#define REG_CK_MONHD 0x1a5c +#define REG_AFE_PWR1_D 0x1a60 +#define REG_TX_AGC_D 0x1a94 +#define REG_RFE_PINMUX_D 0x1ab4 +#define REG_RFE_INVSEL_D 0x1abc +#define BIT_RFE_SELSW0_D GENMASK(27, 20) #define REG_NOMASK_TXBT 0x1ca7 #define REG_ANAPAR 0x1c30 @@ -906,6 +963,7 @@ #define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8)) #define RF18_CHANNEL_MASK (MASKBYTE0) #define RF18_RFSI_MASK (BIT(18) | BIT(17)) +#define RF_RCK1_V1 0x1c #define RF_RCK 0x1d #define RF_MODE_TABLE_ADDR 0x30 #define RF_MODE_TABLE_DATA0 0x31 From f26c7dc4777959cfec48eb6783cd4f837ed074a0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 328/465] wifi: rtw88: Add rtw8814a_table.c (part 1/2) JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f4debfcb1b3c14a28ec157a41e0b98be72e554a0 Author: Bitterblue Smith Date: Fri Mar 7 02:23:22 2025 +0200 wifi: rtw88: Add rtw8814a_table.c (part 1/2) This contains various tables for initialising the RTL8814A, plus TX power limits. Split into two patches because they are big. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/df0b8ceb-2c2f-4bda-906f-a05c7b4d424c@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/realtek/rtw88/rtw8814a_table.c | 12551 ++++++++++++++++ 1 file changed, 12551 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814a_table.c diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c new file mode 100644 index 000000000000..907cbd032178 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c @@ -0,0 +1,12551 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include "main.h" +#include "phy.h" +#include "rtw8814a_table.h" + +static const u32 rtw8814a_mac[] = { + 0x010, 0x0000007C, + 0x014, 0x000000DB, + 0x016, 0x00000002, + 0x073, 0x00000010, + 0x420, 0x00000080, + 0x421, 0x0000000F, + 0x428, 0x0000000A, + 0x429, 0x00000010, + 0x430, 0x00000000, + 0x431, 0x00000000, + 0x432, 0x00000000, + 0x433, 0x00000001, + 0x434, 0x00000004, + 0x435, 0x00000005, + 0x436, 0x00000007, + 0x437, 0x00000008, + 0x43C, 0x00000004, + 0x43D, 0x00000005, + 0x43E, 0x00000007, + 0x43F, 0x00000008, + 0x440, 0x0000005D, + 0x441, 0x00000001, + 0x442, 0x00000000, + 0x444, 0x00000010, + 0x445, 0x000000F0, + 0x446, 0x00000001, + 0x447, 0x000000FE, + 0x448, 0x00000000, + 0x449, 0x00000000, + 0x44A, 0x00000000, + 0x44B, 0x00000040, + 0x44C, 0x00000010, + 0x44D, 0x000000F0, + 0x44E, 0x0000003F, + 0x44F, 0x00000000, + 0x450, 0x00000000, + 0x451, 0x00000000, + 0x452, 0x00000000, + 0x453, 0x00000040, + 0x45E, 0x00000004, + 0x49C, 0x00000010, + 0x49D, 0x000000F0, + 0x49E, 0x00000000, + 0x49F, 0x00000006, + 0x4A0, 0x000000E0, + 0x4A1, 0x00000003, + 0x4A2, 0x00000000, + 0x4A3, 0x00000040, + 0x4A4, 0x00000015, + 0x4A5, 0x000000F0, + 0x4A6, 0x00000000, + 0x4A7, 0x00000006, + 0x4A8, 0x000000E0, + 0x4A9, 0x00000000, + 0x4AA, 0x00000000, + 0x4AB, 0x00000000, + 0x7DA, 0x00000008, + 0x1448, 0x00000006, + 0x144A, 0x00000006, + 0x144C, 0x00000006, + 0x144E, 0x00000006, + 0x4C8, 0x000000FF, + 0x4C9, 0x00000008, + 0x4CA, 0x0000003C, + 0x4CB, 0x0000003C, + 0x4CC, 0x000000FF, + 0x4CD, 0x000000FF, + 0x4CE, 0x00000001, + 0x4CF, 0x00000008, + 0x500, 0x00000026, + 0x501, 0x000000A2, + 0x502, 0x0000002F, + 0x503, 0x00000000, + 0x504, 0x00000028, + 0x505, 0x000000A3, + 0x506, 0x0000005E, + 0x507, 0x00000000, + 0x508, 0x0000002B, + 0x509, 0x000000A4, + 0x50A, 0x0000005E, + 0x50B, 0x00000000, + 0x50C, 0x0000004F, + 0x50D, 0x000000A4, + 0x50E, 0x00000000, + 0x50F, 0x00000000, + 0x512, 0x0000001C, + 0x514, 0x0000000A, + 0x516, 0x0000000A, + 0x521, 0x0000002F, + 0x525, 0x00000047, + 0x550, 0x00000010, + 0x551, 0x00000010, + 0x559, 0x00000002, + 0x55C, 0x00000064, + 0x55D, 0x000000FF, + 0x577, 0x00000003, + 0x5BE, 0x00000064, + 0x604, 0x00000001, + 0x605, 0x00000030, + 0x607, 0x00000001, + 0x608, 0x0000000E, + 0x609, 0x0000002A, + 0x60A, 0x00000000, + 0x60C, 0x00000018, + 0x60D, 0x00000050, + 0x6A0, 0x000000FF, + 0x6A1, 0x000000FF, + 0x6A2, 0x000000FF, + 0x6A3, 0x000000FF, + 0x6A4, 0x000000FF, + 0x6A5, 0x000000FF, + 0x6DE, 0x00000084, + 0x620, 0x000000FF, + 0x621, 0x000000FF, + 0x622, 0x000000FF, + 0x623, 0x000000FF, + 0x624, 0x000000FF, + 0x625, 0x000000FF, + 0x626, 0x000000FF, + 0x627, 0x000000FF, + 0x638, 0x00000064, + 0x63C, 0x0000000A, + 0x63D, 0x0000000A, + 0x63E, 0x0000000E, + 0x63F, 0x0000000E, + 0x640, 0x00000040, + 0x642, 0x00000040, + 0x643, 0x00000000, + 0x652, 0x000000C8, + 0x66E, 0x00000005, + 0x700, 0x00000021, + 0x701, 0x00000043, + 0x702, 0x00000065, + 0x703, 0x00000087, + 0x708, 0x00000021, + 0x709, 0x00000043, + 0x70A, 0x00000065, + 0x70B, 0x00000087, + 0x718, 0x00000040, + 0x7D5, 0x000000BC, + 0x7D8, 0x00000028, + 0x7D9, 0x00000000, + 0x7DA, 0x0000000B, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_mac, rtw_phy_cfg_mac); + +static const u32 rtw8814a_agc[] = { + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000003, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xC43A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0xA1460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x435C0003, + 0x81C, 0x425E0003, + 0x81C, 0x41600003, + 0x81C, 0x27620003, + 0x81C, 0x26640003, + 0x81C, 0x25660003, + 0x81C, 0x24680003, + 0x81C, 0x236A0003, + 0x81C, 0x226C0003, + 0x81C, 0x216E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xEC240003, + 0x81C, 0xEB260003, + 0x81C, 0xEA280003, + 0x81C, 0xE92A0003, + 0x81C, 0xE82C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0xE4340003, + 0x81C, 0xE3360003, + 0x81C, 0xC6380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x49580003, + 0x81C, 0x485A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xEC240003, + 0x81C, 0xEB260003, + 0x81C, 0xEA280003, + 0x81C, 0xE92A0003, + 0x81C, 0xE82C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0xE4340003, + 0x81C, 0xE3360003, + 0x81C, 0xC6380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x49580003, + 0x81C, 0x485A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xDF000003, + 0x81C, 0xDF020003, + 0x81C, 0xDF040003, + 0x81C, 0xDE060003, + 0x81C, 0xDD080003, + 0x81C, 0xDC0A0003, + 0x81C, 0xDB0C0003, + 0x81C, 0xDA0E0003, + 0x81C, 0xD9100003, + 0x81C, 0xD8120003, + 0x81C, 0xD7140003, + 0x81C, 0xD6160003, + 0x81C, 0xD5180003, + 0x81C, 0xD41A0003, + 0x81C, 0xD31C0003, + 0x81C, 0xD21E0003, + 0x81C, 0xD1200003, + 0x81C, 0xD0220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xA73A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0x87460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x82500003, + 0x81C, 0x81520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x445C0003, + 0x81C, 0x435E0003, + 0x81C, 0x42600003, + 0x81C, 0x41620003, + 0x81C, 0x27640003, + 0x81C, 0x26660003, + 0x81C, 0x25680003, + 0x81C, 0x246A0003, + 0x81C, 0x236C0003, + 0x81C, 0x226E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xDF000003, + 0x81C, 0xDF020003, + 0x81C, 0xDF040003, + 0x81C, 0xDE060003, + 0x81C, 0xDD080003, + 0x81C, 0xDC0A0003, + 0x81C, 0xDB0C0003, + 0x81C, 0xDA0E0003, + 0x81C, 0xD9100003, + 0x81C, 0xD8120003, + 0x81C, 0xD7140003, + 0x81C, 0xD6160003, + 0x81C, 0xD5180003, + 0x81C, 0xD41A0003, + 0x81C, 0xD31C0003, + 0x81C, 0xD21E0003, + 0x81C, 0xD1200003, + 0x81C, 0xD0220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xA73A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0x87460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x82500003, + 0x81C, 0x81520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x445C0003, + 0x81C, 0x435E0003, + 0x81C, 0x42600003, + 0x81C, 0x41620003, + 0x81C, 0x27640003, + 0x81C, 0x26660003, + 0x81C, 0x25680003, + 0x81C, 0x246A0003, + 0x81C, 0x236C0003, + 0x81C, 0x226E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0x0F260003, + 0x81C, 0x0E280003, + 0x81C, 0x0D2A0003, + 0x81C, 0x0C2C0003, + 0x81C, 0x0B2E0003, + 0x81C, 0x0A300003, + 0x81C, 0x09320003, + 0x81C, 0x08340003, + 0x81C, 0x07360003, + 0x81C, 0x06380003, + 0x81C, 0x053A0003, + 0x81C, 0x043C0003, + 0x81C, 0x033E0003, + 0x81C, 0x23400003, + 0x81C, 0x22420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0x684E0003, + 0x81C, 0x67500003, + 0x81C, 0x66520003, + 0x81C, 0x65540003, + 0x81C, 0x64560003, + 0x81C, 0x63580003, + 0x81C, 0x625A0003, + 0x81C, 0x615C0003, + 0x81C, 0x475E0003, + 0x81C, 0x46600003, + 0x81C, 0x45620003, + 0x81C, 0x44640003, + 0x81C, 0x43660003, + 0x81C, 0x42680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0x08340003, + 0x81C, 0x07360003, + 0x81C, 0x06380003, + 0x81C, 0x053A0003, + 0x81C, 0x043C0003, + 0x81C, 0x033E0003, + 0x81C, 0x02400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0x684C0003, + 0x81C, 0x674E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0xA0000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xC43A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0xA1460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x435C0003, + 0x81C, 0x425E0003, + 0x81C, 0x41600003, + 0x81C, 0x27620003, + 0x81C, 0x26640003, + 0x81C, 0x25660003, + 0x81C, 0x24680003, + 0x81C, 0x236A0003, + 0x81C, 0x226C0003, + 0x81C, 0x216E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xC32E0103, + 0x81C, 0xC2300103, + 0x81C, 0xC1320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x444E0103, + 0x81C, 0x43500103, + 0x81C, 0x42520103, + 0x81C, 0x41540103, + 0x81C, 0x25560103, + 0x81C, 0x24580103, + 0x81C, 0x235A0103, + 0x81C, 0x065C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000103, + 0x81C, 0xF7020103, + 0x81C, 0xF6040103, + 0x81C, 0xF5060103, + 0x81C, 0xF4080103, + 0x81C, 0xF30A0103, + 0x81C, 0xF20C0103, + 0x81C, 0xF10E0103, + 0x81C, 0xF0100103, + 0x81C, 0xEF120103, + 0x81C, 0xEE140103, + 0x81C, 0xED160103, + 0x81C, 0xEC180103, + 0x81C, 0xEB1A0103, + 0x81C, 0xEA1C0103, + 0x81C, 0xE91E0103, + 0x81C, 0xE8200103, + 0x81C, 0xE7220103, + 0x81C, 0xE6240103, + 0x81C, 0xE5260103, + 0x81C, 0xE4280103, + 0x81C, 0xE32A0103, + 0x81C, 0xE22C0103, + 0x81C, 0xE12E0103, + 0x81C, 0xA5300103, + 0x81C, 0xA4320103, + 0x81C, 0xA3340103, + 0x81C, 0xA2360103, + 0x81C, 0xA1380103, + 0x81C, 0x843A0103, + 0x81C, 0x833C0103, + 0x81C, 0x823E0103, + 0x81C, 0x81400103, + 0x81C, 0x64420103, + 0x81C, 0x63440103, + 0x81C, 0x62460103, + 0x81C, 0x61480103, + 0x81C, 0x454A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x25520103, + 0x81C, 0x24540103, + 0x81C, 0x23560103, + 0x81C, 0x06580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000103, + 0x81C, 0xFB020103, + 0x81C, 0xFA040103, + 0x81C, 0xF9060103, + 0x81C, 0xF8080103, + 0x81C, 0xF70A0103, + 0x81C, 0xF60C0103, + 0x81C, 0xF50E0103, + 0x81C, 0xF4100103, + 0x81C, 0xF3120103, + 0x81C, 0xF2140103, + 0x81C, 0xF1160103, + 0x81C, 0xF0180103, + 0x81C, 0xEF1A0103, + 0x81C, 0xEE1C0103, + 0x81C, 0xED1E0103, + 0x81C, 0xEC200103, + 0x81C, 0xEB220103, + 0x81C, 0xEA240103, + 0x81C, 0xE9260103, + 0x81C, 0xE8280103, + 0x81C, 0xE72A0103, + 0x81C, 0xE62C0103, + 0x81C, 0xE52E0103, + 0x81C, 0xE4300103, + 0x81C, 0xE3320103, + 0x81C, 0xE2340103, + 0x81C, 0xE1360103, + 0x81C, 0x87380103, + 0x81C, 0x863A0103, + 0x81C, 0x853C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x464C0103, + 0x81C, 0x454E0103, + 0x81C, 0x44500103, + 0x81C, 0x43520103, + 0x81C, 0x26540103, + 0x81C, 0x25560103, + 0x81C, 0x24580103, + 0x81C, 0x075A0103, + 0x81C, 0x065C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA6300103, + 0x81C, 0xA5320103, + 0x81C, 0xA4340103, + 0x81C, 0xA3360103, + 0x81C, 0xA2380103, + 0x81C, 0xA13A0103, + 0x81C, 0x843C0103, + 0x81C, 0x833E0103, + 0x81C, 0x82400103, + 0x81C, 0x81420103, + 0x81C, 0x64440103, + 0x81C, 0x63460103, + 0x81C, 0x62480103, + 0x81C, 0x614A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x41520103, + 0x81C, 0x25540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x225A0103, + 0x81C, 0x055C0103, + 0x81C, 0x045E0103, + 0x81C, 0x03600103, + 0x81C, 0x02620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFD000103, + 0x81C, 0xFC020103, + 0x81C, 0xFB040103, + 0x81C, 0xFA060103, + 0x81C, 0xF9080103, + 0x81C, 0xF80A0103, + 0x81C, 0xF70C0103, + 0x81C, 0xF60E0103, + 0x81C, 0xF5100103, + 0x81C, 0xF4120103, + 0x81C, 0xF3140103, + 0x81C, 0xF2160103, + 0x81C, 0xF1180103, + 0x81C, 0xF01A0103, + 0x81C, 0xEF1C0103, + 0x81C, 0xEE1E0103, + 0x81C, 0xED200103, + 0x81C, 0xEC220103, + 0x81C, 0xEB240103, + 0x81C, 0xEA260103, + 0x81C, 0xE9280103, + 0x81C, 0xE82A0103, + 0x81C, 0xE72C0103, + 0x81C, 0xE62E0103, + 0x81C, 0xE5300103, + 0x81C, 0xE4320103, + 0x81C, 0xE3340103, + 0x81C, 0xE2360103, + 0x81C, 0xE1380103, + 0x81C, 0xA33A0103, + 0x81C, 0xA23C0103, + 0x81C, 0xA13E0103, + 0x81C, 0x84400103, + 0x81C, 0x83420103, + 0x81C, 0x82440103, + 0x81C, 0x81460103, + 0x81C, 0x64480103, + 0x81C, 0x634A0103, + 0x81C, 0x624C0103, + 0x81C, 0x614E0103, + 0x81C, 0x45500103, + 0x81C, 0x44520103, + 0x81C, 0x43540103, + 0x81C, 0x42560103, + 0x81C, 0x25580103, + 0x81C, 0x245A0103, + 0x81C, 0x235C0103, + 0x81C, 0x065E0103, + 0x81C, 0x05600103, + 0x81C, 0x04620103, + 0x81C, 0x03640103, + 0x81C, 0x02660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000103, + 0x81C, 0xF9020103, + 0x81C, 0xF8040103, + 0x81C, 0xF7060103, + 0x81C, 0xF6080103, + 0x81C, 0xF50A0103, + 0x81C, 0xF40C0103, + 0x81C, 0xF30E0103, + 0x81C, 0xF2100103, + 0x81C, 0xF1120103, + 0x81C, 0xF0140103, + 0x81C, 0xEF160103, + 0x81C, 0xEE180103, + 0x81C, 0xED1A0103, + 0x81C, 0xEC1C0103, + 0x81C, 0xEB1E0103, + 0x81C, 0xEA200103, + 0x81C, 0xE9220103, + 0x81C, 0xE8240103, + 0x81C, 0xE7260103, + 0x81C, 0xE6280103, + 0x81C, 0xE52A0103, + 0x81C, 0xE42C0103, + 0x81C, 0xE32E0103, + 0x81C, 0xE2300103, + 0x81C, 0xE1320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x454E0103, + 0x81C, 0x44500103, + 0x81C, 0x43520103, + 0x81C, 0x42540103, + 0x81C, 0x41560103, + 0x81C, 0x24580103, + 0x81C, 0x235A0103, + 0x81C, 0x225C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFF020103, + 0x81C, 0xFE040103, + 0x81C, 0xFD060103, + 0x81C, 0xFC080103, + 0x81C, 0xFB0A0103, + 0x81C, 0xFA0C0103, + 0x81C, 0xF90E0103, + 0x81C, 0xF8100103, + 0x81C, 0xF7120103, + 0x81C, 0xF6140103, + 0x81C, 0xF5160103, + 0x81C, 0xF4180103, + 0x81C, 0xF31A0103, + 0x81C, 0xF21C0103, + 0x81C, 0xF11E0103, + 0x81C, 0xF0200103, + 0x81C, 0xEF220103, + 0x81C, 0xEE240103, + 0x81C, 0xED260103, + 0x81C, 0xEC280103, + 0x81C, 0xEB2A0103, + 0x81C, 0xEA2C0103, + 0x81C, 0xE92E0103, + 0x81C, 0xE8300103, + 0x81C, 0xE7320103, + 0x81C, 0xE6340103, + 0x81C, 0xE5360103, + 0x81C, 0xE4380103, + 0x81C, 0xE33A0103, + 0x81C, 0xA53C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x85460103, + 0x81C, 0x84480103, + 0x81C, 0x834A0103, + 0x81C, 0x824C0103, + 0x81C, 0x814E0103, + 0x81C, 0x64500103, + 0x81C, 0x63520103, + 0x81C, 0x62540103, + 0x81C, 0x44560103, + 0x81C, 0x43580103, + 0x81C, 0x425A0103, + 0x81C, 0x265C0103, + 0x81C, 0x255E0103, + 0x81C, 0x24600103, + 0x81C, 0x07620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000103, + 0x81C, 0xF7020103, + 0x81C, 0xF6040103, + 0x81C, 0xF5060103, + 0x81C, 0xF4080103, + 0x81C, 0xF30A0103, + 0x81C, 0xF20C0103, + 0x81C, 0xF10E0103, + 0x81C, 0xF0100103, + 0x81C, 0xEF120103, + 0x81C, 0xEE140103, + 0x81C, 0xED160103, + 0x81C, 0xEC180103, + 0x81C, 0xEB1A0103, + 0x81C, 0xEA1C0103, + 0x81C, 0xE91E0103, + 0x81C, 0xE8200103, + 0x81C, 0xE7220103, + 0x81C, 0xE6240103, + 0x81C, 0xE5260103, + 0x81C, 0xE4280103, + 0x81C, 0xE32A0103, + 0x81C, 0xE22C0103, + 0x81C, 0xE12E0103, + 0x81C, 0xA4300103, + 0x81C, 0xA3320103, + 0x81C, 0xA2340103, + 0x81C, 0xA1360103, + 0x81C, 0x85380103, + 0x81C, 0x843A0103, + 0x81C, 0x833C0103, + 0x81C, 0x823E0103, + 0x81C, 0x65400103, + 0x81C, 0x64420103, + 0x81C, 0x63440103, + 0x81C, 0x62460103, + 0x81C, 0x45480103, + 0x81C, 0x444A0103, + 0x81C, 0x434C0103, + 0x81C, 0x264E0103, + 0x81C, 0x25500103, + 0x81C, 0x24520103, + 0x81C, 0x08540103, + 0x81C, 0x07560103, + 0x81C, 0x06580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFF020103, + 0x81C, 0xFE040103, + 0x81C, 0xFD060103, + 0x81C, 0xFC080103, + 0x81C, 0xFB0A0103, + 0x81C, 0xFA0C0103, + 0x81C, 0xF90E0103, + 0x81C, 0xF8100103, + 0x81C, 0xF7120103, + 0x81C, 0xF6140103, + 0x81C, 0xF5160103, + 0x81C, 0xF4180103, + 0x81C, 0xF31A0103, + 0x81C, 0xF21C0103, + 0x81C, 0xF11E0103, + 0x81C, 0xF0200103, + 0x81C, 0xEF220103, + 0x81C, 0xEE240103, + 0x81C, 0xED260103, + 0x81C, 0xEC280103, + 0x81C, 0xEB2A0103, + 0x81C, 0xEA2C0103, + 0x81C, 0xE92E0103, + 0x81C, 0xE8300103, + 0x81C, 0xE7320103, + 0x81C, 0xE6340103, + 0x81C, 0xE5360103, + 0x81C, 0xE4380103, + 0x81C, 0xE33A0103, + 0x81C, 0xA53C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x85460103, + 0x81C, 0x84480103, + 0x81C, 0x834A0103, + 0x81C, 0x824C0103, + 0x81C, 0x814E0103, + 0x81C, 0x64500103, + 0x81C, 0x63520103, + 0x81C, 0x62540103, + 0x81C, 0x44560103, + 0x81C, 0x43580103, + 0x81C, 0x425A0103, + 0x81C, 0x265C0103, + 0x81C, 0x255E0103, + 0x81C, 0x24600103, + 0x81C, 0x07620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA6300103, + 0x81C, 0xA5320103, + 0x81C, 0xA4340103, + 0x81C, 0xA3360103, + 0x81C, 0xA2380103, + 0x81C, 0xA13A0103, + 0x81C, 0x843C0103, + 0x81C, 0x833E0103, + 0x81C, 0x82400103, + 0x81C, 0x81420103, + 0x81C, 0x64440103, + 0x81C, 0x63460103, + 0x81C, 0x62480103, + 0x81C, 0x614A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x41520103, + 0x81C, 0x25540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x225A0103, + 0x81C, 0x055C0103, + 0x81C, 0x045E0103, + 0x81C, 0x03600103, + 0x81C, 0x02620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000103, + 0x81C, 0xF9020103, + 0x81C, 0xF8040103, + 0x81C, 0xF7060103, + 0x81C, 0xF6080103, + 0x81C, 0xF50A0103, + 0x81C, 0xF40C0103, + 0x81C, 0xF30E0103, + 0x81C, 0xF2100103, + 0x81C, 0xF1120103, + 0x81C, 0xF0140103, + 0x81C, 0xEF160103, + 0x81C, 0xEE180103, + 0x81C, 0xED1A0103, + 0x81C, 0xCC1C0103, + 0x81C, 0xCB1E0103, + 0x81C, 0xCA200103, + 0x81C, 0xE9220103, + 0x81C, 0xE8240103, + 0x81C, 0xE7260103, + 0x81C, 0xE6280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA7300103, + 0x81C, 0xA6320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x65440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x444E0103, + 0x81C, 0x43500103, + 0x81C, 0x42520103, + 0x81C, 0x41540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFE020103, + 0x81C, 0xFD040103, + 0x81C, 0xFC060103, + 0x81C, 0xFB080103, + 0x81C, 0xFA0A0103, + 0x81C, 0xF90C0103, + 0x81C, 0xF80E0103, + 0x81C, 0xF7100103, + 0x81C, 0xF6120103, + 0x81C, 0xF5140103, + 0x81C, 0xF4160103, + 0x81C, 0xF3180103, + 0x81C, 0xF21A0103, + 0x81C, 0xF11C0103, + 0x81C, 0xF01E0103, + 0x81C, 0xEF200103, + 0x81C, 0xEE220103, + 0x81C, 0xED240103, + 0x81C, 0xEC260103, + 0x81C, 0xEB280103, + 0x81C, 0xEA2A0103, + 0x81C, 0xE92C0103, + 0x81C, 0xE82E0103, + 0x81C, 0xE7300103, + 0x81C, 0xE6320103, + 0x81C, 0xE5340103, + 0x81C, 0xE4360103, + 0x81C, 0xE3380103, + 0x81C, 0xE23A0103, + 0x81C, 0xE13C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x86460103, + 0x81C, 0x85480103, + 0x81C, 0x844A0103, + 0x81C, 0x834C0103, + 0x81C, 0x824E0103, + 0x81C, 0x81500103, + 0x81C, 0x64520103, + 0x81C, 0x63540103, + 0x81C, 0x62560103, + 0x81C, 0x61580103, + 0x81C, 0x435A0103, + 0x81C, 0x425C0103, + 0x81C, 0x415E0103, + 0x81C, 0x25600103, + 0x81C, 0x24620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000203, + 0x81C, 0xF9020203, + 0x81C, 0xF8040203, + 0x81C, 0xF7060203, + 0x81C, 0xF6080203, + 0x81C, 0xF50A0203, + 0x81C, 0xF40C0203, + 0x81C, 0xF30E0203, + 0x81C, 0xF2100203, + 0x81C, 0xF1120203, + 0x81C, 0xF0140203, + 0x81C, 0xEF160203, + 0x81C, 0xEE180203, + 0x81C, 0xED1A0203, + 0x81C, 0xEC1C0203, + 0x81C, 0xEB1E0203, + 0x81C, 0xEA200203, + 0x81C, 0xE9220203, + 0x81C, 0xE8240203, + 0x81C, 0xE7260203, + 0x81C, 0xE6280203, + 0x81C, 0xE52A0203, + 0x81C, 0xE42C0203, + 0x81C, 0xE32E0203, + 0x81C, 0xE2300203, + 0x81C, 0xE1320203, + 0x81C, 0xA5340203, + 0x81C, 0xA4360203, + 0x81C, 0xA3380203, + 0x81C, 0xA23A0203, + 0x81C, 0xA13C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x464C0203, + 0x81C, 0x454E0203, + 0x81C, 0x44500203, + 0x81C, 0x43520203, + 0x81C, 0x42540203, + 0x81C, 0x41560203, + 0x81C, 0x24580203, + 0x81C, 0x235A0203, + 0x81C, 0x065C0203, + 0x81C, 0x055E0203, + 0x81C, 0x04600203, + 0x81C, 0x03620203, + 0x81C, 0x02640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0x853A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x25520203, + 0x81C, 0x24540203, + 0x81C, 0x23560203, + 0x81C, 0x06580203, + 0x81C, 0x055A0203, + 0x81C, 0x045C0203, + 0x81C, 0x035E0203, + 0x81C, 0x02600203, + 0x81C, 0x01620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000203, + 0x81C, 0xFB020203, + 0x81C, 0xFA040203, + 0x81C, 0xF9060203, + 0x81C, 0xF8080203, + 0x81C, 0xF70A0203, + 0x81C, 0xF60C0203, + 0x81C, 0xF50E0203, + 0x81C, 0xF4100203, + 0x81C, 0xF3120203, + 0x81C, 0xF2140203, + 0x81C, 0xF1160203, + 0x81C, 0xF0180203, + 0x81C, 0xEF1A0203, + 0x81C, 0xEE1C0203, + 0x81C, 0xED1E0203, + 0x81C, 0xEC200203, + 0x81C, 0xEB220203, + 0x81C, 0xEA240203, + 0x81C, 0xE9260203, + 0x81C, 0xE8280203, + 0x81C, 0xE72A0203, + 0x81C, 0xE62C0203, + 0x81C, 0xE52E0203, + 0x81C, 0xE4300203, + 0x81C, 0xE3320203, + 0x81C, 0xE2340203, + 0x81C, 0xE1360203, + 0x81C, 0x87380203, + 0x81C, 0x863A0203, + 0x81C, 0x853C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x474C0203, + 0x81C, 0x464E0203, + 0x81C, 0x45500203, + 0x81C, 0x44520203, + 0x81C, 0x43540203, + 0x81C, 0x42560203, + 0x81C, 0x24580203, + 0x81C, 0x235A0203, + 0x81C, 0x075C0203, + 0x81C, 0x065E0203, + 0x81C, 0x05600203, + 0x81C, 0x04620203, + 0x81C, 0x03640203, + 0x81C, 0x02660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x41520203, + 0x81C, 0x25540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x065A0203, + 0x81C, 0x055C0203, + 0x81C, 0x045E0203, + 0x81C, 0x03600203, + 0x81C, 0x02620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFB000203, + 0x81C, 0xFA020203, + 0x81C, 0xF9040203, + 0x81C, 0xF8060203, + 0x81C, 0xF7080203, + 0x81C, 0xF60A0203, + 0x81C, 0xF50C0203, + 0x81C, 0xF40E0203, + 0x81C, 0xF3100203, + 0x81C, 0xF2120203, + 0x81C, 0xF1140203, + 0x81C, 0xF0160203, + 0x81C, 0xEF180203, + 0x81C, 0xEE1A0203, + 0x81C, 0xED1C0203, + 0x81C, 0xEC1E0203, + 0x81C, 0xEB200203, + 0x81C, 0xEA220203, + 0x81C, 0xE9240203, + 0x81C, 0xE8260203, + 0x81C, 0xE7280203, + 0x81C, 0xE62A0203, + 0x81C, 0xE52C0203, + 0x81C, 0xE42E0203, + 0x81C, 0xE3300203, + 0x81C, 0xE2320203, + 0x81C, 0xE1340203, + 0x81C, 0xA5360203, + 0x81C, 0xA4380203, + 0x81C, 0xA33A0203, + 0x81C, 0xA23C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x614C0203, + 0x81C, 0x474E0203, + 0x81C, 0x46500203, + 0x81C, 0x45520203, + 0x81C, 0x44540203, + 0x81C, 0x43560203, + 0x81C, 0x25580203, + 0x81C, 0x245A0203, + 0x81C, 0x235C0203, + 0x81C, 0x075E0203, + 0x81C, 0x06600203, + 0x81C, 0x05620203, + 0x81C, 0x04640203, + 0x81C, 0x03660203, + 0x81C, 0x02680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000203, + 0x81C, 0xFB020203, + 0x81C, 0xFA040203, + 0x81C, 0xF9060203, + 0x81C, 0xF8080203, + 0x81C, 0xF70A0203, + 0x81C, 0xF60C0203, + 0x81C, 0xF50E0203, + 0x81C, 0xF4100203, + 0x81C, 0xF3120203, + 0x81C, 0xF2140203, + 0x81C, 0xF1160203, + 0x81C, 0xF0180203, + 0x81C, 0xEF1A0203, + 0x81C, 0xEE1C0203, + 0x81C, 0xED1E0203, + 0x81C, 0xEC200203, + 0x81C, 0xEB220203, + 0x81C, 0xEA240203, + 0x81C, 0xE9260203, + 0x81C, 0xE8280203, + 0x81C, 0xE72A0203, + 0x81C, 0xE62C0203, + 0x81C, 0xE52E0203, + 0x81C, 0xE4300203, + 0x81C, 0xE3320203, + 0x81C, 0xE2340203, + 0x81C, 0xE1360203, + 0x81C, 0xA5380203, + 0x81C, 0xA43A0203, + 0x81C, 0xA33C0203, + 0x81C, 0x853E0203, + 0x81C, 0x84400203, + 0x81C, 0x83420203, + 0x81C, 0x82440203, + 0x81C, 0x81460203, + 0x81C, 0x64480203, + 0x81C, 0x634A0203, + 0x81C, 0x624C0203, + 0x81C, 0x614E0203, + 0x81C, 0x46500203, + 0x81C, 0x45520203, + 0x81C, 0x44540203, + 0x81C, 0x43560203, + 0x81C, 0x25580203, + 0x81C, 0x245A0203, + 0x81C, 0x235C0203, + 0x81C, 0x075E0203, + 0x81C, 0x06600203, + 0x81C, 0x05620203, + 0x81C, 0x04640203, + 0x81C, 0x03660203, + 0x81C, 0x02680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x84480203, + 0x81C, 0x834A0203, + 0x81C, 0x824C0203, + 0x81C, 0x814E0203, + 0x81C, 0x64500203, + 0x81C, 0x63520203, + 0x81C, 0x62540203, + 0x81C, 0x61560203, + 0x81C, 0x45580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x24600203, + 0x81C, 0x23620203, + 0x81C, 0x07640203, + 0x81C, 0x06660203, + 0x81C, 0x05680203, + 0x81C, 0x046A0203, + 0x81C, 0x036C0203, + 0x81C, 0x026E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000203, + 0x81C, 0xF6020203, + 0x81C, 0xF5040203, + 0x81C, 0xF4060203, + 0x81C, 0xF3080203, + 0x81C, 0xF20A0203, + 0x81C, 0xF10C0203, + 0x81C, 0xF00E0203, + 0x81C, 0xEF100203, + 0x81C, 0xEE120203, + 0x81C, 0xED140203, + 0x81C, 0xEC160203, + 0x81C, 0xEB180203, + 0x81C, 0xEA1A0203, + 0x81C, 0xE91C0203, + 0x81C, 0xE81E0203, + 0x81C, 0xE7200203, + 0x81C, 0xE6220203, + 0x81C, 0xE5240203, + 0x81C, 0xE4260203, + 0x81C, 0xE3280203, + 0x81C, 0xE22A0203, + 0x81C, 0xA62C0203, + 0x81C, 0xA52E0203, + 0x81C, 0xA4300203, + 0x81C, 0xA3320203, + 0x81C, 0xA2340203, + 0x81C, 0xA1360203, + 0x81C, 0x86380203, + 0x81C, 0x853A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x65400203, + 0x81C, 0x64420203, + 0x81C, 0x63440203, + 0x81C, 0x46460203, + 0x81C, 0x45480203, + 0x81C, 0x444A0203, + 0x81C, 0x434C0203, + 0x81C, 0x264E0203, + 0x81C, 0x25500203, + 0x81C, 0x24520203, + 0x81C, 0x08540203, + 0x81C, 0x07560203, + 0x81C, 0x06580203, + 0x81C, 0x055A0203, + 0x81C, 0x045C0203, + 0x81C, 0x035E0203, + 0x81C, 0x02600203, + 0x81C, 0x01620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x84480203, + 0x81C, 0x834A0203, + 0x81C, 0x824C0203, + 0x81C, 0x814E0203, + 0x81C, 0x64500203, + 0x81C, 0x63520203, + 0x81C, 0x62540203, + 0x81C, 0x61560203, + 0x81C, 0x45580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x24600203, + 0x81C, 0x23620203, + 0x81C, 0x07640203, + 0x81C, 0x06660203, + 0x81C, 0x05680203, + 0x81C, 0x046A0203, + 0x81C, 0x036C0203, + 0x81C, 0x026E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x41520203, + 0x81C, 0x25540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x065A0203, + 0x81C, 0x055C0203, + 0x81C, 0x045E0203, + 0x81C, 0x03600203, + 0x81C, 0x02620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000203, + 0x81C, 0xF8020203, + 0x81C, 0xF7040203, + 0x81C, 0xF6060203, + 0x81C, 0xF5080203, + 0x81C, 0xF40A0203, + 0x81C, 0xF30C0203, + 0x81C, 0xF20E0203, + 0x81C, 0xF1100203, + 0x81C, 0xF0120203, + 0x81C, 0xEF140203, + 0x81C, 0xCE160203, + 0x81C, 0xCD180203, + 0x81C, 0xCC1A0203, + 0x81C, 0xCB1C0203, + 0x81C, 0xCA1E0203, + 0x81C, 0xC9200203, + 0x81C, 0xC8220203, + 0x81C, 0xC7240203, + 0x81C, 0xC6260203, + 0x81C, 0xC5280203, + 0x81C, 0xC42A0203, + 0x81C, 0xC32C0203, + 0x81C, 0xC22E0203, + 0x81C, 0xC1300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x853C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x614C0203, + 0x81C, 0x444E0203, + 0x81C, 0x43500203, + 0x81C, 0x42520203, + 0x81C, 0x41540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x075A0203, + 0x81C, 0x065C0203, + 0x81C, 0x055E0203, + 0x81C, 0x04600203, + 0x81C, 0x03620203, + 0x81C, 0x02640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x85480203, + 0x81C, 0x844A0203, + 0x81C, 0x834C0203, + 0x81C, 0x824E0203, + 0x81C, 0x81500203, + 0x81C, 0x64520203, + 0x81C, 0x63540203, + 0x81C, 0x62560203, + 0x81C, 0x61580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x25600203, + 0x81C, 0x24620203, + 0x81C, 0x06640203, + 0x81C, 0x05660203, + 0x81C, 0x04680203, + 0x81C, 0x036A0203, + 0x81C, 0x026C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000303, + 0x81C, 0xF7020303, + 0x81C, 0xF6040303, + 0x81C, 0xF5060303, + 0x81C, 0xF4080303, + 0x81C, 0xF30A0303, + 0x81C, 0xF20C0303, + 0x81C, 0xF10E0303, + 0x81C, 0xF0100303, + 0x81C, 0xEF120303, + 0x81C, 0xEE140303, + 0x81C, 0xED160303, + 0x81C, 0xEC180303, + 0x81C, 0xEB1A0303, + 0x81C, 0xEA1C0303, + 0x81C, 0xE91E0303, + 0x81C, 0xE8200303, + 0x81C, 0xE7220303, + 0x81C, 0xE6240303, + 0x81C, 0xE5260303, + 0x81C, 0xE4280303, + 0x81C, 0xE32A0303, + 0x81C, 0xE22C0303, + 0x81C, 0xE12E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xC32A0303, + 0x81C, 0xC22C0303, + 0x81C, 0xC12E0303, + 0x81C, 0xA5300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x25520303, + 0x81C, 0x24540303, + 0x81C, 0x23560303, + 0x81C, 0x06580303, + 0x81C, 0x055A0303, + 0x81C, 0x045C0303, + 0x81C, 0x035E0303, + 0x81C, 0x02600303, + 0x81C, 0x01620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000303, + 0x81C, 0xF8020303, + 0x81C, 0xF7040303, + 0x81C, 0xF6060303, + 0x81C, 0xF5080303, + 0x81C, 0xF40A0303, + 0x81C, 0xF30C0303, + 0x81C, 0xF20E0303, + 0x81C, 0xF1100303, + 0x81C, 0xF0120303, + 0x81C, 0xEF140303, + 0x81C, 0xEE160303, + 0x81C, 0xED180303, + 0x81C, 0xEC1A0303, + 0x81C, 0xEB1C0303, + 0x81C, 0xEA1E0303, + 0x81C, 0xE9200303, + 0x81C, 0xE8220303, + 0x81C, 0xE7240303, + 0x81C, 0xE6260303, + 0x81C, 0xE5280303, + 0x81C, 0xE42A0303, + 0x81C, 0xE32C0303, + 0x81C, 0xE22E0303, + 0x81C, 0xE1300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x444C0303, + 0x81C, 0x434E0303, + 0x81C, 0x42500303, + 0x81C, 0x25520303, + 0x81C, 0x24540303, + 0x81C, 0x23560303, + 0x81C, 0x07580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xE12C0303, + 0x81C, 0xA72E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFB000303, + 0x81C, 0xFA020303, + 0x81C, 0xF9040303, + 0x81C, 0xF8060303, + 0x81C, 0xF7080303, + 0x81C, 0xF60A0303, + 0x81C, 0xF50C0303, + 0x81C, 0xF40E0303, + 0x81C, 0xF3100303, + 0x81C, 0xF2120303, + 0x81C, 0xF1140303, + 0x81C, 0xF0160303, + 0x81C, 0xEF180303, + 0x81C, 0xEE1A0303, + 0x81C, 0xED1C0303, + 0x81C, 0xEC1E0303, + 0x81C, 0xEB200303, + 0x81C, 0xEA220303, + 0x81C, 0xE9240303, + 0x81C, 0xE8260303, + 0x81C, 0xE7280303, + 0x81C, 0xE62A0303, + 0x81C, 0xE52C0303, + 0x81C, 0xE42E0303, + 0x81C, 0xE3300303, + 0x81C, 0xE2320303, + 0x81C, 0xE1340303, + 0x81C, 0xC2360303, + 0x81C, 0xC1380303, + 0x81C, 0xA33A0303, + 0x81C, 0xA23C0303, + 0x81C, 0x853E0303, + 0x81C, 0x84400303, + 0x81C, 0x83420303, + 0x81C, 0x66440303, + 0x81C, 0x65460303, + 0x81C, 0x64480303, + 0x81C, 0x634A0303, + 0x81C, 0x624C0303, + 0x81C, 0x614E0303, + 0x81C, 0x45500303, + 0x81C, 0x44520303, + 0x81C, 0x43540303, + 0x81C, 0x42560303, + 0x81C, 0x25580303, + 0x81C, 0x245A0303, + 0x81C, 0x235C0303, + 0x81C, 0x065E0303, + 0x81C, 0x05600303, + 0x81C, 0x04620303, + 0x81C, 0x03640303, + 0x81C, 0x02660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000303, + 0x81C, 0xF8020303, + 0x81C, 0xF7040303, + 0x81C, 0xF6060303, + 0x81C, 0xF5080303, + 0x81C, 0xF40A0303, + 0x81C, 0xF30C0303, + 0x81C, 0xF20E0303, + 0x81C, 0xF1100303, + 0x81C, 0xF0120303, + 0x81C, 0xEF140303, + 0x81C, 0xEE160303, + 0x81C, 0xED180303, + 0x81C, 0xEC1A0303, + 0x81C, 0xEB1C0303, + 0x81C, 0xEA1E0303, + 0x81C, 0xE9200303, + 0x81C, 0xE8220303, + 0x81C, 0xE7240303, + 0x81C, 0xE6260303, + 0x81C, 0xE5280303, + 0x81C, 0xE42A0303, + 0x81C, 0xE32C0303, + 0x81C, 0xE22E0303, + 0x81C, 0xE1300303, + 0x81C, 0xA6320303, + 0x81C, 0xA5340303, + 0x81C, 0xA4360303, + 0x81C, 0xA3380303, + 0x81C, 0xA23A0303, + 0x81C, 0xA13C0303, + 0x81C, 0x853E0303, + 0x81C, 0x84400303, + 0x81C, 0x83420303, + 0x81C, 0x82440303, + 0x81C, 0x81460303, + 0x81C, 0x64480303, + 0x81C, 0x634A0303, + 0x81C, 0x624C0303, + 0x81C, 0x614E0303, + 0x81C, 0x44500303, + 0x81C, 0x43520303, + 0x81C, 0x42540303, + 0x81C, 0x41560303, + 0x81C, 0x25580303, + 0x81C, 0x245A0303, + 0x81C, 0x235C0303, + 0x81C, 0x055E0303, + 0x81C, 0x04600303, + 0x81C, 0x03620303, + 0x81C, 0x02640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000303, + 0x81C, 0xFD020303, + 0x81C, 0xFC040303, + 0x81C, 0xFB060303, + 0x81C, 0xFA080303, + 0x81C, 0xF90A0303, + 0x81C, 0xF80C0303, + 0x81C, 0xF70E0303, + 0x81C, 0xF6100303, + 0x81C, 0xF5120303, + 0x81C, 0xF4140303, + 0x81C, 0xF3160303, + 0x81C, 0xF2180303, + 0x81C, 0xF11A0303, + 0x81C, 0xF01C0303, + 0x81C, 0xEF1E0303, + 0x81C, 0xEE200303, + 0x81C, 0xED220303, + 0x81C, 0xEC240303, + 0x81C, 0xEB260303, + 0x81C, 0xEA280303, + 0x81C, 0xE92A0303, + 0x81C, 0xE82C0303, + 0x81C, 0xE72E0303, + 0x81C, 0xE6300303, + 0x81C, 0xE5320303, + 0x81C, 0xE4340303, + 0x81C, 0xE3360303, + 0x81C, 0xC3380303, + 0x81C, 0xC23A0303, + 0x81C, 0xC13C0303, + 0x81C, 0xA43E0303, + 0x81C, 0xA3400303, + 0x81C, 0xA2420303, + 0x81C, 0xA1440303, + 0x81C, 0x85460303, + 0x81C, 0x84480303, + 0x81C, 0x834A0303, + 0x81C, 0x824C0303, + 0x81C, 0x814E0303, + 0x81C, 0x64500303, + 0x81C, 0x63520303, + 0x81C, 0x62540303, + 0x81C, 0x61560303, + 0x81C, 0x44580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x265E0303, + 0x81C, 0x25600303, + 0x81C, 0x24620303, + 0x81C, 0x06640303, + 0x81C, 0x05660303, + 0x81C, 0x04680303, + 0x81C, 0x036A0303, + 0x81C, 0x026C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xA62C0303, + 0x81C, 0xA52E0303, + 0x81C, 0xA4300303, + 0x81C, 0xA3320303, + 0x81C, 0xA2340303, + 0x81C, 0x87360303, + 0x81C, 0x86380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x66400303, + 0x81C, 0x65420303, + 0x81C, 0x64440303, + 0x81C, 0x45460303, + 0x81C, 0x44480303, + 0x81C, 0x434A0303, + 0x81C, 0x274C0303, + 0x81C, 0x264E0303, + 0x81C, 0x25500303, + 0x81C, 0x24520303, + 0x81C, 0x23540303, + 0x81C, 0x08560303, + 0x81C, 0x07580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000303, + 0x81C, 0xFD020303, + 0x81C, 0xFC040303, + 0x81C, 0xFB060303, + 0x81C, 0xFA080303, + 0x81C, 0xF90A0303, + 0x81C, 0xF80C0303, + 0x81C, 0xF70E0303, + 0x81C, 0xF6100303, + 0x81C, 0xF5120303, + 0x81C, 0xF4140303, + 0x81C, 0xF3160303, + 0x81C, 0xF2180303, + 0x81C, 0xF11A0303, + 0x81C, 0xF01C0303, + 0x81C, 0xEF1E0303, + 0x81C, 0xEE200303, + 0x81C, 0xED220303, + 0x81C, 0xEC240303, + 0x81C, 0xEB260303, + 0x81C, 0xEA280303, + 0x81C, 0xE92A0303, + 0x81C, 0xE82C0303, + 0x81C, 0xE72E0303, + 0x81C, 0xE6300303, + 0x81C, 0xE5320303, + 0x81C, 0xE4340303, + 0x81C, 0xE3360303, + 0x81C, 0xC3380303, + 0x81C, 0xC23A0303, + 0x81C, 0xC13C0303, + 0x81C, 0xA43E0303, + 0x81C, 0xA3400303, + 0x81C, 0xA2420303, + 0x81C, 0xA1440303, + 0x81C, 0x85460303, + 0x81C, 0x84480303, + 0x81C, 0x834A0303, + 0x81C, 0x824C0303, + 0x81C, 0x814E0303, + 0x81C, 0x64500303, + 0x81C, 0x63520303, + 0x81C, 0x62540303, + 0x81C, 0x61560303, + 0x81C, 0x44580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x265E0303, + 0x81C, 0x25600303, + 0x81C, 0x24620303, + 0x81C, 0x06640303, + 0x81C, 0x05660303, + 0x81C, 0x04680303, + 0x81C, 0x036A0303, + 0x81C, 0x026C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xE12C0303, + 0x81C, 0xA72E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xAF1C0303, + 0x81C, 0xAE1E0303, + 0x81C, 0xAD200303, + 0x81C, 0xAC220303, + 0x81C, 0xAB240303, + 0x81C, 0xAA260303, + 0x81C, 0xC5280303, + 0x81C, 0xC42A0303, + 0x81C, 0xC32C0303, + 0x81C, 0xC22E0303, + 0x81C, 0xA5300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x444C0303, + 0x81C, 0x434E0303, + 0x81C, 0x42500303, + 0x81C, 0x41520303, + 0x81C, 0x25540303, + 0x81C, 0x24560303, + 0x81C, 0x06580303, + 0x81C, 0x055A0303, + 0x81C, 0x045C0303, + 0x81C, 0x035E0303, + 0x81C, 0x02600303, + 0x81C, 0x01620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0xA0000000, 0x00000000, + 0x81C, 0xFD000303, + 0x81C, 0xFC020303, + 0x81C, 0xFB040303, + 0x81C, 0xFA060303, + 0x81C, 0xF9080303, + 0x81C, 0xF80A0303, + 0x81C, 0xF70C0303, + 0x81C, 0xF60E0303, + 0x81C, 0xF5100303, + 0x81C, 0xF4120303, + 0x81C, 0xF3140303, + 0x81C, 0xF2160303, + 0x81C, 0xF1180303, + 0x81C, 0xF01A0303, + 0x81C, 0xEF1C0303, + 0x81C, 0xEE1E0303, + 0x81C, 0xED200303, + 0x81C, 0xEC220303, + 0x81C, 0xEB240303, + 0x81C, 0xEA260303, + 0x81C, 0xE9280303, + 0x81C, 0xE82A0303, + 0x81C, 0xE72C0303, + 0x81C, 0xE62E0303, + 0x81C, 0xE5300303, + 0x81C, 0xE4320303, + 0x81C, 0xE3340303, + 0x81C, 0xE2360303, + 0x81C, 0xE1380303, + 0x81C, 0xA53A0303, + 0x81C, 0xA43C0303, + 0x81C, 0xA33E0303, + 0x81C, 0xA2400303, + 0x81C, 0xA1420303, + 0x81C, 0x87440303, + 0x81C, 0x86460303, + 0x81C, 0x85480303, + 0x81C, 0x844A0303, + 0x81C, 0x834C0303, + 0x81C, 0x824E0303, + 0x81C, 0x81500303, + 0x81C, 0x64520303, + 0x81C, 0x63540303, + 0x81C, 0x62560303, + 0x81C, 0x61580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x415E0303, + 0x81C, 0x07600303, + 0x81C, 0x06620303, + 0x81C, 0x05640303, + 0x81C, 0x04660303, + 0x81C, 0x03680303, + 0x81C, 0x026A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0xB0000000, 0x00000000, + 0xC50, 0x00000022, + 0xC50, 0x00000020, + 0xE50, 0x00000022, + 0xE50, 0x00000020, + 0x1850, 0x00000022, + 0x1850, 0x00000020, + 0x1A50, 0x00000022, + 0x1A50, 0x00000020, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_agc, rtw_phy_cfg_agc); + +static const u32 rtw8814a_bb[] = { + 0x800, 0x9020D010, + 0x804, 0x08011280, + 0x808, 0x0E0282FF, + 0x80C, 0x1000002F, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x810, 0x33303265, + 0xA0000000, 0x00000000, + 0x810, 0x33303265, + 0xB0000000, 0x00000000, + 0x814, 0x020C3D10, + 0x818, 0x04A10385, + 0x820, 0x00000000, + 0x824, 0x00033E40, + 0x828, 0x00000000, + 0x82C, 0x73985170, + 0x830, 0x79A0EA08, + 0x834, 0x042E708A, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667640, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0xA0000000, 0x00000000, + 0x838, 0x86667640, + 0xB0000000, 0x00000000, + 0x83C, 0x9798B9B9, + 0x840, 0x17578F60, + 0x844, 0x4BBDFCDE, + 0x848, 0x5CD07F8B, + 0x84C, 0x6CFBF7B5, + 0x850, 0x28834706, + 0x854, 0x0001520C, + 0x858, 0x4060C000, + 0x85C, 0x74210368, + 0x860, 0x6929C321, + 0x864, 0x79727432, + 0x868, 0x8CA7A314, + 0x86C, 0x438C2878, + 0x870, 0x44444444, + 0x874, 0x21612C2E, + 0x878, 0x00003152, + 0x87C, 0x000FC000, + 0x8A0, 0x00000013, + 0x8A4, 0x7F7F7F7F, + 0x8A8, 0xA202033E, + 0x8AC, 0xF40F550A, + 0x8B0, 0x00000600, + 0x8B4, 0x000FC080, + 0x8B8, 0xEC0057FF, + 0x8BC, 0x8CA520C3, + 0x8C0, 0x3FF00020, + 0x8C4, 0x44C00000, + 0x80000009, 0x00000000, 0x40000000, 0x00000000, + 0x8C8, 0x80025969, + 0xA0000000, 0x00000000, + 0x8C8, 0x80025167, + 0xB0000000, 0x00000000, + 0x8CC, 0x08250492, + 0x8D0, 0x0000B800, + 0x8D4, 0x940008A0, + 0x8D8, 0x290B5612, + 0x8DC, 0x00000000, + 0x8E0, 0x32316407, + 0x8E4, 0x4A092925, + 0x8E8, 0xFFFFC42C, + 0x8EC, 0x99999999, + 0x8F0, 0x00009999, + 0x8F4, 0x00F80FA1, + 0x8F8, 0x400082C0, + 0x8FC, 0x00000000, + 0x900, 0x00400700, + 0x90C, 0x09004000, + 0x910, 0x0000FC00, + 0x914, 0xD6400404, + 0x918, 0x1C1028C0, + 0x91C, 0x64B11A1C, + 0x920, 0xE0767233, + 0x924, 0x055AA500, + 0x928, 0x4AB0E4E4, + 0x92C, 0xFFFE0000, + 0x930, 0xFFFFFFFE, + 0x934, 0x001FFFFF, + 0x938, 0x00008400, + 0x93C, 0x932C0642, + 0x940, 0x093E9360, + 0x944, 0x08000000, + 0x948, 0x04000000, + 0x950, 0x02010080, + 0x954, 0x86510080, + 0x960, 0x00000000, + 0x964, 0x00000000, + 0x968, 0x00000000, + 0x96C, 0x00000000, + 0x970, 0x801FFFFF, + 0x978, 0x00000000, + 0x97C, 0x00000000, + 0x980, 0x00000000, + 0x984, 0x00000000, + 0x988, 0x00000000, + 0x98C, 0x03440000, + 0x990, 0x27100000, + 0x994, 0xFFFF0100, + 0x998, 0xFFFFFF5C, + 0x99C, 0xFFFFFFFF, + 0x9A0, 0x000000FF, + 0x9A4, 0x00080080, + 0x9A8, 0x0C2F0000, + 0x9AC, 0x00560000, + 0x9B0, 0x81081008, + 0x9B4, 0x00000000, + 0x9B8, 0x01081008, + 0x9BC, 0x01081008, + 0x9D0, 0x00000000, + 0x9D4, 0x00000000, + 0x9D8, 0x00000000, + 0x9DC, 0x00000000, + 0x9E4, 0x00000002, + 0x9E8, 0x000022D5, + 0x9FC, 0xEFFFF7FF, + 0xB00, 0xE3100000, + 0xB04, 0x0000B000, + 0xB0C, 0x31EAA006, + 0xB5C, 0x41CFFFFF, + 0xC00, 0x00000007, + 0xC04, 0x00042020, + 0xC08, 0x80410231, + 0xC0C, 0x00000000, + 0xC10, 0x00000100, + 0xC14, 0x01000000, + 0xC1C, 0x40000053, + 0xC50, 0x00000020, + 0xC54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0xC58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0xC5C, 0x0D000058, + 0xC60, 0x1B800000, + 0xC60, 0x0B800001, + 0xC60, 0x05800002, + 0xC60, 0x07800003, + 0xC60, 0x1A800004, + 0xC60, 0x0B800005, + 0xC60, 0x05800006, + 0xC60, 0x0E800007, + 0xC60, 0x1A800008, + 0xC60, 0x0B800009, + 0xC60, 0x1580000A, + 0xC60, 0x0880000B, + 0xC60, 0x1A80000C, + 0xC60, 0x0B80000D, + 0xC60, 0x0580000E, + 0xC60, 0x0E80000F, + 0xC60, 0x1A800010, + 0xC60, 0x0B800011, + 0xC60, 0x15800012, + 0xC60, 0x08800013, + 0xC60, 0x1A800014, + 0xC60, 0x0B800015, + 0xC60, 0x05800016, + 0xC60, 0x07800017, + 0xC60, 0x1A800018, + 0xC60, 0x0B800019, + 0xC60, 0x1580001A, + 0xC60, 0x0880001B, + 0xC60, 0x1B80001C, + 0xC60, 0x0B80001D, + 0xC60, 0x0580001E, + 0xC60, 0x0780001F, + 0xC60, 0x1B800020, + 0xC60, 0x0B800021, + 0xC60, 0x05800022, + 0xC60, 0x07800023, + 0xC60, 0x1B800024, + 0xC60, 0x0B800025, + 0xC60, 0x05800026, + 0xC60, 0x07800027, + 0xC60, 0x1B800028, + 0xC60, 0x0B800029, + 0xC60, 0x0580002A, + 0xC60, 0x0780002B, + 0xC60, 0x1B800030, + 0xC60, 0x0B800031, + 0xC60, 0x05800032, + 0xC60, 0x00800033, + 0xC60, 0x1B800034, + 0xC60, 0x0B800035, + 0xC60, 0x05800036, + 0xC60, 0x00800037, + 0xC60, 0x1B800038, + 0xC60, 0x0B800039, + 0xC60, 0x0580003A, + 0xC60, 0x0E80803B, + 0xC94, 0x01000401, + 0xC98, 0x00188000, + 0xCA0, 0x00002929, + 0xCA4, 0x08040201, + 0xCA8, 0x80402010, + 0xCAC, 0x77777000, + 0xCB0, 0x54775477, + 0xCB4, 0x54775477, + 0xCB8, 0x00500000, + 0xCBC, 0x77700000, + 0xCC0, 0x00000010, + 0xCC8, 0x00000010, + 0xE00, 0x00000007, + 0xE04, 0x00042020, + 0xE08, 0x80410231, + 0xE0C, 0x00000000, + 0xE10, 0x00000100, + 0xE14, 0x01000000, + 0xE1C, 0x40000053, + 0xE50, 0x00000020, + 0xE54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0xE58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0xE5C, 0x0D000058, + 0xE60, 0x1B800000, + 0xE60, 0x0B800001, + 0xE60, 0x05800002, + 0xE60, 0x07800003, + 0xE60, 0x1A800004, + 0xE60, 0x0B800005, + 0xE60, 0x05800006, + 0xE60, 0x0E800007, + 0xE60, 0x1A800008, + 0xE60, 0x0B800009, + 0xE60, 0x1580000A, + 0xE60, 0x0880000B, + 0xE60, 0x1A80000C, + 0xE60, 0x0B80000D, + 0xE60, 0x0580000E, + 0xE60, 0x0E80000F, + 0xE60, 0x1A800010, + 0xE60, 0x0B800011, + 0xE60, 0x15800012, + 0xE60, 0x08800013, + 0xE60, 0x1A800014, + 0xE60, 0x0B800015, + 0xE60, 0x05800016, + 0xE60, 0x07800017, + 0xE60, 0x1A800018, + 0xE60, 0x0B800019, + 0xE60, 0x1580001A, + 0xE60, 0x0880001B, + 0xE60, 0x1B80001C, + 0xE60, 0x0B80001D, + 0xE60, 0x0580001E, + 0xE60, 0x0780001F, + 0xE60, 0x1B800020, + 0xE60, 0x0B800021, + 0xE60, 0x05800022, + 0xE60, 0x07800023, + 0xE60, 0x1B800024, + 0xE60, 0x0B800025, + 0xE60, 0x05800026, + 0xE60, 0x07800027, + 0xE60, 0x1B800028, + 0xE60, 0x0B800029, + 0xE60, 0x0580002A, + 0xE60, 0x0780002B, + 0xE60, 0x1B800030, + 0xE60, 0x0B800031, + 0xE60, 0x05800032, + 0xE60, 0x00800033, + 0xE60, 0x1B800034, + 0xE60, 0x0B800035, + 0xE60, 0x05800036, + 0xE60, 0x00800037, + 0xE60, 0x1B800038, + 0xE60, 0x0B800039, + 0xE60, 0x0580003A, + 0xE60, 0x0E80803B, + 0xE94, 0x01000401, + 0xE98, 0x00188000, + 0xEA0, 0x00002929, + 0xEA4, 0x08040201, + 0xEA8, 0x80402010, + 0xEAC, 0x77777000, + 0xEB0, 0x54775477, + 0xEB4, 0x54775477, + 0xEB8, 0x00500000, + 0xEBC, 0x77700000, + 0x1800, 0x00000007, + 0x1804, 0x00042020, + 0x1808, 0x80410231, + 0x180C, 0x00000000, + 0x1810, 0x00000100, + 0x1814, 0x01000000, + 0x181C, 0x40000053, + 0x1850, 0x00000020, + 0x1854, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0x1858, 0x3C020C14, + 0xB0000000, 0x00000000, + 0x185C, 0x0D000058, + 0x1860, 0x1B800000, + 0x1860, 0x0B800001, + 0x1860, 0x05800002, + 0x1860, 0x07800003, + 0x1860, 0x1A800004, + 0x1860, 0x0B800005, + 0x1860, 0x05800006, + 0x1860, 0x0E800007, + 0x1860, 0x1A800008, + 0x1860, 0x0B800009, + 0x1860, 0x1580000A, + 0x1860, 0x0880000B, + 0x1860, 0x1A80000C, + 0x1860, 0x0B80000D, + 0x1860, 0x0580000E, + 0x1860, 0x0E80000F, + 0x1860, 0x1A800010, + 0x1860, 0x0B800011, + 0x1860, 0x15800012, + 0x1860, 0x08800013, + 0x1860, 0x1A800014, + 0x1860, 0x0B800015, + 0x1860, 0x05800016, + 0x1860, 0x07800017, + 0x1860, 0x1A800018, + 0x1860, 0x0B800019, + 0x1860, 0x1580001A, + 0x1860, 0x0880001B, + 0x1860, 0x1B80001C, + 0x1860, 0x0B80001D, + 0x1860, 0x0580001E, + 0x1860, 0x0780001F, + 0x1860, 0x1B800020, + 0x1860, 0x0B800021, + 0x1860, 0x05800022, + 0x1860, 0x07800023, + 0x1860, 0x1B800024, + 0x1860, 0x0B800025, + 0x1860, 0x05800026, + 0x1860, 0x07800027, + 0x1860, 0x1B800028, + 0x1860, 0x0B800029, + 0x1860, 0x0580002A, + 0x1860, 0x0780002B, + 0x1860, 0x1B800030, + 0x1860, 0x0B800031, + 0x1860, 0x05800032, + 0x1860, 0x00800033, + 0x1860, 0x1B800034, + 0x1860, 0x0B800035, + 0x1860, 0x05800036, + 0x1860, 0x00800037, + 0x1860, 0x1B800038, + 0x1860, 0x0B800039, + 0x1860, 0x0580003A, + 0x1860, 0x0E80803B, + 0x1894, 0x01000401, + 0x1898, 0x00188000, + 0x18A0, 0x00002929, + 0x18A4, 0x08040201, + 0x18A8, 0x80402010, + 0x18AC, 0x77777000, + 0x18B0, 0x54775477, + 0x18B4, 0x54775477, + 0x18B8, 0x00500000, + 0x18BC, 0x77700000, + 0x1A00, 0x00000007, + 0x1A04, 0x00042020, + 0x1A08, 0x80410231, + 0x1A0C, 0x00000000, + 0x1A10, 0x00000100, + 0x1A14, 0x01000000, + 0x1A1C, 0x40000053, + 0x1A50, 0x00000020, + 0x1A54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0x1A58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0x1A5C, 0x0D000058, + 0x1A60, 0x1B800000, + 0x1A60, 0x0B800001, + 0x1A60, 0x05800002, + 0x1A60, 0x07800003, + 0x1A60, 0x1A800004, + 0x1A60, 0x0B800005, + 0x1A60, 0x05800006, + 0x1A60, 0x0E800007, + 0x1A60, 0x1A800008, + 0x1A60, 0x0B800009, + 0x1A60, 0x1580000A, + 0x1A60, 0x0880000B, + 0x1A60, 0x1A80000C, + 0x1A60, 0x0B80000D, + 0x1A60, 0x0580000E, + 0x1A60, 0x0E80000F, + 0x1A60, 0x1A800010, + 0x1A60, 0x0B800011, + 0x1A60, 0x15800012, + 0x1A60, 0x08800013, + 0x1A60, 0x1A800014, + 0x1A60, 0x0B800015, + 0x1A60, 0x05800016, + 0x1A60, 0x07800017, + 0x1A60, 0x1A800018, + 0x1A60, 0x0B800019, + 0x1A60, 0x1580001A, + 0x1A60, 0x0880001B, + 0x1A60, 0x1B80001C, + 0x1A60, 0x0B80001D, + 0x1A60, 0x0580001E, + 0x1A60, 0x0780001F, + 0x1A60, 0x1B800020, + 0x1A60, 0x0B800021, + 0x1A60, 0x05800022, + 0x1A60, 0x07800023, + 0x1A60, 0x1B800024, + 0x1A60, 0x0B800025, + 0x1A60, 0x05800026, + 0x1A60, 0x07800027, + 0x1A60, 0x1B800028, + 0x1A60, 0x0B800029, + 0x1A60, 0x0580002A, + 0x1A60, 0x0780002B, + 0x1A60, 0x1B800030, + 0x1A60, 0x0B800031, + 0x1A60, 0x05800032, + 0x1A60, 0x00800033, + 0x1A60, 0x1B800034, + 0x1A60, 0x0B800035, + 0x1A60, 0x05800036, + 0x1A60, 0x00800037, + 0x1A60, 0x1B800038, + 0x1A60, 0x0B800039, + 0x1A60, 0x0580003A, + 0x1A60, 0x0E80803B, + 0x1A94, 0x01000401, + 0x1A98, 0x00188000, + 0x1AA0, 0x00002929, + 0x1AA4, 0x08040201, + 0x1AA8, 0x80402010, + 0x1AAC, 0x77777000, + 0x1AB0, 0x54775477, + 0x1AB4, 0x54775477, + 0x1AB8, 0x00500000, + 0x1ABC, 0x77700000, + 0x1904, 0x00030000, + 0x1914, 0x00030000, + 0x1984, 0x03000000, + 0x1988, 0x00000087, + 0x198C, 0x00000007, + 0x1990, 0xFFAA5500, + 0x1994, 0x00000077, + 0x1998, 0x12801000, + 0x1998, 0x12801000, + 0x1998, 0x12801001, + 0x1998, 0x12801002, + 0x1998, 0x12801003, + 0x1998, 0x12801004, + 0x1998, 0x12801005, + 0x1998, 0x12801006, + 0x1998, 0x12801007, + 0x1998, 0x12801008, + 0x1998, 0x12801009, + 0x1998, 0x1280100A, + 0x1998, 0x1280100B, + 0x1998, 0x1280100C, + 0x1998, 0x1280100D, + 0x1998, 0x1280100E, + 0x1998, 0x1280100F, + 0x1998, 0x12801010, + 0x1998, 0x12801011, + 0x1998, 0x12801012, + 0x1998, 0x12801013, + 0x1998, 0x12801014, + 0x1998, 0x12801015, + 0x1998, 0x12801016, + 0x1998, 0x12801017, + 0x1998, 0x12801018, + 0x1998, 0x12801019, + 0x1998, 0x1280101A, + 0x1998, 0x1280101B, + 0x1998, 0x1280101C, + 0x1998, 0x1280101D, + 0x1998, 0x1280101E, + 0x1998, 0x1280101F, + 0x1998, 0x12801020, + 0x1998, 0x12801021, + 0x1998, 0x12801022, + 0x1998, 0x12801023, + 0x1998, 0x1280102C, + 0x1998, 0x1280102D, + 0x1998, 0x1280102E, + 0x1998, 0x1280102F, + 0x1998, 0x12801030, + 0x1998, 0x12801031, + 0x1998, 0x12801032, + 0x1998, 0x12801033, + 0x1998, 0x12801034, + 0x1998, 0x12801035, + 0x1998, 0x12801036, + 0x1998, 0x12801037, + 0x1998, 0x12801038, + 0x1998, 0x12801039, + 0x1998, 0x1280103A, + 0x1998, 0x1280103B, + 0x1998, 0x1280103C, + 0x1998, 0x1280103D, + 0x1998, 0x1280103E, + 0x1998, 0x1280103F, + 0x1998, 0x12801040, + 0x1998, 0x12801041, + 0x1998, 0x12801042, + 0x1998, 0x12801043, + 0x1998, 0x12801044, + 0x1998, 0x12801045, + 0x1998, 0x12801046, + 0x1998, 0x12801047, + 0x1998, 0x12801048, + 0x1998, 0x12801049, + 0x1998, 0x12801100, + 0x1998, 0x12801101, + 0x1998, 0x12801102, + 0x1998, 0x12801103, + 0x1998, 0x12801104, + 0x1998, 0x12801105, + 0x1998, 0x12801106, + 0x1998, 0x12801107, + 0x1998, 0x12801108, + 0x1998, 0x12801109, + 0x1998, 0x1280110A, + 0x1998, 0x1280110B, + 0x1998, 0x1280110C, + 0x1998, 0x1280110D, + 0x1998, 0x1280110E, + 0x1998, 0x1280110F, + 0x1998, 0x12801110, + 0x1998, 0x12801111, + 0x1998, 0x12801112, + 0x1998, 0x12801113, + 0x1998, 0x12801114, + 0x1998, 0x12801115, + 0x1998, 0x12801116, + 0x1998, 0x12801117, + 0x1998, 0x12801118, + 0x1998, 0x12801119, + 0x1998, 0x1280111A, + 0x1998, 0x1280111B, + 0x1998, 0x1280111C, + 0x1998, 0x1280111D, + 0x1998, 0x1280111E, + 0x1998, 0x1280111F, + 0x1998, 0x12801120, + 0x1998, 0x12801121, + 0x1998, 0x12801122, + 0x1998, 0x12801123, + 0x1998, 0x1280112C, + 0x1998, 0x1280112D, + 0x1998, 0x1280112E, + 0x1998, 0x1280112F, + 0x1998, 0x12801130, + 0x1998, 0x12801131, + 0x1998, 0x12801132, + 0x1998, 0x12801133, + 0x1998, 0x12801134, + 0x1998, 0x12801135, + 0x1998, 0x12801136, + 0x1998, 0x12801137, + 0x1998, 0x12801138, + 0x1998, 0x12801139, + 0x1998, 0x1280113A, + 0x1998, 0x1280113B, + 0x1998, 0x1280113C, + 0x1998, 0x1280113D, + 0x1998, 0x1280113E, + 0x1998, 0x1280113F, + 0x1998, 0x12801140, + 0x1998, 0x12801141, + 0x1998, 0x12801142, + 0x1998, 0x12801143, + 0x1998, 0x12801144, + 0x1998, 0x12801145, + 0x1998, 0x12801146, + 0x1998, 0x12801147, + 0x1998, 0x12801148, + 0x1998, 0x12801149, + 0x1998, 0x12801200, + 0x1998, 0x12801201, + 0x1998, 0x12801202, + 0x1998, 0x12801203, + 0x1998, 0x12801204, + 0x1998, 0x12801205, + 0x1998, 0x12801206, + 0x1998, 0x12801207, + 0x1998, 0x12801208, + 0x1998, 0x12801209, + 0x1998, 0x1280120A, + 0x1998, 0x1280120B, + 0x1998, 0x1280120C, + 0x1998, 0x1280120D, + 0x1998, 0x1280120E, + 0x1998, 0x1280120F, + 0x1998, 0x12801210, + 0x1998, 0x12801211, + 0x1998, 0x12801212, + 0x1998, 0x12801213, + 0x1998, 0x12801214, + 0x1998, 0x12801215, + 0x1998, 0x12801216, + 0x1998, 0x12801217, + 0x1998, 0x12801218, + 0x1998, 0x12801219, + 0x1998, 0x1280121A, + 0x1998, 0x1280121B, + 0x1998, 0x1280121C, + 0x1998, 0x1280121D, + 0x1998, 0x1280121E, + 0x1998, 0x1280121F, + 0x1998, 0x12801220, + 0x1998, 0x12801221, + 0x1998, 0x12801222, + 0x1998, 0x12801223, + 0x1998, 0x1280122C, + 0x1998, 0x1280122D, + 0x1998, 0x1280122E, + 0x1998, 0x1280122F, + 0x1998, 0x12801230, + 0x1998, 0x12801231, + 0x1998, 0x12801232, + 0x1998, 0x12801233, + 0x1998, 0x12801234, + 0x1998, 0x12801235, + 0x1998, 0x12801236, + 0x1998, 0x12801237, + 0x1998, 0x12801238, + 0x1998, 0x12801239, + 0x1998, 0x1280123A, + 0x1998, 0x1280123B, + 0x1998, 0x1280123C, + 0x1998, 0x1280123D, + 0x1998, 0x1280123E, + 0x1998, 0x1280123F, + 0x1998, 0x12801240, + 0x1998, 0x12801241, + 0x1998, 0x12801242, + 0x1998, 0x12801243, + 0x1998, 0x12801244, + 0x1998, 0x12801245, + 0x1998, 0x12801246, + 0x1998, 0x12801247, + 0x1998, 0x12801248, + 0x1998, 0x12801249, + 0x1998, 0x12801300, + 0x1998, 0x12801301, + 0x1998, 0x12801302, + 0x1998, 0x12801303, + 0x1998, 0x12801304, + 0x1998, 0x12801305, + 0x1998, 0x12801306, + 0x1998, 0x12801307, + 0x1998, 0x12801308, + 0x1998, 0x12801309, + 0x1998, 0x1280130A, + 0x1998, 0x1280130B, + 0x1998, 0x1280130C, + 0x1998, 0x1280130D, + 0x1998, 0x1280130E, + 0x1998, 0x1280130F, + 0x1998, 0x12801310, + 0x1998, 0x12801311, + 0x1998, 0x12801312, + 0x1998, 0x12801313, + 0x1998, 0x12801314, + 0x1998, 0x12801315, + 0x1998, 0x12801316, + 0x1998, 0x12801317, + 0x1998, 0x12801318, + 0x1998, 0x12801319, + 0x1998, 0x1280131A, + 0x1998, 0x1280131B, + 0x1998, 0x1280131C, + 0x1998, 0x1280131D, + 0x1998, 0x1280131E, + 0x1998, 0x1280131F, + 0x1998, 0x12801320, + 0x1998, 0x12801321, + 0x1998, 0x12801322, + 0x1998, 0x12801323, + 0x1998, 0x1280132C, + 0x1998, 0x1280132D, + 0x1998, 0x1280132E, + 0x1998, 0x1280132F, + 0x1998, 0x12801330, + 0x1998, 0x12801331, + 0x1998, 0x12801332, + 0x1998, 0x12801333, + 0x1998, 0x12801334, + 0x1998, 0x12801335, + 0x1998, 0x12801336, + 0x1998, 0x12801337, + 0x1998, 0x12801338, + 0x1998, 0x12801339, + 0x1998, 0x1280133A, + 0x1998, 0x1280133B, + 0x1998, 0x1280133C, + 0x1998, 0x1280133D, + 0x1998, 0x1280133E, + 0x1998, 0x1280133F, + 0x1998, 0x12801340, + 0x1998, 0x12801341, + 0x1998, 0x12801342, + 0x1998, 0x12801343, + 0x1998, 0x12801344, + 0x1998, 0x12801345, + 0x1998, 0x12801346, + 0x1998, 0x12801347, + 0x1998, 0x12801348, + 0x1998, 0x12801349, + 0x19D4, 0x88888888, + 0x19D8, 0x00000888, + 0xB00, 0xE3100100, + 0xB00, 0xE7100100, + 0xC60, 0x15808002, + 0xC60, 0x01808003, + 0xE60, 0x15808002, + 0xE60, 0x01808003, + 0x1860, 0x15808002, + 0x1860, 0x01808003, + 0x1A60, 0x15808002, + 0x1A60, 0x01808003, + 0xB00, 0xE3100100, + 0xC5C, 0x0D080058, + 0xE5C, 0x0D080058, + 0x185C, 0x0D080058, + 0x1A5C, 0x0D080058, + 0xC5C, 0x0D000058, + 0xE5C, 0x0D000058, + 0x185C, 0x0D000058, + 0x1A5C, 0x0D000058, + 0xC60, 0x05808002, + 0xC60, 0x0E808003, + 0xE60, 0x05808002, + 0xE60, 0x0E808003, + 0x1860, 0x05808002, + 0x1860, 0x0E808003, + 0x1A60, 0x05808002, + 0x1A60, 0x0E808003, + 0xB00, 0xE7100100, + 0xB00, 0xE3100100, + 0xB00, 0xE3100000, + 0x1C38, 0x00000002, + 0xA00, 0x00D047C8, + 0xA04, 0x46FF800C, + 0xA08, 0x8C838300, + 0xA0C, 0x2E7E000F, + 0xA10, 0x9500BB78, + 0xA14, 0x11144028, + 0xA18, 0x00881117, + 0xA1C, 0x89140F00, + 0xA20, 0x1A1B0030, + 0xA24, 0x090E1317, + 0xA28, 0x00000204, + 0xA2C, 0x00900000, + 0xA70, 0x101FFF00, + 0xA74, 0x00000128, + 0xA78, 0x00000900, + 0xA7C, 0x225B0606, + 0xA80, 0x218075B2, + 0xA84, 0x9C1F8C00, + 0x1B04, 0xE24628D2, + 0x1B10, 0x88010D46, + 0x1B14, 0x00000000, + 0x1B18, 0x00292903, + 0x1B00, 0xF8000000, + 0x1B00, 0xF800D000, + 0x1B00, 0xF801F000, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000002, + 0x1B00, 0xF800D002, + 0x1B00, 0xF801F002, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000004, + 0x1B00, 0xF800D004, + 0x1B00, 0xF801F004, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000006, + 0x1B00, 0xF800D006, + 0x1B00, 0xF801F006, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000000, + 0x1B80, 0x00000007, + 0x1B80, 0x09060005, + 0x1B80, 0x09060007, + 0x1B80, 0x0FFE0015, + 0x1B80, 0x0FFE0017, + 0x1B80, 0x00240025, + 0x1B80, 0x00240027, + 0x1B80, 0x00040035, + 0x1B80, 0x00040037, + 0x1B80, 0x05C00045, + 0x1B80, 0x05C00047, + 0x1B80, 0x00070055, + 0x1B80, 0x00070057, + 0x1B80, 0x64000065, + 0x1B80, 0x64000067, + 0x1B80, 0x00020075, + 0x1B80, 0x00020077, + 0x1B80, 0x00080085, + 0x1B80, 0x00080087, + 0x1B80, 0x80000095, + 0x1B80, 0x80000097, + 0x1B80, 0x090100A5, + 0x1B80, 0x090100A7, + 0x1B80, 0x0F0200B5, + 0x1B80, 0x0F0200B7, + 0x1B80, 0x002400C5, + 0x1B80, 0x002400C7, + 0x1B80, 0x000400D5, + 0x1B80, 0x000400D7, + 0x1B80, 0x05C000E5, + 0x1B80, 0x05C000E7, + 0x1B80, 0x000700F5, + 0x1B80, 0x000700F7, + 0x1B80, 0x64020105, + 0x1B80, 0x64020107, + 0x1B80, 0x00020115, + 0x1B80, 0x00020117, + 0x1B80, 0x00040125, + 0x1B80, 0x00040127, + 0x1B80, 0x4A000135, + 0x1B80, 0x4A000137, + 0x1B80, 0x4B040145, + 0x1B80, 0x4B040147, + 0x1B80, 0x85030155, + 0x1B80, 0x85030157, + 0x1B80, 0x40010165, + 0x1B80, 0x40010167, + 0x1B80, 0xE0290175, + 0x1B80, 0xE0290177, + 0x1B80, 0x00040185, + 0x1B80, 0x00040187, + 0x1B80, 0x4B050195, + 0x1B80, 0x4B050197, + 0x1B80, 0x860301A5, + 0x1B80, 0x860301A7, + 0x1B80, 0x400301B5, + 0x1B80, 0x400301B7, + 0x1B80, 0xE02901C5, + 0x1B80, 0xE02901C7, + 0x1B80, 0x000401D5, + 0x1B80, 0x000401D7, + 0x1B80, 0x4B0601E5, + 0x1B80, 0x4B0601E7, + 0x1B80, 0x870301F5, + 0x1B80, 0x870301F7, + 0x1B80, 0x40050205, + 0x1B80, 0x40050207, + 0x1B80, 0xE0290215, + 0x1B80, 0xE0290217, + 0x1B80, 0x00040225, + 0x1B80, 0x00040227, + 0x1B80, 0x4B070235, + 0x1B80, 0x4B070237, + 0x1B80, 0x88030245, + 0x1B80, 0x88030247, + 0x1B80, 0x40070255, + 0x1B80, 0x40070257, + 0x1B80, 0xE0290265, + 0x1B80, 0xE0290267, + 0x1B80, 0x4B000275, + 0x1B80, 0x4B000277, + 0x1B80, 0x30000285, + 0x1B80, 0x30000287, + 0x1B80, 0xFE100295, + 0x1B80, 0xFE100297, + 0x1B80, 0xFF1002A5, + 0x1B80, 0xFF1002A7, + 0x1B80, 0xE18602B5, + 0x1B80, 0xE18602B7, + 0x1B80, 0xF00A02C5, + 0x1B80, 0xF00A02C7, + 0x1B80, 0xF10A02D5, + 0x1B80, 0xF10A02D7, + 0x1B80, 0xF20A02E5, + 0x1B80, 0xF20A02E7, + 0x1B80, 0xF30802F5, + 0x1B80, 0xF30802F7, + 0x1B80, 0xF4070305, + 0x1B80, 0xF4070307, + 0x1B80, 0xF5060315, + 0x1B80, 0xF5060317, + 0x1B80, 0xF7060325, + 0x1B80, 0xF7060327, + 0x1B80, 0xF8050335, + 0x1B80, 0xF8050337, + 0x1B80, 0xF9040345, + 0x1B80, 0xF9040347, + 0x1B80, 0x00010355, + 0x1B80, 0x00010357, + 0x1B80, 0x303B0365, + 0x1B80, 0x303B0367, + 0x1B80, 0x30500375, + 0x1B80, 0x30500377, + 0x1B80, 0x305C0385, + 0x1B80, 0x305C0387, + 0x1B80, 0x31D50395, + 0x1B80, 0x31D50397, + 0x1B80, 0x31C503A5, + 0x1B80, 0x31C503A7, + 0x1B80, 0x4D0403B5, + 0x1B80, 0x4D0403B7, + 0x1B80, 0x2EF003C5, + 0x1B80, 0x2EF003C7, + 0x1B80, 0x000203D5, + 0x1B80, 0x000203D7, + 0x1B80, 0x208003E5, + 0x1B80, 0x208003E7, + 0x1B80, 0x000003F5, + 0x1B80, 0x000003F7, + 0x1B80, 0x4D000405, + 0x1B80, 0x4D000407, + 0x1B80, 0x55070415, + 0x1B80, 0x55070417, + 0x1B80, 0xE1230425, + 0x1B80, 0xE1230427, + 0x1B80, 0xE1230435, + 0x1B80, 0xE1230437, + 0x1B80, 0x4D040445, + 0x1B80, 0x4D040447, + 0x1B80, 0x20800455, + 0x1B80, 0x20800457, + 0x1B80, 0x84000465, + 0x1B80, 0x84000467, + 0x1B80, 0x4D000475, + 0x1B80, 0x4D000477, + 0x1B80, 0x550F0485, + 0x1B80, 0x550F0487, + 0x1B80, 0xE1230495, + 0x1B80, 0xE1230497, + 0x1B80, 0x4F0204A5, + 0x1B80, 0x4F0204A7, + 0x1B80, 0x4E0004B5, + 0x1B80, 0x4E0004B7, + 0x1B80, 0x530204C5, + 0x1B80, 0x530204C7, + 0x1B80, 0x520104D5, + 0x1B80, 0x520104D7, + 0x1B80, 0xE12704E5, + 0x1B80, 0xE12704E7, + 0x1B80, 0x000104F5, + 0x1B80, 0x000104F7, + 0x1B80, 0x5C720505, + 0x1B80, 0x5C720507, + 0x1B80, 0xE1320515, + 0x1B80, 0xE1320517, + 0x1B80, 0x54E50525, + 0x1B80, 0x54E50527, + 0x1B80, 0x54BF0535, + 0x1B80, 0x54BF0537, + 0x1B80, 0x54C50545, + 0x1B80, 0x54C50547, + 0x1B80, 0x54BE0555, + 0x1B80, 0x54BE0557, + 0x1B80, 0x54DF0565, + 0x1B80, 0x54DF0567, + 0x1B80, 0x0BA60575, + 0x1B80, 0x0BA60577, + 0x1B80, 0xF3130585, + 0x1B80, 0xF3130587, + 0x1B80, 0xF41E0595, + 0x1B80, 0xF41E0597, + 0x1B80, 0xF53C05A5, + 0x1B80, 0xF53C05A7, + 0x1B80, 0x000105B5, + 0x1B80, 0x000105B7, + 0x1B80, 0x620605C5, + 0x1B80, 0x620605C7, + 0x1B80, 0x600605D5, + 0x1B80, 0x600605D7, + 0x1B80, 0xE1A905E5, + 0x1B80, 0xE1A905E7, + 0x1B80, 0x0C0005F5, + 0x1B80, 0x0C0005F7, + 0x1B80, 0x5C720605, + 0x1B80, 0x5C720607, + 0x1B80, 0xE1320615, + 0x1B80, 0xE1320617, + 0x1B80, 0x5CF10625, + 0x1B80, 0x5CF10627, + 0x1B80, 0x0C010635, + 0x1B80, 0x0C010637, + 0x1B80, 0xF2020645, + 0x1B80, 0xF2020647, + 0x1B80, 0x30D60655, + 0x1B80, 0x30D60657, + 0x1B80, 0x0AC60665, + 0x1B80, 0x0AC60667, + 0x1B80, 0xE1B60675, + 0x1B80, 0xE1B60677, + 0x1B80, 0xE1580685, + 0x1B80, 0xE1580687, + 0x1B80, 0x54E50695, + 0x1B80, 0x54E50697, + 0x1B80, 0x000106A5, + 0x1B80, 0x000106A7, + 0x1B80, 0x560106B5, + 0x1B80, 0x560106B7, + 0x1B80, 0x5CE206C5, + 0x1B80, 0x5CE206C7, + 0x1B80, 0x0AE106D5, + 0x1B80, 0x0AE106D7, + 0x1B80, 0x630C06E5, + 0x1B80, 0x630C06E7, + 0x1B80, 0xE13F06F5, + 0x1B80, 0xE13F06F7, + 0x1B80, 0x00270705, + 0x1B80, 0x00270707, + 0x1B80, 0xE16C0715, + 0x1B80, 0xE16C0717, + 0x1B80, 0x00020725, + 0x1B80, 0x00020727, + 0x1B80, 0x002A0735, + 0x1B80, 0x002A0737, + 0x1B80, 0x07140745, + 0x1B80, 0x07140747, + 0x1B80, 0x00020755, + 0x1B80, 0x00020757, + 0x1B80, 0x30C30765, + 0x1B80, 0x30C30767, + 0x1B80, 0x56010775, + 0x1B80, 0x56010777, + 0x1B80, 0x5CE20785, + 0x1B80, 0x5CE20787, + 0x1B80, 0x0AE10795, + 0x1B80, 0x0AE10797, + 0x1B80, 0x631707A5, + 0x1B80, 0x631707A7, + 0x1B80, 0xE13F07B5, + 0x1B80, 0xE13F07B7, + 0x1B80, 0x002507C5, + 0x1B80, 0x002507C7, + 0x1B80, 0xE16C07D5, + 0x1B80, 0xE16C07D7, + 0x1B80, 0x000207E5, + 0x1B80, 0x000207E7, + 0x1B80, 0x630F07F5, + 0x1B80, 0x630F07F7, + 0x1B80, 0xE13F0805, + 0x1B80, 0xE13F0807, + 0x1B80, 0x63070815, + 0x1B80, 0x63070817, + 0x1B80, 0xE13F0825, + 0x1B80, 0xE13F0827, + 0x1B80, 0x07140835, + 0x1B80, 0x07140837, + 0x1B80, 0x56000845, + 0x1B80, 0x56000847, + 0x1B80, 0x5CF20855, + 0x1B80, 0x5CF20857, + 0x1B80, 0x0AF10865, + 0x1B80, 0x0AF10867, + 0x1B80, 0x07140875, + 0x1B80, 0x07140877, + 0x1B80, 0x07140885, + 0x1B80, 0x07140887, + 0x1B80, 0x630F0895, + 0x1B80, 0x630F0897, + 0x1B80, 0xE13F08A5, + 0x1B80, 0xE13F08A7, + 0x1B80, 0x631708B5, + 0x1B80, 0x631708B7, + 0x1B80, 0xE13F08C5, + 0x1B80, 0xE13F08C7, + 0x1B80, 0x002508D5, + 0x1B80, 0x002508D7, + 0x1B80, 0xE16C08E5, + 0x1B80, 0xE16C08E7, + 0x1B80, 0x000208F5, + 0x1B80, 0x000208F7, + 0x1B80, 0x30C30905, + 0x1B80, 0x30C30907, + 0x1B80, 0xE1A90915, + 0x1B80, 0xE1A90917, + 0x1B80, 0x62060925, + 0x1B80, 0x62060927, + 0x1B80, 0x60060935, + 0x1B80, 0x60060937, + 0x1B80, 0xE1160945, + 0x1B80, 0xE1160947, + 0x1B80, 0x54BE0955, + 0x1B80, 0x54BE0957, + 0x1B80, 0x56010965, + 0x1B80, 0x56010967, + 0x1B80, 0x5CE20975, + 0x1B80, 0x5CE20977, + 0x1B80, 0x0AE10985, + 0x1B80, 0x0AE10987, + 0x1B80, 0x633A0995, + 0x1B80, 0x633A0997, + 0x1B80, 0xE13F09A5, + 0x1B80, 0xE13F09A7, + 0x1B80, 0x633709B5, + 0x1B80, 0x633709B7, + 0x1B80, 0xE13F09C5, + 0x1B80, 0xE13F09C7, + 0x1B80, 0x632F09D5, + 0x1B80, 0x632F09D7, + 0x1B80, 0xE13F09E5, + 0x1B80, 0xE13F09E7, + 0x1B80, 0x632709F5, + 0x1B80, 0x632709F7, + 0x1B80, 0xE13F0A05, + 0x1B80, 0xE13F0A07, + 0x1B80, 0x631F0A15, + 0x1B80, 0x631F0A17, + 0x1B80, 0xE13F0A25, + 0x1B80, 0xE13F0A27, + 0x1B80, 0x63170A35, + 0x1B80, 0x63170A37, + 0x1B80, 0xE13F0A45, + 0x1B80, 0xE13F0A47, + 0x1B80, 0x630F0A55, + 0x1B80, 0x630F0A57, + 0x1B80, 0xE13F0A65, + 0x1B80, 0xE13F0A67, + 0x1B80, 0x63070A75, + 0x1B80, 0x63070A77, + 0x1B80, 0xE13F0A85, + 0x1B80, 0xE13F0A87, + 0x1B80, 0xE16C0A95, + 0x1B80, 0xE16C0A97, + 0x1B80, 0x56000AA5, + 0x1B80, 0x56000AA7, + 0x1B80, 0x5CF20AB5, + 0x1B80, 0x5CF20AB7, + 0x1B80, 0x0AF10AC5, + 0x1B80, 0x0AF10AC7, + 0x1B80, 0xF5040AD5, + 0x1B80, 0xF5040AD7, + 0x1B80, 0xE13F0AE5, + 0x1B80, 0xE13F0AE7, + 0x1B80, 0xE16C0AF5, + 0x1B80, 0xE16C0AF7, + 0x1B80, 0x30B30B05, + 0x1B80, 0x30B30B07, + 0x1B80, 0x07140B15, + 0x1B80, 0x07140B17, + 0x1B80, 0x07140B25, + 0x1B80, 0x07140B27, + 0x1B80, 0x630F0B35, + 0x1B80, 0x630F0B37, + 0x1B80, 0xE13F0B45, + 0x1B80, 0xE13F0B47, + 0x1B80, 0x63170B55, + 0x1B80, 0x63170B57, + 0x1B80, 0xE13F0B65, + 0x1B80, 0xE13F0B67, + 0x1B80, 0x631F0B75, + 0x1B80, 0x631F0B77, + 0x1B80, 0xE13F0B85, + 0x1B80, 0xE13F0B87, + 0x1B80, 0x63270B95, + 0x1B80, 0x63270B97, + 0x1B80, 0xE13F0BA5, + 0x1B80, 0xE13F0BA7, + 0x1B80, 0x632F0BB5, + 0x1B80, 0x632F0BB7, + 0x1B80, 0xE13F0BC5, + 0x1B80, 0xE13F0BC7, + 0x1B80, 0x63370BD5, + 0x1B80, 0x63370BD7, + 0x1B80, 0xE13F0BE5, + 0x1B80, 0xE13F0BE7, + 0x1B80, 0x633A0BF5, + 0x1B80, 0x633A0BF7, + 0x1B80, 0xE13F0C05, + 0x1B80, 0xE13F0C07, + 0x1B80, 0xF60B0C15, + 0x1B80, 0xF60B0C17, + 0x1B80, 0xF7170C25, + 0x1B80, 0xF7170C27, + 0x1B80, 0x4D300C35, + 0x1B80, 0x4D300C37, + 0x1B80, 0x57040C45, + 0x1B80, 0x57040C47, + 0x1B80, 0x57000C55, + 0x1B80, 0x57000C57, + 0x1B80, 0x96000C65, + 0x1B80, 0x96000C67, + 0x1B80, 0x57080C75, + 0x1B80, 0x57080C77, + 0x1B80, 0x57000C85, + 0x1B80, 0x57000C87, + 0x1B80, 0x95000C95, + 0x1B80, 0x95000C97, + 0x1B80, 0x4D000CA5, + 0x1B80, 0x4D000CA7, + 0x1B80, 0x6C070CB5, + 0x1B80, 0x6C070CB7, + 0x1B80, 0x00010CC5, + 0x1B80, 0x00010CC7, + 0x1B80, 0x00220CD5, + 0x1B80, 0x00220CD7, + 0x1B80, 0x06140CE5, + 0x1B80, 0x06140CE7, + 0x1B80, 0xE16C0CF5, + 0x1B80, 0xE16C0CF7, + 0x1B80, 0x00020D05, + 0x1B80, 0x00020D07, + 0x1B80, 0x00250D15, + 0x1B80, 0x00250D17, + 0x1B80, 0x06140D25, + 0x1B80, 0x06140D27, + 0x1B80, 0xE16C0D35, + 0x1B80, 0xE16C0D37, + 0x1B80, 0x00020D45, + 0x1B80, 0x00020D47, + 0x1B80, 0x00010D55, + 0x1B80, 0x00010D57, + 0x1B80, 0x00320D65, + 0x1B80, 0x00320D67, + 0x1B80, 0xE16C0D75, + 0x1B80, 0xE16C0D77, + 0x1B80, 0x00020D85, + 0x1B80, 0x00020D87, + 0x1B80, 0xE1860D95, + 0x1B80, 0xE1860D97, + 0x1B80, 0xE1B60DA5, + 0x1B80, 0xE1B60DA7, + 0x1B80, 0x5CD10DB5, + 0x1B80, 0x5CD10DB7, + 0x1B80, 0x673A0DC5, + 0x1B80, 0x673A0DC7, + 0x1B80, 0xE1230DD5, + 0x1B80, 0xE1230DD7, + 0x1B80, 0xF80B0DE5, + 0x1B80, 0xF80B0DE7, + 0x1B80, 0xF9110DF5, + 0x1B80, 0xF9110DF7, + 0x1B80, 0xE1580E05, + 0x1B80, 0xE1580E07, + 0x1B80, 0x67370E15, + 0x1B80, 0x67370E17, + 0x1B80, 0xE1580E25, + 0x1B80, 0xE1580E27, + 0x1B80, 0x672F0E35, + 0x1B80, 0x672F0E37, + 0x1B80, 0xE1580E45, + 0x1B80, 0xE1580E47, + 0x1B80, 0x67270E55, + 0x1B80, 0x67270E57, + 0x1B80, 0xE1580E65, + 0x1B80, 0xE1580E67, + 0x1B80, 0x671F0E75, + 0x1B80, 0x671F0E77, + 0x1B80, 0xE1580E85, + 0x1B80, 0xE1580E87, + 0x1B80, 0x67170E95, + 0x1B80, 0x67170E97, + 0x1B80, 0xE1580EA5, + 0x1B80, 0xE1580EA7, + 0x1B80, 0xF8020EB5, + 0x1B80, 0xF8020EB7, + 0x1B80, 0x30EE0EC5, + 0x1B80, 0x30EE0EC7, + 0x1B80, 0xE0D10ED5, + 0x1B80, 0xE0D10ED7, + 0x1B80, 0x670F0EE5, + 0x1B80, 0x670F0EE7, + 0x1B80, 0xE1580EF5, + 0x1B80, 0xE1580EF7, + 0x1B80, 0x67070F05, + 0x1B80, 0x67070F07, + 0x1B80, 0xE1580F15, + 0x1B80, 0xE1580F17, + 0x1B80, 0xF9020F25, + 0x1B80, 0xF9020F27, + 0x1B80, 0x30F50F35, + 0x1B80, 0x30F50F37, + 0x1B80, 0xE0CD0F45, + 0x1B80, 0xE0CD0F47, + 0x1B80, 0x06140F55, + 0x1B80, 0x06140F57, + 0x1B80, 0xE16C0F65, + 0x1B80, 0xE16C0F67, + 0x1B80, 0x5CF10F75, + 0x1B80, 0x5CF10F77, + 0x1B80, 0xE1580F85, + 0x1B80, 0xE1580F87, + 0x1B80, 0x06140F95, + 0x1B80, 0x06140F97, + 0x1B80, 0xE16C0FA5, + 0x1B80, 0xE16C0FA7, + 0x1B80, 0xF9020FB5, + 0x1B80, 0xF9020FB7, + 0x1B80, 0x30FF0FC5, + 0x1B80, 0x30FF0FC7, + 0x1B80, 0xE0CD0FD5, + 0x1B80, 0xE0CD0FD7, + 0x1B80, 0x31130FE5, + 0x1B80, 0x31130FE7, + 0x1B80, 0x670F0FF5, + 0x1B80, 0x670F0FF7, + 0x1B80, 0xE1581005, + 0x1B80, 0xE1581007, + 0x1B80, 0x67171015, + 0x1B80, 0x67171017, + 0x1B80, 0xE1581025, + 0x1B80, 0xE1581027, + 0x1B80, 0xF8021035, + 0x1B80, 0xF8021037, + 0x1B80, 0x31071045, + 0x1B80, 0x31071047, + 0x1B80, 0xE0D11055, + 0x1B80, 0xE0D11057, + 0x1B80, 0x31131065, + 0x1B80, 0x31131067, + 0x1B80, 0x670F1075, + 0x1B80, 0x670F1077, + 0x1B80, 0xE1581085, + 0x1B80, 0xE1581087, + 0x1B80, 0x671F1095, + 0x1B80, 0x671F1097, + 0x1B80, 0xE15810A5, + 0x1B80, 0xE15810A7, + 0x1B80, 0x672710B5, + 0x1B80, 0x672710B7, + 0x1B80, 0xE15810C5, + 0x1B80, 0xE15810C7, + 0x1B80, 0x672F10D5, + 0x1B80, 0x672F10D7, + 0x1B80, 0xE15810E5, + 0x1B80, 0xE15810E7, + 0x1B80, 0x673710F5, + 0x1B80, 0x673710F7, + 0x1B80, 0xE1581105, + 0x1B80, 0xE1581107, + 0x1B80, 0x673A1115, + 0x1B80, 0x673A1117, + 0x1B80, 0xE1581125, + 0x1B80, 0xE1581127, + 0x1B80, 0x4D101135, + 0x1B80, 0x4D101137, + 0x1B80, 0x30C41145, + 0x1B80, 0x30C41147, + 0x1B80, 0x00011155, + 0x1B80, 0x00011157, + 0x1B80, 0x6F241165, + 0x1B80, 0x6F241167, + 0x1B80, 0x6E401175, + 0x1B80, 0x6E401177, + 0x1B80, 0x6D001185, + 0x1B80, 0x6D001187, + 0x1B80, 0x55031195, + 0x1B80, 0x55031197, + 0x1B80, 0x312311A5, + 0x1B80, 0x312311A7, + 0x1B80, 0x6F1C11B5, + 0x1B80, 0x6F1C11B7, + 0x1B80, 0x6E4011C5, + 0x1B80, 0x6E4011C7, + 0x1B80, 0x550B11D5, + 0x1B80, 0x550B11D7, + 0x1B80, 0x312311E5, + 0x1B80, 0x312311E7, + 0x1B80, 0x061C11F5, + 0x1B80, 0x061C11F7, + 0x1B80, 0x54DE1205, + 0x1B80, 0x54DE1207, + 0x1B80, 0x06DC1215, + 0x1B80, 0x06DC1217, + 0x1B80, 0x55131225, + 0x1B80, 0x55131227, + 0x1B80, 0x74011235, + 0x1B80, 0x74011237, + 0x1B80, 0x74001245, + 0x1B80, 0x74001247, + 0x1B80, 0x8E001255, + 0x1B80, 0x8E001257, + 0x1B80, 0x00011265, + 0x1B80, 0x00011267, + 0x1B80, 0x57021275, + 0x1B80, 0x57021277, + 0x1B80, 0x57001285, + 0x1B80, 0x57001287, + 0x1B80, 0x97001295, + 0x1B80, 0x97001297, + 0x1B80, 0x000112A5, + 0x1B80, 0x000112A7, + 0x1B80, 0x54BF12B5, + 0x1B80, 0x54BF12B7, + 0x1B80, 0x54C112C5, + 0x1B80, 0x54C112C7, + 0x1B80, 0x54A212D5, + 0x1B80, 0x54A212D7, + 0x1B80, 0x54C012E5, + 0x1B80, 0x54C012E7, + 0x1B80, 0x54A112F5, + 0x1B80, 0x54A112F7, + 0x1B80, 0x54DF1305, + 0x1B80, 0x54DF1307, + 0x1B80, 0x00011315, + 0x1B80, 0x00011317, + 0x1B80, 0x55001325, + 0x1B80, 0x55001327, + 0x1B80, 0xE1231335, + 0x1B80, 0xE1231337, + 0x1B80, 0x54811345, + 0x1B80, 0x54811347, + 0x1B80, 0xE1231355, + 0x1B80, 0xE1231357, + 0x1B80, 0x54801365, + 0x1B80, 0x54801367, + 0x1B80, 0x002A1375, + 0x1B80, 0x002A1377, + 0x1B80, 0xE12B1385, + 0x1B80, 0xE12B1387, + 0x1B80, 0xE1231395, + 0x1B80, 0xE1231397, + 0x1B80, 0x548013A5, + 0x1B80, 0x548013A7, + 0x1B80, 0xE17213B5, + 0x1B80, 0xE17213B7, + 0x1B80, 0xBF3013C5, + 0x1B80, 0xBF3013C7, + 0x1B80, 0x000213D5, + 0x1B80, 0x000213D7, + 0x1B80, 0x302813E5, + 0x1B80, 0x302813E7, + 0x1B80, 0x4F7813F5, + 0x1B80, 0x4F7813F7, + 0x1B80, 0x4E001405, + 0x1B80, 0x4E001407, + 0x1B80, 0x53871415, + 0x1B80, 0x53871417, + 0x1B80, 0x52F11425, + 0x1B80, 0x52F11427, + 0x1B80, 0xE1161435, + 0x1B80, 0xE1161437, + 0x1B80, 0xE11B1445, + 0x1B80, 0xE11B1447, + 0x1B80, 0xE11F1455, + 0x1B80, 0xE11F1457, + 0x1B80, 0xE1271465, + 0x1B80, 0xE1271467, + 0x1B80, 0x54811475, + 0x1B80, 0x54811477, + 0x1B80, 0xE1161485, + 0x1B80, 0xE1161487, + 0x1B80, 0xE11B1495, + 0x1B80, 0xE11B1497, + 0x1B80, 0xE11F14A5, + 0x1B80, 0xE11F14A7, + 0x1B80, 0xE12714B5, + 0x1B80, 0xE12714B7, + 0x1B80, 0x548014C5, + 0x1B80, 0x548014C7, + 0x1B80, 0x002A14D5, + 0x1B80, 0x002A14D7, + 0x1B80, 0xE12B14E5, + 0x1B80, 0xE12B14E7, + 0x1B80, 0xE11614F5, + 0x1B80, 0xE11614F7, + 0x1B80, 0xE11B1505, + 0x1B80, 0xE11B1507, + 0x1B80, 0xE11F1515, + 0x1B80, 0xE11F1517, + 0x1B80, 0xE1271525, + 0x1B80, 0xE1271527, + 0x1B80, 0x54801535, + 0x1B80, 0x54801537, + 0x1B80, 0xE1721545, + 0x1B80, 0xE1721547, + 0x1B80, 0xBF171555, + 0x1B80, 0xBF171557, + 0x1B80, 0x00021565, + 0x1B80, 0x00021567, + 0x1B80, 0x30281575, + 0x1B80, 0x30281577, + 0x1B80, 0x06141585, + 0x1B80, 0x06141587, + 0x1B80, 0x73201595, + 0x1B80, 0x73201597, + 0x1B80, 0x720015A5, + 0x1B80, 0x720015A7, + 0x1B80, 0x710015B5, + 0x1B80, 0x710015B7, + 0x1B80, 0x550115C5, + 0x1B80, 0x550115C7, + 0x1B80, 0xE12315D5, + 0x1B80, 0xE12315D7, + 0x1B80, 0xE12715E5, + 0x1B80, 0xE12715E7, + 0x1B80, 0x548115F5, + 0x1B80, 0x548115F7, + 0x1B80, 0xE1231605, + 0x1B80, 0xE1231607, + 0x1B80, 0xE1271615, + 0x1B80, 0xE1271617, + 0x1B80, 0x54801625, + 0x1B80, 0x54801627, + 0x1B80, 0x002A1635, + 0x1B80, 0x002A1637, + 0x1B80, 0xE12B1645, + 0x1B80, 0xE12B1647, + 0x1B80, 0xE1231655, + 0x1B80, 0xE1231657, + 0x1B80, 0xE1271665, + 0x1B80, 0xE1271667, + 0x1B80, 0x54801675, + 0x1B80, 0x54801677, + 0x1B80, 0xE1721685, + 0x1B80, 0xE1721687, + 0x1B80, 0xBF031695, + 0x1B80, 0xBF031697, + 0x1B80, 0x000216A5, + 0x1B80, 0x000216A7, + 0x1B80, 0x302816B5, + 0x1B80, 0x302816B7, + 0x1B80, 0x54BF16C5, + 0x1B80, 0x54BF16C7, + 0x1B80, 0x54C516D5, + 0x1B80, 0x54C516D7, + 0x1B80, 0x050A16E5, + 0x1B80, 0x050A16E7, + 0x1B80, 0x071416F5, + 0x1B80, 0x071416F7, + 0x1B80, 0x54DF1705, + 0x1B80, 0x54DF1707, + 0x1B80, 0x00011715, + 0x1B80, 0x00011717, + 0x1B80, 0x54BF1725, + 0x1B80, 0x54BF1727, + 0x1B80, 0x54C01735, + 0x1B80, 0x54C01737, + 0x1B80, 0x54A31745, + 0x1B80, 0x54A31747, + 0x1B80, 0x54C11755, + 0x1B80, 0x54C11757, + 0x1B80, 0x54A41765, + 0x1B80, 0x54A41767, + 0x1B80, 0x4C831775, + 0x1B80, 0x4C831777, + 0x1B80, 0x4C031785, + 0x1B80, 0x4C031787, + 0x1B80, 0xBF0B1795, + 0x1B80, 0xBF0B1797, + 0x1B80, 0x54C217A5, + 0x1B80, 0x54C217A7, + 0x1B80, 0x54A417B5, + 0x1B80, 0x54A417B7, + 0x1B80, 0x4C8517C5, + 0x1B80, 0x4C8517C7, + 0x1B80, 0x4C0517D5, + 0x1B80, 0x4C0517D7, + 0x1B80, 0xBF0617E5, + 0x1B80, 0xBF0617E7, + 0x1B80, 0x54C117F5, + 0x1B80, 0x54C117F7, + 0x1B80, 0x54A31805, + 0x1B80, 0x54A31807, + 0x1B80, 0x4C861815, + 0x1B80, 0x4C861817, + 0x1B80, 0x4C061825, + 0x1B80, 0x4C061827, + 0x1B80, 0xBF011835, + 0x1B80, 0xBF011837, + 0x1B80, 0x54DF1845, + 0x1B80, 0x54DF1847, + 0x1B80, 0x00011855, + 0x1B80, 0x00011857, + 0x1B80, 0x00071865, + 0x1B80, 0x00071867, + 0x1B80, 0x54011875, + 0x1B80, 0x54011877, + 0x1B80, 0x00041885, + 0x1B80, 0x00041887, + 0x1B80, 0x56001895, + 0x1B80, 0x56001897, + 0x1B80, 0x5CF218A5, + 0x1B80, 0x5CF218A7, + 0x1B80, 0x630718B5, + 0x1B80, 0x630718B7, + 0x1B80, 0x620418C5, + 0x1B80, 0x620418C7, + 0x1B80, 0x610018D5, + 0x1B80, 0x610018D7, + 0x1B80, 0x670718E5, + 0x1B80, 0x670718E7, + 0x1B80, 0x660618F5, + 0x1B80, 0x660618F7, + 0x1B80, 0x6F201905, + 0x1B80, 0x6F201907, + 0x1B80, 0x6E001915, + 0x1B80, 0x6E001917, + 0x1B80, 0x6D001925, + 0x1B80, 0x6D001927, + 0x1B80, 0x6C031935, + 0x1B80, 0x6C031937, + 0x1B80, 0x73201945, + 0x1B80, 0x73201947, + 0x1B80, 0x72001955, + 0x1B80, 0x72001957, + 0x1B80, 0x71001965, + 0x1B80, 0x71001967, + 0x1B80, 0x7B201975, + 0x1B80, 0x7B201977, + 0x1B80, 0x7A001985, + 0x1B80, 0x7A001987, + 0x1B80, 0x79001995, + 0x1B80, 0x79001997, + 0x1B80, 0x7F2019A5, + 0x1B80, 0x7F2019A7, + 0x1B80, 0x7E0019B5, + 0x1B80, 0x7E0019B7, + 0x1B80, 0x7D0019C5, + 0x1B80, 0x7D0019C7, + 0x1B80, 0x090119D5, + 0x1B80, 0x090119D7, + 0x1B80, 0x0AC619E5, + 0x1B80, 0x0AC619E7, + 0x1B80, 0x0BA619F5, + 0x1B80, 0x0BA619F7, + 0x1B80, 0x0C011A05, + 0x1B80, 0x0C011A07, + 0x1B80, 0x0D021A15, + 0x1B80, 0x0D021A17, + 0x1B80, 0x0E041A25, + 0x1B80, 0x0E041A27, + 0x1B80, 0x0FFF1A35, + 0x1B80, 0x0FFF1A37, + 0x1B80, 0x4D041A45, + 0x1B80, 0x4D041A47, + 0x1B80, 0x28F81A55, + 0x1B80, 0x28F81A57, + 0x1B80, 0xE0001A65, + 0x1B80, 0xE0001A67, + 0x1B80, 0x4D001A75, + 0x1B80, 0x4D001A77, + 0x1B80, 0x00011A85, + 0x1B80, 0x00011A87, + 0x1B80, 0x4D041A95, + 0x1B80, 0x4D041A97, + 0x1B80, 0x2EF81AA5, + 0x1B80, 0x2EF81AA7, + 0x1B80, 0x00021AB5, + 0x1B80, 0x00021AB7, + 0x1B80, 0x23031AC5, + 0x1B80, 0x23031AC7, + 0x1B80, 0x00001AD5, + 0x1B80, 0x00001AD7, + 0x1B80, 0x23131AE5, + 0x1B80, 0x23131AE7, + 0x1B80, 0xE77F1AF5, + 0x1B80, 0xE77F1AF7, + 0x1B80, 0x232F1B05, + 0x1B80, 0x232F1B07, + 0x1B80, 0xEFBF1B15, + 0x1B80, 0xEFBF1B17, + 0x1B80, 0x2EF01B25, + 0x1B80, 0x2EF01B27, + 0x1B80, 0x00021B35, + 0x1B80, 0x00021B37, + 0x1B80, 0x4D001B45, + 0x1B80, 0x4D001B47, + 0x1B80, 0x00011B55, + 0x1B80, 0x00011B57, + 0x1B80, 0x4D041B65, + 0x1B80, 0x4D041B67, + 0x1B80, 0x2EF81B75, + 0x1B80, 0x2EF81B77, + 0x1B80, 0x00021B85, + 0x1B80, 0x00021B87, + 0x1B80, 0x23031B95, + 0x1B80, 0x23031B97, + 0x1B80, 0x00001BA5, + 0x1B80, 0x00001BA7, + 0x1B80, 0x23131BB5, + 0x1B80, 0x23131BB7, + 0x1B80, 0xE77F1BC5, + 0x1B80, 0xE77F1BC7, + 0x1B80, 0x232F1BD5, + 0x1B80, 0x232F1BD7, + 0x1B80, 0xE79F1BE5, + 0x1B80, 0xE79F1BE7, + 0x1B80, 0x2EF01BF5, + 0x1B80, 0x2EF01BF7, + 0x1B80, 0x00021C05, + 0x1B80, 0x00021C07, + 0x1B80, 0x28F81C15, + 0x1B80, 0x28F81C17, + 0x1B80, 0x80001C25, + 0x1B80, 0x80001C27, + 0x1B80, 0x4D001C35, + 0x1B80, 0x4D001C37, + 0x1B80, 0x00011C45, + 0x1B80, 0x00011C47, + 0x1B80, 0x00041C55, + 0x1B80, 0x00041C57, + 0x1B80, 0x6BC01C65, + 0x1B80, 0x6BC01C67, + 0x1B80, 0x4D041C75, + 0x1B80, 0x4D041C77, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0xA0000000, 0x00000000, + 0x1B80, 0x68481C85, + 0x1B80, 0x68481C87, + 0xB0000000, 0x00000000, + 0x1B80, 0x66061C95, + 0x1B80, 0x66061C97, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0xA0000000, 0x00000000, + 0x1B80, 0x65041CA5, + 0x1B80, 0x65041CA7, + 0xB0000000, 0x00000000, + 0x1B80, 0x64471CB5, + 0x1B80, 0x64471CB7, + 0x1B80, 0x23411CC5, + 0x1B80, 0x23411CC7, + 0x1B80, 0x100E1CD5, + 0x1B80, 0x100E1CD7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0xA0000000, 0x00000000, + 0x1B80, 0x60011CE5, + 0x1B80, 0x60011CE7, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411CF5, + 0x1B80, 0x23411CF7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0xA0000000, 0x00000000, + 0x1B80, 0x60611D05, + 0x1B80, 0x60611D07, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411D15, + 0x1B80, 0x23411D17, + 0x1B80, 0x70E11D25, + 0x1B80, 0x70E11D27, + 0x1B80, 0x4D001D35, + 0x1B80, 0x4D001D37, + 0x1B80, 0x00011D45, + 0x1B80, 0x00011D47, + 0x1B80, 0x00041D55, + 0x1B80, 0x00041D57, + 0x1B80, 0x6B401D65, + 0x1B80, 0x6B401D67, + 0x1B80, 0x4D041D75, + 0x1B80, 0x4D041D77, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0xA0000000, 0x00000000, + 0x1B80, 0x68481D85, + 0x1B80, 0x68481D87, + 0xB0000000, 0x00000000, + 0x1B80, 0x66061D95, + 0x1B80, 0x66061D97, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0xA0000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0xA0000000, 0x00000000, + 0x1B80, 0x64471DB5, + 0x1B80, 0x64471DB7, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411DC5, + 0x1B80, 0x23411DC7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0xA0000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0xB0000000, 0x00000000, + 0x1B80, 0x60011DE5, + 0x1B80, 0x60011DE7, + 0x1B80, 0x23411DF5, + 0x1B80, 0x23411DF7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0xA0000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411E15, + 0x1B80, 0x23411E17, + 0x1B80, 0x70611E25, + 0x1B80, 0x70611E27, + 0x1B80, 0x4D001E35, + 0x1B80, 0x4D001E37, + 0x1B80, 0x00011E45, + 0x1B80, 0x00011E47, + 0x1B80, 0x00001E55, + 0x1B80, 0x00001E57, + 0x1B80, 0x00001E65, + 0x1B80, 0x00001E67, + 0x1B80, 0x00001E75, + 0x1B80, 0x00001E77, + 0x1B80, 0x00001E85, + 0x1B80, 0x00001E87, + 0x1B80, 0x00001E95, + 0x1B80, 0x00001E97, + 0x1B80, 0x00001EA5, + 0x1B80, 0x00001EA7, + 0x1B80, 0x00001EB5, + 0x1B80, 0x00001EB7, + 0x1B80, 0x00001EC5, + 0x1B80, 0x00001EC7, + 0x1B80, 0x00001ED5, + 0x1B80, 0x00001ED7, + 0x1B80, 0x00001EE5, + 0x1B80, 0x00001EE7, + 0x1B80, 0x00001EF5, + 0x1B80, 0x00001EF7, + 0x1B80, 0x00001F05, + 0x1B80, 0x00001F07, + 0x1B80, 0x00001F15, + 0x1B80, 0x00001F17, + 0x1B80, 0x00001F25, + 0x1B80, 0x00001F27, + 0x1B80, 0x00001F35, + 0x1B80, 0x00001F37, + 0x1B80, 0x00001F45, + 0x1B80, 0x00001F47, + 0x1B80, 0x00001F55, + 0x1B80, 0x00001F57, + 0x1B80, 0x00001F65, + 0x1B80, 0x00001F67, + 0x1B80, 0x00001F75, + 0x1B80, 0x00001F77, + 0x1B80, 0x00001F85, + 0x1B80, 0x00001F87, + 0x1B80, 0x00001F95, + 0x1B80, 0x00001F97, + 0x1B80, 0x00001FA5, + 0x1B80, 0x00001FA7, + 0x1B80, 0x00001FB5, + 0x1B80, 0x00001FB7, + 0x1B80, 0x00001FC5, + 0x1B80, 0x00001FC7, + 0x1B80, 0x00001FD5, + 0x1B80, 0x00001FD7, + 0x1B80, 0x00001FE5, + 0x1B80, 0x00001FE7, + 0x1B80, 0x00001FF5, + 0x1B80, 0x00001FF7, + 0x1B80, 0x00000006, + 0x1B80, 0x00000002, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_bb, rtw_phy_cfg_bb); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type0[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x28303232, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x26283032, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x30303030, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x26283032, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x30302224, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x28303030, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x28303232, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x26283032, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x30303030, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x26283032, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x30302224, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x28303030, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type0); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type2[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type2); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type3[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x42444646, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x46463840, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x46464646, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x42444646, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463840, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type3); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type4[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x36384042, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x34363840, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x42424242, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x34363840, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x34363840, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x42423032, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x38404242, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x36384042, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x34363840, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x42424242, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x34363840, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x42423032, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x38404242, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type4); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type5[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x44444040, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38384042, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203636, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x44444040, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38384042, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203636, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x44444444, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x40424444, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x44444040, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x44444444, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38384042, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20203636, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x44444040, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38384042, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203636, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x44443840, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x36384042, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203436, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x44443840, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x36384042, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203436, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x44444444, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x40424444, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x44443840, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x44444444, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x36384042, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20203436, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x44443840, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x36384042, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203436, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type5); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type7[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x34343434, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x28303234, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x34342426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x32343434, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x34343434, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x28303234, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x34342426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x32343434, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type7); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type8[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x35373941, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x33353739, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x43434343, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x31333537, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x43434343, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x29313335, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x43434343, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x43434343, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x35373941, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x41434343, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x33353739, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x39414141, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x31333537, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x37393939, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x29313335, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x43434343, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x43434343, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x35373941, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x41434343, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x33353739, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x39414141, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x31333537, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x37393939, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x29313335, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x43434343, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x43434343, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x35373941, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x41434343, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x33353739, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x39414141, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x31333537, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x37393939, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x29313335, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x39414345, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x38404244, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x36384042, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x38404244, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463738, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x35373840, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x37394143, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x33333335, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x39414345, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x38404244, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x36384042, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x38404244, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463738, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x35373840, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x37394143, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x33333335, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x39414345, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x38404244, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x36384042, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x38404244, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463738, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x35373840, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x37394143, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x33333335, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x39414345, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x38404244, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x36384042, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x38404244, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463738, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x35373840, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x37394143, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x33333335, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type8); + +static const u32 rtw8814a_rf_a[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x0B0, 0x000FFFFE, + 0x0B1, 0x0003FF48, + 0x0B2, 0x0006AA3F, + 0x0B3, 0x000FFC9A, + 0x0B4, 0x0000A78F, + 0x0B5, 0x00000A3F, + 0x0B6, 0x0000C09C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0xA0000000, 0x00000000, + 0x0B7, 0x0003000C, + 0xB0000000, 0x00000000, + 0x0B8, 0x0007400E, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0xA0000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0xB0000000, 0x00000000, + 0x0BA, 0x00050780, + 0x0BB, 0x00000000, + 0x0BC, 0x00040009, + 0x0BD, 0x00000000, + 0x0BE, 0x00000000, + 0x0BF, 0x00000000, + 0x0EF, 0x00020000, + 0x03E, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x03E, 0x00020000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00040000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00040000, + 0xA0000000, 0x00000000, + 0x03F, 0x00040000, + 0xB0000000, 0x00000000, + 0x03E, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x03E, 0x00060000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00010000, + 0x03E, 0x00000000, + 0x03F, 0x00006800, + 0x03E, 0x00000080, + 0x03F, 0x00006000, + 0x03E, 0x00000100, + 0x03F, 0x00004800, + 0x03E, 0x00000180, + 0x03F, 0x00004000, + 0x03E, 0x00000200, + 0x03F, 0x00004000, + 0x03E, 0x00000280, + 0x03F, 0x00002800, + 0x03E, 0x00000300, + 0x03F, 0x00002800, + 0x03E, 0x00000380, + 0x03F, 0x00002000, + 0x0EF, 0x00000000, + 0x0EF, 0x00040000, + 0x03E, 0x00000000, + 0x03F, 0x000000BC, + 0x03E, 0x00000040, + 0x03F, 0x00000053, + 0x03E, 0x00000050, + 0x03F, 0x00000050, + 0x03E, 0x00000060, + 0x03F, 0x00000050, + 0x0EF, 0x00000000, + 0x0EF, 0x00000400, + 0x03E, 0x00000006, + 0x041, 0x000EE080, + 0x03E, 0x00000008, + 0x041, 0x000EE0C0, + 0x03E, 0x0000000A, + 0x041, 0x000EE100, + 0x03E, 0x0000000C, + 0x041, 0x000EE100, + 0x0EF, 0x00000000, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00028000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0000118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000AC000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00040000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x0004C000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0004138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0004078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0008C000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0xA0000000, 0x00000000, + 0x03C, 0x00004000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000801, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001801, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000003, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001001, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A1AD, + 0x034, 0x000491AA, + 0x034, 0x000481A7, + 0x034, 0x000470AA, + 0x034, 0x000460A7, + 0x034, 0x00045049, + 0x034, 0x00044046, + 0x034, 0x00043026, + 0x034, 0x00042009, + 0x034, 0x00041006, + 0x034, 0x00040003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F2, + 0x034, 0x000483B0, + 0x034, 0x00047370, + 0x034, 0x0004636D, + 0x034, 0x0004536A, + 0x034, 0x00044349, + 0x034, 0x0004316A, + 0x034, 0x00042167, + 0x034, 0x00041129, + 0x034, 0x00040049, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF1, + 0x034, 0x00049FEE, + 0x034, 0x00048FEB, + 0x034, 0x00047FE8, + 0x034, 0x00046DEA, + 0x034, 0x00045DE7, + 0x034, 0x00044CEA, + 0x034, 0x00043CE7, + 0x034, 0x00042C69, + 0x034, 0x00041C66, + 0x034, 0x00040C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A1AD, + 0x034, 0x000291AA, + 0x034, 0x000281A7, + 0x034, 0x000270AA, + 0x034, 0x000260A7, + 0x034, 0x00025049, + 0x034, 0x00024046, + 0x034, 0x00023026, + 0x034, 0x00022009, + 0x034, 0x00021006, + 0x034, 0x00020003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F2, + 0x034, 0x000282F1, + 0x034, 0x000272B0, + 0x034, 0x000262AD, + 0x034, 0x000252AA, + 0x034, 0x000242A7, + 0x034, 0x000230EC, + 0x034, 0x000220E9, + 0x034, 0x0002106A, + 0x034, 0x00020067, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF1, + 0x034, 0x00029FEE, + 0x034, 0x00028FEB, + 0x034, 0x00027FE8, + 0x034, 0x00026DEA, + 0x034, 0x00025DE7, + 0x034, 0x00024CEA, + 0x034, 0x00023CE7, + 0x034, 0x00022C69, + 0x034, 0x00021C66, + 0x034, 0x00020C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x0000938C, + 0x034, 0x000081AD, + 0x034, 0x000071AA, + 0x034, 0x000061A7, + 0x034, 0x000050AA, + 0x034, 0x000040A7, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x0000100C, + 0x034, 0x00000009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F4, + 0x034, 0x000093F1, + 0x034, 0x000082B1, + 0x034, 0x000071D1, + 0x034, 0x000061CE, + 0x034, 0x000051CB, + 0x034, 0x000041C8, + 0x034, 0x000030CB, + 0x034, 0x000020C8, + 0x034, 0x00001087, + 0x034, 0x00000084, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF1, + 0x034, 0x00009FEE, + 0x034, 0x00008FEB, + 0x034, 0x00007FE8, + 0x034, 0x00006DEA, + 0x034, 0x00005DE7, + 0x034, 0x00004CEA, + 0x034, 0x00003CE7, + 0x034, 0x00002C69, + 0x034, 0x00001C66, + 0x034, 0x00000C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA1AD, + 0x034, 0x000C91AA, + 0x034, 0x000C81A7, + 0x034, 0x000C70AA, + 0x034, 0x000C60A7, + 0x034, 0x000C5049, + 0x034, 0x000C4046, + 0x034, 0x000C3026, + 0x034, 0x000C2009, + 0x034, 0x000C1006, + 0x034, 0x000C0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F2, + 0x034, 0x000C83B0, + 0x034, 0x000C7370, + 0x034, 0x000C636D, + 0x034, 0x000C536A, + 0x034, 0x000C4349, + 0x034, 0x000C316A, + 0x034, 0x000C2167, + 0x034, 0x000C1129, + 0x034, 0x000C0049, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA1AD, + 0x034, 0x000A91AA, + 0x034, 0x000A81A7, + 0x034, 0x000A70AA, + 0x034, 0x000A60A7, + 0x034, 0x000A5049, + 0x034, 0x000A4046, + 0x034, 0x000A3026, + 0x034, 0x000A2009, + 0x034, 0x000A1006, + 0x034, 0x000A0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F2, + 0x034, 0x000A82F1, + 0x034, 0x000A72B0, + 0x034, 0x000A62AD, + 0x034, 0x000A52AA, + 0x034, 0x000A42A7, + 0x034, 0x000A30EC, + 0x034, 0x000A20E9, + 0x034, 0x000A106A, + 0x034, 0x000A0067, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x0008938C, + 0x034, 0x000881AD, + 0x034, 0x000871AA, + 0x034, 0x000861A7, + 0x034, 0x000850AA, + 0x034, 0x000840A7, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x0008100C, + 0x034, 0x00080009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F4, + 0x034, 0x000893F1, + 0x034, 0x000882B1, + 0x034, 0x000871D1, + 0x034, 0x000861CE, + 0x034, 0x000851CB, + 0x034, 0x000841C8, + 0x034, 0x000830CB, + 0x034, 0x000820C8, + 0x034, 0x00081087, + 0x034, 0x00080084, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0DF, 0x00000001, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000747, + 0x035, 0x00008747, + 0x035, 0x00010747, + 0x035, 0x00020747, + 0x035, 0x00028747, + 0x035, 0x00030747, + 0x035, 0x00040747, + 0x035, 0x00048747, + 0x035, 0x00050747, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0DF, 0x00000001, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x00005146, + 0x037, 0x00004000, + 0x038, 0x00005146, + 0x037, 0x00008000, + 0x038, 0x00005146, + 0x037, 0x00010000, + 0x038, 0x00005146, + 0x037, 0x00014000, + 0x038, 0x00005146, + 0x037, 0x00018000, + 0x038, 0x00004D4E, + 0x037, 0x0001C000, + 0x038, 0x00004D4E, + 0x037, 0x00020000, + 0x038, 0x00004D4E, + 0x037, 0x00024000, + 0x038, 0x000071C6, + 0x037, 0x00028000, + 0x038, 0x000071C6, + 0x037, 0x0002C000, + 0x038, 0x000071C6, + 0x037, 0x00030000, + 0x038, 0x000071CE, + 0x037, 0x00034000, + 0x038, 0x000071CE, + 0x037, 0x00038000, + 0x038, 0x00005126, + 0x037, 0x0003C000, + 0x038, 0x00005126, + 0x037, 0x00040000, + 0x038, 0x00005126, + 0x037, 0x00044000, + 0x038, 0x00005126, + 0x037, 0x00048000, + 0x038, 0x00005126, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000933FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, + 0x01C, 0x000739D2, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0xA0000000, 0x00000000, + 0x01F, 0x0002255C, + 0xB0000000, 0x00000000, + 0x0B1, 0x0007FF48, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0xA0000000, 0x00000000, + 0x0C4, 0x00083F00, + 0xB0000000, 0x00000000, + 0x018, 0x0001B126, + 0xFFE, 0x00000000, + 0xFFE, 0x00000000, + 0xFFE, 0x00000000, + 0x018, 0x00013126, + 0x018, 0x00013124, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_a, A); + +static const u32 rtw8814a_rf_b[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F39B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F39B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00040000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00000F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x03B, 0x0004078B, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x000491AD, + 0x034, 0x000481AA, + 0x034, 0x000471A7, + 0x034, 0x000460AA, + 0x034, 0x000450A7, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x0004200C, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38B, + 0x034, 0x00049388, + 0x034, 0x0004818B, + 0x034, 0x00047188, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38B, + 0x034, 0x00049388, + 0x034, 0x0004818B, + 0x034, 0x00047188, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F3, + 0x034, 0x000483B2, + 0x034, 0x00047390, + 0x034, 0x0004638D, + 0x034, 0x0004538A, + 0x034, 0x00044387, + 0x034, 0x0004324A, + 0x034, 0x00042247, + 0x034, 0x0004104D, + 0x034, 0x0004004A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043DEA, + 0x034, 0x00042DE7, + 0x034, 0x00041DE4, + 0x034, 0x00040CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A38C, + 0x034, 0x000291AD, + 0x034, 0x000281AA, + 0x034, 0x000271A7, + 0x034, 0x000260AA, + 0x034, 0x000250A7, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x0002200C, + 0x034, 0x00021009, + 0x034, 0x00020006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AD, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AD, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F3, + 0x034, 0x000283D0, + 0x034, 0x00027371, + 0x034, 0x0002636E, + 0x034, 0x0002536B, + 0x034, 0x00024368, + 0x034, 0x0002332A, + 0x034, 0x00022327, + 0x034, 0x0002104C, + 0x034, 0x00020049, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020F25, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A38C, + 0x034, 0x000091AD, + 0x034, 0x000081AA, + 0x034, 0x000071A7, + 0x034, 0x000060AA, + 0x034, 0x000050A7, + 0x034, 0x0000402C, + 0x034, 0x00003029, + 0x034, 0x00002026, + 0x034, 0x00001009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093AD, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093AD, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F4, + 0x034, 0x000093F0, + 0x034, 0x000083AE, + 0x034, 0x00007350, + 0x034, 0x0000634D, + 0x034, 0x0000534A, + 0x034, 0x00004347, + 0x034, 0x0000312D, + 0x034, 0x0000212A, + 0x034, 0x00001127, + 0x034, 0x0000002A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF4, + 0x034, 0x00008FF1, + 0x034, 0x00007FEE, + 0x034, 0x00006FEB, + 0x034, 0x00005FE8, + 0x034, 0x00004DEB, + 0x034, 0x00003DE8, + 0x034, 0x00002DE5, + 0x034, 0x00001C8B, + 0x034, 0x00000C88, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C91AD, + 0x034, 0x000C81AA, + 0x034, 0x000C71A7, + 0x034, 0x000C60AA, + 0x034, 0x000C50A7, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C200C, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38B, + 0x034, 0x000C9388, + 0x034, 0x000C818B, + 0x034, 0x000C7188, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38B, + 0x034, 0x000C9388, + 0x034, 0x000C818B, + 0x034, 0x000C7188, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F3, + 0x034, 0x000C83B2, + 0x034, 0x000C7390, + 0x034, 0x000C638D, + 0x034, 0x000C538A, + 0x034, 0x000C4387, + 0x034, 0x000C324A, + 0x034, 0x000C2247, + 0x034, 0x000C104D, + 0x034, 0x000C004A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3DEA, + 0x034, 0x000C2DE7, + 0x034, 0x000C1DE4, + 0x034, 0x000C0CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA38C, + 0x034, 0x000A91AD, + 0x034, 0x000A81AA, + 0x034, 0x000A71A7, + 0x034, 0x000A60AA, + 0x034, 0x000A50A7, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A200C, + 0x034, 0x000A1009, + 0x034, 0x000A0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AD, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AD, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F3, + 0x034, 0x000A83D0, + 0x034, 0x000A7371, + 0x034, 0x000A636E, + 0x034, 0x000A536B, + 0x034, 0x000A4368, + 0x034, 0x000A332A, + 0x034, 0x000A2327, + 0x034, 0x000A104C, + 0x034, 0x000A0049, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0F25, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A38C, + 0x034, 0x000891AD, + 0x034, 0x000881AA, + 0x034, 0x000871A7, + 0x034, 0x000860AA, + 0x034, 0x000850A7, + 0x034, 0x0008402C, + 0x034, 0x00083029, + 0x034, 0x00082026, + 0x034, 0x00081009, + 0x034, 0x00080006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893AD, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893AD, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F4, + 0x034, 0x000893F0, + 0x034, 0x000883AE, + 0x034, 0x00087350, + 0x034, 0x0008634D, + 0x034, 0x0008534A, + 0x034, 0x00084347, + 0x034, 0x0008312D, + 0x034, 0x0008212A, + 0x034, 0x00081127, + 0x034, 0x0008002A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF4, + 0x034, 0x00088FF1, + 0x034, 0x00087FEE, + 0x034, 0x00086FEB, + 0x034, 0x00085FE8, + 0x034, 0x00084DEB, + 0x034, 0x00083DE8, + 0x034, 0x00082DE5, + 0x034, 0x00081C8B, + 0x034, 0x00080C88, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_b, B); + +static const u32 rtw8814a_rf_c[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0006C000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000D4000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0006C000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0008C000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x000A0000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0000118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000D0000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0xA0000000, 0x00000000, + 0x03C, 0x00028000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x03B, 0x0004078B, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008128B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0xA0000000, 0x00000000, + 0x018, 0x00013124, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x000491AD, + 0x034, 0x000481AA, + 0x034, 0x000471A7, + 0x034, 0x000460AA, + 0x034, 0x000450A7, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x0004200C, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F3, + 0x034, 0x00048393, + 0x034, 0x00047390, + 0x034, 0x0004638D, + 0x034, 0x0004538A, + 0x034, 0x00044387, + 0x034, 0x000430ED, + 0x034, 0x000420EA, + 0x034, 0x000410E7, + 0x034, 0x0004002D, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043CD0, + 0x034, 0x00042CCD, + 0x034, 0x00041CCA, + 0x034, 0x00040CC7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002938C, + 0x034, 0x000281AD, + 0x034, 0x000271AA, + 0x034, 0x000261A7, + 0x034, 0x000250AA, + 0x034, 0x000240A7, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x0002100C, + 0x034, 0x00020009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F3, + 0x034, 0x000282F2, + 0x034, 0x000272D0, + 0x034, 0x000262CD, + 0x034, 0x000252CA, + 0x034, 0x000242C7, + 0x034, 0x000230CD, + 0x034, 0x000220CA, + 0x034, 0x000210C7, + 0x034, 0x00020086, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020E44, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A38C, + 0x034, 0x000091AD, + 0x034, 0x000081AA, + 0x034, 0x000071A7, + 0x034, 0x000060AA, + 0x034, 0x000050A7, + 0x034, 0x0000402C, + 0x034, 0x00003029, + 0x034, 0x0000200C, + 0x034, 0x00001009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F5, + 0x034, 0x000093F1, + 0x034, 0x000083B0, + 0x034, 0x00007370, + 0x034, 0x0000636D, + 0x034, 0x0000536A, + 0x034, 0x00004367, + 0x034, 0x0000308E, + 0x034, 0x0000208B, + 0x034, 0x00001088, + 0x034, 0x00000085, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF5, + 0x034, 0x00008FF2, + 0x034, 0x00007FEF, + 0x034, 0x00006FEC, + 0x034, 0x00005FE9, + 0x034, 0x00004EAA, + 0x034, 0x00003EA7, + 0x034, 0x00002C70, + 0x034, 0x00001C6D, + 0x034, 0x00000C6A, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C91AD, + 0x034, 0x000C81AA, + 0x034, 0x000C71A7, + 0x034, 0x000C60AA, + 0x034, 0x000C50A7, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C200C, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F3, + 0x034, 0x000C8393, + 0x034, 0x000C7390, + 0x034, 0x000C638D, + 0x034, 0x000C538A, + 0x034, 0x000C4387, + 0x034, 0x000C30ED, + 0x034, 0x000C20EA, + 0x034, 0x000C10E7, + 0x034, 0x000C002D, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3CD0, + 0x034, 0x000C2CCD, + 0x034, 0x000C1CCA, + 0x034, 0x000C0CC7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A938C, + 0x034, 0x000A81AD, + 0x034, 0x000A71AA, + 0x034, 0x000A61A7, + 0x034, 0x000A50AA, + 0x034, 0x000A40A7, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A100C, + 0x034, 0x000A0009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F3, + 0x034, 0x000A82F2, + 0x034, 0x000A72D0, + 0x034, 0x000A62CD, + 0x034, 0x000A52CA, + 0x034, 0x000A42C7, + 0x034, 0x000A30CD, + 0x034, 0x000A20CA, + 0x034, 0x000A10C7, + 0x034, 0x000A0086, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0E44, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A38C, + 0x034, 0x000891AD, + 0x034, 0x000881AA, + 0x034, 0x000871A7, + 0x034, 0x000860AA, + 0x034, 0x000850A7, + 0x034, 0x0008402C, + 0x034, 0x00083029, + 0x034, 0x0008200C, + 0x034, 0x00081009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F5, + 0x034, 0x000893F1, + 0x034, 0x000883B0, + 0x034, 0x00087370, + 0x034, 0x0008636D, + 0x034, 0x0008536A, + 0x034, 0x00084367, + 0x034, 0x0008308E, + 0x034, 0x0008208B, + 0x034, 0x00081088, + 0x034, 0x00080085, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF5, + 0x034, 0x00088FF2, + 0x034, 0x00087FEF, + 0x034, 0x00086FEC, + 0x034, 0x00085FE9, + 0x034, 0x00084EAA, + 0x034, 0x00083EA7, + 0x034, 0x00082C70, + 0x034, 0x00081C6D, + 0x034, 0x00080C6A, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000541, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001541, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002541, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_c, C); From 11733e0d904e3150de2e232773388456be94e358 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 329/465] wifi: rtw88: Add rtw8814a_table.c (part 2/2) JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e38246889cc9f8497c8d7413b73856ae1634322d Author: Bitterblue Smith Date: Fri Mar 7 02:24:02 2025 +0200 wifi: rtw88: Add rtw8814a_table.c (part 2/2) This contains various tables for initialising the RTL8814A, plus TX power limits. Also add rtw8814a_table.h. Split into two patches because they are big. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/4c48e35e-1b04-42ed-940e-0e931693def6@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/realtek/rtw88/rtw8814a_table.c | 11379 ++++++++++++++++ .../wireless/realtek/rtw88/rtw8814a_table.h | 40 + 2 files changed, 11419 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814a_table.h diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c index 907cbd032178..b9ce51a09fc8 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c @@ -12549,3 +12549,11382 @@ static const u32 rtw8814a_rf_c[] = { }; RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_c, C); + +static const u32 rtw8814a_rf_d[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F09B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F09B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00040000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00048000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00000F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0xA0000000, 0x00000000, + 0x03C, 0x00024000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00040F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0004078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0xA0000000, 0x00000000, + 0x03C, 0x00004000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001002, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000001, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EB, + 0x034, 0x0004938B, + 0x034, 0x000481AC, + 0x034, 0x000471A9, + 0x034, 0x000460AC, + 0x034, 0x000450A9, + 0x034, 0x0004402E, + 0x034, 0x0004302B, + 0x034, 0x00042028, + 0x034, 0x0004100B, + 0x034, 0x00040008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F4, + 0x034, 0x000493D2, + 0x034, 0x000482D1, + 0x034, 0x000471F1, + 0x034, 0x000461EE, + 0x034, 0x000451EB, + 0x034, 0x000441E8, + 0x034, 0x0004314B, + 0x034, 0x00042148, + 0x034, 0x0004104B, + 0x034, 0x00040048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043CB1, + 0x034, 0x00042CAE, + 0x034, 0x00041CAB, + 0x034, 0x00040CA8, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293EB, + 0x034, 0x0002838B, + 0x034, 0x000271AC, + 0x034, 0x000261A9, + 0x034, 0x000250AC, + 0x034, 0x000240A9, + 0x034, 0x000230A6, + 0x034, 0x0002202C, + 0x034, 0x00021029, + 0x034, 0x00020026, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293D2, + 0x034, 0x000283CE, + 0x034, 0x00027290, + 0x034, 0x0002628D, + 0x034, 0x0002528A, + 0x034, 0x00024287, + 0x034, 0x0002308D, + 0x034, 0x0002208A, + 0x034, 0x00021087, + 0x034, 0x00020048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093EC, + 0x034, 0x0000838C, + 0x034, 0x000071AD, + 0x034, 0x000061AA, + 0x034, 0x000050AD, + 0x034, 0x000040AA, + 0x034, 0x0000306A, + 0x034, 0x0000202D, + 0x034, 0x0000102A, + 0x034, 0x00000027, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F1, + 0x034, 0x000092B1, + 0x034, 0x000081CF, + 0x034, 0x00007170, + 0x034, 0x0000616D, + 0x034, 0x0000516A, + 0x034, 0x00004167, + 0x034, 0x0000302F, + 0x034, 0x0000202C, + 0x034, 0x00001029, + 0x034, 0x00000026, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF6, + 0x034, 0x00008FF3, + 0x034, 0x00007FF0, + 0x034, 0x00006FED, + 0x034, 0x00005FEA, + 0x034, 0x00004FE7, + 0x034, 0x00003EC7, + 0x034, 0x00002EC4, + 0x034, 0x00001D4B, + 0x034, 0x00000D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EB, + 0x034, 0x000C938B, + 0x034, 0x000C81AC, + 0x034, 0x000C71A9, + 0x034, 0x000C60AC, + 0x034, 0x000C50A9, + 0x034, 0x000C402E, + 0x034, 0x000C302B, + 0x034, 0x000C2028, + 0x034, 0x000C100B, + 0x034, 0x000C0008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F4, + 0x034, 0x000C93D2, + 0x034, 0x000C82D1, + 0x034, 0x000C71F1, + 0x034, 0x000C61EE, + 0x034, 0x000C51EB, + 0x034, 0x000C41E8, + 0x034, 0x000C314B, + 0x034, 0x000C2148, + 0x034, 0x000C104B, + 0x034, 0x000C0048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3CB1, + 0x034, 0x000C2CAE, + 0x034, 0x000C1CAB, + 0x034, 0x000C0CA8, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93EB, + 0x034, 0x000A838B, + 0x034, 0x000A71AC, + 0x034, 0x000A61A9, + 0x034, 0x000A50AC, + 0x034, 0x000A40A9, + 0x034, 0x000A30A6, + 0x034, 0x000A202C, + 0x034, 0x000A1029, + 0x034, 0x000A0026, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93D2, + 0x034, 0x000A83CE, + 0x034, 0x000A7290, + 0x034, 0x000A628D, + 0x034, 0x000A528A, + 0x034, 0x000A4287, + 0x034, 0x000A308D, + 0x034, 0x000A208A, + 0x034, 0x000A1087, + 0x034, 0x000A0048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893EC, + 0x034, 0x0008838C, + 0x034, 0x000871AD, + 0x034, 0x000861AA, + 0x034, 0x000850AD, + 0x034, 0x000840AA, + 0x034, 0x0008306A, + 0x034, 0x0008202D, + 0x034, 0x0008102A, + 0x034, 0x00080027, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F1, + 0x034, 0x000892B1, + 0x034, 0x000881CF, + 0x034, 0x00087170, + 0x034, 0x0008616D, + 0x034, 0x0008516A, + 0x034, 0x00084167, + 0x034, 0x0008302F, + 0x034, 0x0008202C, + 0x034, 0x00081029, + 0x034, 0x00080026, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF6, + 0x034, 0x00088FF3, + 0x034, 0x00087FF0, + 0x034, 0x00086FED, + 0x034, 0x00085FEA, + 0x034, 0x00084FE7, + 0x034, 0x00083EC7, + 0x034, 0x00082EC4, + 0x034, 0x00081D4B, + 0x034, 0x00080D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000275, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x00001275, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x00002275, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_d, D); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt[] = { + { 0, 0, 0, 0, 1, 36, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 36, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 36, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 36, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 36, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 36, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 36, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 36, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 36, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 36, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 36, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 34, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 36, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 36, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 36, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 36, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 36, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 36, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 36, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 36, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 36, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 32, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 34, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 36, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 36, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 36, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 36, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 36, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 36, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 36, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 36, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 36, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 32, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 34, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 34, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 34, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 34, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 34, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 34, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 34, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 34, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 34, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 30, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 30, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 32, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 32, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 32, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 32, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 32, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 32, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 32, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 32, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 32, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 28, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 28, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 30, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 30, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 30, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 30, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 30, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 30, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 30, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 30, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 30, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 26, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 32, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 36, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 36, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 36, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 36, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 36, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 36, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 36, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 32, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 30, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 28, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 32, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 32, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 32, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 32, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 32, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 32, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 32, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 26, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 30, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 30, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 30, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 30, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 30, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 30, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 30, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 26, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 30, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 30, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 30, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 30, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 36, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 34, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 28, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 30, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 30, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 34, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 34, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 36, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 34, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 30, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 30, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 36, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 36, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 36, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 36, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 36, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 30, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 30, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 30, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 30, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 34, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 28, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 30, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 30, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 34, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 34, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 36, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 34, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 30, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 30, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 28, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 36, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 36, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 36, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 36, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 36, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 28, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 28, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 28, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 28, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 34, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 32, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 30, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 26, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 28, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 28, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 30, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 32, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 32, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 34, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 32, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 30, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 28, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 28, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 26, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 34, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 34, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 34, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 34, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 34, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 26, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 26, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 26, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 26, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 32, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 30, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 28, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 24, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 26, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 26, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 28, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 30, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 30, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 30, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 28, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 26, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 26, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 24, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 32, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 32, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 32, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 32, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 32, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 24, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 24, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 24, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 24, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 30, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 28, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 26, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 22, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 24, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 24, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 26, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 28, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 28, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 30, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 28, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 26, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 24, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 24, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 22, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 30, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 30, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 30, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 30, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 30, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 30, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 30, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 32, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 28, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 36, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 34, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 32, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 36, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 36, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 28, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 28, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 30, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 26, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 30, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 34, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 32, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 30, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 34, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 34, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 26, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 26, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 28, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 24, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 28, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 32, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 30, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 28, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 32, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 32, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 24, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 24, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 26, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 22, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 26, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 30, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 28, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 26, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 30, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 30, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 30, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 28, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 30, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 34, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 36, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 28, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 26, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 28, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 32, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 34, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 26, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 24, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 26, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 30, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 32, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 24, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 22, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 24, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 28, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 30, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type0[] = { + { 0, 0, 0, 0, 1, 32, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 32, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 32, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 32, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 32, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 32, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 32, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 32, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 32, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 32, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 32, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 24, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 16, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 28, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 32, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 32, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 32, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 32, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 32, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 32, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 32, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 32, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 32, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 28, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 18, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 8, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 26, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 32, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 32, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 32, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 32, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 32, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 32, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 32, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 32, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 32, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 26, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 16, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 6, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 24, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 30, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 30, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 30, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 30, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 30, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 30, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 30, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 30, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 30, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 24, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 14, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 4, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 22, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 28, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 28, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 28, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 28, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 28, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 28, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 28, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 28, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 28, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 22, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 14, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 4, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 20, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 26, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 26, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 26, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 26, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 26, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 26, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 26, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 26, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 26, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 20, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 14, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 4, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 26, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 32, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 32, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 32, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 32, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 32, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 32, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 32, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 26, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 16, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 10, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 24, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 30, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 30, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 30, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 30, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 30, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 30, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 30, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 24, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 14, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 8, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 22, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 28, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 28, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 28, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 28, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 28, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 28, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 28, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 22, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 14, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 8, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 20, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 26, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 26, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 26, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 26, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 26, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 26, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 26, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 20, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 14, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 8, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 28, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 32, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 32, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 32, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 32, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 32, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 28, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 28, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 32, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 32, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 32, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 32, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 32, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 32, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 32, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 28, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 32, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 32, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 32, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 32, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 26, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 32, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 32, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 26, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 26, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 26, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 26, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 32, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 32, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 32, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 32, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 24, }, + { 2, 1, 0, 3, 36, 28, }, + { 1, 1, 0, 3, 36, 28, }, + { 0, 1, 0, 3, 40, 28, }, + { 2, 1, 0, 3, 40, 28, }, + { 1, 1, 0, 3, 40, 28, }, + { 0, 1, 0, 3, 44, 28, }, + { 2, 1, 0, 3, 44, 28, }, + { 1, 1, 0, 3, 44, 28, }, + { 0, 1, 0, 3, 48, 28, }, + { 2, 1, 0, 3, 48, 28, }, + { 1, 1, 0, 3, 48, 28, }, + { 0, 1, 0, 3, 52, 28, }, + { 2, 1, 0, 3, 52, 28, }, + { 1, 1, 0, 3, 52, 28, }, + { 0, 1, 0, 3, 56, 28, }, + { 2, 1, 0, 3, 56, 28, }, + { 1, 1, 0, 3, 56, 28, }, + { 0, 1, 0, 3, 60, 28, }, + { 2, 1, 0, 3, 60, 28, }, + { 1, 1, 0, 3, 60, 28, }, + { 0, 1, 0, 3, 64, 24, }, + { 2, 1, 0, 3, 64, 28, }, + { 1, 1, 0, 3, 64, 28, }, + { 0, 1, 0, 3, 100, 24, }, + { 2, 1, 0, 3, 100, 28, }, + { 1, 1, 0, 3, 100, 28, }, + { 0, 1, 0, 3, 104, 28, }, + { 2, 1, 0, 3, 104, 28, }, + { 1, 1, 0, 3, 104, 28, }, + { 0, 1, 0, 3, 108, 28, }, + { 2, 1, 0, 3, 108, 28, }, + { 1, 1, 0, 3, 108, 28, }, + { 0, 1, 0, 3, 112, 28, }, + { 2, 1, 0, 3, 112, 28, }, + { 1, 1, 0, 3, 112, 28, }, + { 0, 1, 0, 3, 116, 28, }, + { 2, 1, 0, 3, 116, 28, }, + { 1, 1, 0, 3, 116, 28, }, + { 0, 1, 0, 3, 120, 28, }, + { 2, 1, 0, 3, 120, 28, }, + { 1, 1, 0, 3, 120, 28, }, + { 0, 1, 0, 3, 124, 28, }, + { 2, 1, 0, 3, 124, 28, }, + { 1, 1, 0, 3, 124, 28, }, + { 0, 1, 0, 3, 128, 28, }, + { 2, 1, 0, 3, 128, 28, }, + { 1, 1, 0, 3, 128, 28, }, + { 0, 1, 0, 3, 132, 28, }, + { 2, 1, 0, 3, 132, 28, }, + { 1, 1, 0, 3, 132, 28, }, + { 0, 1, 0, 3, 136, 28, }, + { 2, 1, 0, 3, 136, 28, }, + { 1, 1, 0, 3, 136, 28, }, + { 0, 1, 0, 3, 140, 24, }, + { 2, 1, 0, 3, 140, 28, }, + { 1, 1, 0, 3, 140, 28, }, + { 0, 1, 0, 3, 149, 24, }, + { 2, 1, 0, 3, 149, 28, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 28, }, + { 2, 1, 0, 3, 153, 28, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 28, }, + { 2, 1, 0, 3, 157, 28, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 28, }, + { 2, 1, 0, 3, 161, 28, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 28, }, + { 2, 1, 0, 3, 165, 28, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 22, }, + { 2, 1, 0, 6, 36, 26, }, + { 1, 1, 0, 6, 36, 26, }, + { 0, 1, 0, 6, 40, 26, }, + { 2, 1, 0, 6, 40, 26, }, + { 1, 1, 0, 6, 40, 26, }, + { 0, 1, 0, 6, 44, 26, }, + { 2, 1, 0, 6, 44, 26, }, + { 1, 1, 0, 6, 44, 26, }, + { 0, 1, 0, 6, 48, 26, }, + { 2, 1, 0, 6, 48, 26, }, + { 1, 1, 0, 6, 48, 26, }, + { 0, 1, 0, 6, 52, 26, }, + { 2, 1, 0, 6, 52, 26, }, + { 1, 1, 0, 6, 52, 26, }, + { 0, 1, 0, 6, 56, 26, }, + { 2, 1, 0, 6, 56, 26, }, + { 1, 1, 0, 6, 56, 26, }, + { 0, 1, 0, 6, 60, 26, }, + { 2, 1, 0, 6, 60, 26, }, + { 1, 1, 0, 6, 60, 26, }, + { 0, 1, 0, 6, 64, 22, }, + { 2, 1, 0, 6, 64, 26, }, + { 1, 1, 0, 6, 64, 26, }, + { 0, 1, 0, 6, 100, 22, }, + { 2, 1, 0, 6, 100, 26, }, + { 1, 1, 0, 6, 100, 26, }, + { 0, 1, 0, 6, 104, 26, }, + { 2, 1, 0, 6, 104, 26, }, + { 1, 1, 0, 6, 104, 26, }, + { 0, 1, 0, 6, 108, 26, }, + { 2, 1, 0, 6, 108, 26, }, + { 1, 1, 0, 6, 108, 26, }, + { 0, 1, 0, 6, 112, 26, }, + { 2, 1, 0, 6, 112, 26, }, + { 1, 1, 0, 6, 112, 26, }, + { 0, 1, 0, 6, 116, 26, }, + { 2, 1, 0, 6, 116, 26, }, + { 1, 1, 0, 6, 116, 26, }, + { 0, 1, 0, 6, 120, 26, }, + { 2, 1, 0, 6, 120, 26, }, + { 1, 1, 0, 6, 120, 26, }, + { 0, 1, 0, 6, 124, 26, }, + { 2, 1, 0, 6, 124, 26, }, + { 1, 1, 0, 6, 124, 26, }, + { 0, 1, 0, 6, 128, 26, }, + { 2, 1, 0, 6, 128, 26, }, + { 1, 1, 0, 6, 128, 26, }, + { 0, 1, 0, 6, 132, 26, }, + { 2, 1, 0, 6, 132, 26, }, + { 1, 1, 0, 6, 132, 26, }, + { 0, 1, 0, 6, 136, 26, }, + { 2, 1, 0, 6, 136, 26, }, + { 1, 1, 0, 6, 136, 26, }, + { 0, 1, 0, 6, 140, 22, }, + { 2, 1, 0, 6, 140, 26, }, + { 1, 1, 0, 6, 140, 26, }, + { 0, 1, 0, 6, 149, 22, }, + { 2, 1, 0, 6, 149, 26, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 26, }, + { 2, 1, 0, 6, 153, 26, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 26, }, + { 2, 1, 0, 6, 157, 26, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 26, }, + { 2, 1, 0, 6, 161, 26, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 26, }, + { 2, 1, 0, 6, 165, 26, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 20, }, + { 2, 1, 0, 7, 36, 24, }, + { 1, 1, 0, 7, 36, 24, }, + { 0, 1, 0, 7, 40, 24, }, + { 2, 1, 0, 7, 40, 24, }, + { 1, 1, 0, 7, 40, 24, }, + { 0, 1, 0, 7, 44, 24, }, + { 2, 1, 0, 7, 44, 24, }, + { 1, 1, 0, 7, 44, 24, }, + { 0, 1, 0, 7, 48, 24, }, + { 2, 1, 0, 7, 48, 24, }, + { 1, 1, 0, 7, 48, 24, }, + { 0, 1, 0, 7, 52, 24, }, + { 2, 1, 0, 7, 52, 24, }, + { 1, 1, 0, 7, 52, 24, }, + { 0, 1, 0, 7, 56, 24, }, + { 2, 1, 0, 7, 56, 24, }, + { 1, 1, 0, 7, 56, 24, }, + { 0, 1, 0, 7, 60, 24, }, + { 2, 1, 0, 7, 60, 24, }, + { 1, 1, 0, 7, 60, 24, }, + { 0, 1, 0, 7, 64, 20, }, + { 2, 1, 0, 7, 64, 24, }, + { 1, 1, 0, 7, 64, 24, }, + { 0, 1, 0, 7, 100, 20, }, + { 2, 1, 0, 7, 100, 24, }, + { 1, 1, 0, 7, 100, 24, }, + { 0, 1, 0, 7, 104, 24, }, + { 2, 1, 0, 7, 104, 24, }, + { 1, 1, 0, 7, 104, 24, }, + { 0, 1, 0, 7, 108, 24, }, + { 2, 1, 0, 7, 108, 24, }, + { 1, 1, 0, 7, 108, 24, }, + { 0, 1, 0, 7, 112, 24, }, + { 2, 1, 0, 7, 112, 24, }, + { 1, 1, 0, 7, 112, 24, }, + { 0, 1, 0, 7, 116, 24, }, + { 2, 1, 0, 7, 116, 24, }, + { 1, 1, 0, 7, 116, 24, }, + { 0, 1, 0, 7, 120, 24, }, + { 2, 1, 0, 7, 120, 24, }, + { 1, 1, 0, 7, 120, 24, }, + { 0, 1, 0, 7, 124, 24, }, + { 2, 1, 0, 7, 124, 24, }, + { 1, 1, 0, 7, 124, 24, }, + { 0, 1, 0, 7, 128, 24, }, + { 2, 1, 0, 7, 128, 24, }, + { 1, 1, 0, 7, 128, 24, }, + { 0, 1, 0, 7, 132, 24, }, + { 2, 1, 0, 7, 132, 24, }, + { 1, 1, 0, 7, 132, 24, }, + { 0, 1, 0, 7, 136, 24, }, + { 2, 1, 0, 7, 136, 24, }, + { 1, 1, 0, 7, 136, 24, }, + { 0, 1, 0, 7, 140, 20, }, + { 2, 1, 0, 7, 140, 24, }, + { 1, 1, 0, 7, 140, 24, }, + { 0, 1, 0, 7, 149, 20, }, + { 2, 1, 0, 7, 149, 24, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 24, }, + { 2, 1, 0, 7, 153, 24, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 24, }, + { 2, 1, 0, 7, 157, 24, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 24, }, + { 2, 1, 0, 7, 161, 24, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 24, }, + { 2, 1, 0, 7, 165, 24, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 26, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 26, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 26, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 32, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 26, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 24, }, + { 2, 1, 1, 3, 38, 28, }, + { 1, 1, 1, 3, 38, 28, }, + { 0, 1, 1, 3, 46, 28, }, + { 2, 1, 1, 3, 46, 28, }, + { 1, 1, 1, 3, 46, 28, }, + { 0, 1, 1, 3, 54, 28, }, + { 2, 1, 1, 3, 54, 28, }, + { 1, 1, 1, 3, 54, 28, }, + { 0, 1, 1, 3, 62, 24, }, + { 2, 1, 1, 3, 62, 28, }, + { 1, 1, 1, 3, 62, 28, }, + { 0, 1, 1, 3, 102, 24, }, + { 2, 1, 1, 3, 102, 28, }, + { 1, 1, 1, 3, 102, 28, }, + { 0, 1, 1, 3, 110, 28, }, + { 2, 1, 1, 3, 110, 28, }, + { 1, 1, 1, 3, 110, 28, }, + { 0, 1, 1, 3, 118, 28, }, + { 2, 1, 1, 3, 118, 28, }, + { 1, 1, 1, 3, 118, 28, }, + { 0, 1, 1, 3, 126, 28, }, + { 2, 1, 1, 3, 126, 28, }, + { 1, 1, 1, 3, 126, 28, }, + { 0, 1, 1, 3, 134, 28, }, + { 2, 1, 1, 3, 134, 28, }, + { 1, 1, 1, 3, 134, 28, }, + { 0, 1, 1, 3, 151, 24, }, + { 2, 1, 1, 3, 151, 28, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 28, }, + { 2, 1, 1, 3, 159, 28, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 20, }, + { 2, 1, 1, 6, 38, 26, }, + { 1, 1, 1, 6, 38, 26, }, + { 0, 1, 1, 6, 46, 26, }, + { 2, 1, 1, 6, 46, 26, }, + { 1, 1, 1, 6, 46, 26, }, + { 0, 1, 1, 6, 54, 26, }, + { 2, 1, 1, 6, 54, 26, }, + { 1, 1, 1, 6, 54, 26, }, + { 0, 1, 1, 6, 62, 20, }, + { 2, 1, 1, 6, 62, 26, }, + { 1, 1, 1, 6, 62, 26, }, + { 0, 1, 1, 6, 102, 20, }, + { 2, 1, 1, 6, 102, 26, }, + { 1, 1, 1, 6, 102, 26, }, + { 0, 1, 1, 6, 110, 26, }, + { 2, 1, 1, 6, 110, 26, }, + { 1, 1, 1, 6, 110, 26, }, + { 0, 1, 1, 6, 118, 26, }, + { 2, 1, 1, 6, 118, 26, }, + { 1, 1, 1, 6, 118, 26, }, + { 0, 1, 1, 6, 126, 26, }, + { 2, 1, 1, 6, 126, 26, }, + { 1, 1, 1, 6, 126, 26, }, + { 0, 1, 1, 6, 134, 26, }, + { 2, 1, 1, 6, 134, 26, }, + { 1, 1, 1, 6, 134, 26, }, + { 0, 1, 1, 6, 151, 20, }, + { 2, 1, 1, 6, 151, 26, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 26, }, + { 2, 1, 1, 6, 159, 26, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 18, }, + { 2, 1, 1, 7, 38, 24, }, + { 1, 1, 1, 7, 38, 24, }, + { 0, 1, 1, 7, 46, 24, }, + { 2, 1, 1, 7, 46, 24, }, + { 1, 1, 1, 7, 46, 24, }, + { 0, 1, 1, 7, 54, 24, }, + { 2, 1, 1, 7, 54, 24, }, + { 1, 1, 1, 7, 54, 24, }, + { 0, 1, 1, 7, 62, 18, }, + { 2, 1, 1, 7, 62, 24, }, + { 1, 1, 1, 7, 62, 24, }, + { 0, 1, 1, 7, 102, 18, }, + { 2, 1, 1, 7, 102, 24, }, + { 1, 1, 1, 7, 102, 24, }, + { 0, 1, 1, 7, 110, 24, }, + { 2, 1, 1, 7, 110, 24, }, + { 1, 1, 1, 7, 110, 24, }, + { 0, 1, 1, 7, 118, 24, }, + { 2, 1, 1, 7, 118, 24, }, + { 1, 1, 1, 7, 118, 24, }, + { 0, 1, 1, 7, 126, 24, }, + { 2, 1, 1, 7, 126, 24, }, + { 1, 1, 1, 7, 126, 24, }, + { 0, 1, 1, 7, 134, 24, }, + { 2, 1, 1, 7, 134, 24, }, + { 1, 1, 1, 7, 134, 24, }, + { 0, 1, 1, 7, 151, 18, }, + { 2, 1, 1, 7, 151, 24, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 24, }, + { 2, 1, 1, 7, 159, 24, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 22, }, + { 2, 1, 2, 4, 42, 30, }, + { 1, 1, 2, 4, 42, 30, }, + { 0, 1, 2, 4, 58, 22, }, + { 2, 1, 2, 4, 58, 30, }, + { 1, 1, 2, 4, 58, 30, }, + { 0, 1, 2, 4, 106, 22, }, + { 2, 1, 2, 4, 106, 30, }, + { 1, 1, 2, 4, 106, 30, }, + { 0, 1, 2, 4, 122, 30, }, + { 2, 1, 2, 4, 122, 30, }, + { 1, 1, 2, 4, 122, 30, }, + { 0, 1, 2, 4, 155, 22, }, + { 2, 1, 2, 4, 155, 30, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 20, }, + { 2, 1, 2, 5, 42, 28, }, + { 1, 1, 2, 5, 42, 28, }, + { 0, 1, 2, 5, 58, 20, }, + { 2, 1, 2, 5, 58, 28, }, + { 1, 1, 2, 5, 58, 28, }, + { 0, 1, 2, 5, 106, 20, }, + { 2, 1, 2, 5, 106, 28, }, + { 1, 1, 2, 5, 106, 28, }, + { 0, 1, 2, 5, 122, 28, }, + { 2, 1, 2, 5, 122, 28, }, + { 1, 1, 2, 5, 122, 28, }, + { 0, 1, 2, 5, 155, 20, }, + { 2, 1, 2, 5, 155, 28, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 18, }, + { 2, 1, 2, 8, 42, 26, }, + { 1, 1, 2, 8, 42, 26, }, + { 0, 1, 2, 8, 58, 18, }, + { 2, 1, 2, 8, 58, 26, }, + { 1, 1, 2, 8, 58, 26, }, + { 0, 1, 2, 8, 106, 18, }, + { 2, 1, 2, 8, 106, 26, }, + { 1, 1, 2, 8, 106, 26, }, + { 0, 1, 2, 8, 122, 26, }, + { 2, 1, 2, 8, 122, 26, }, + { 1, 1, 2, 8, 122, 26, }, + { 0, 1, 2, 8, 155, 18, }, + { 2, 1, 2, 8, 155, 26, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 16, }, + { 2, 1, 2, 9, 42, 24, }, + { 1, 1, 2, 9, 42, 24, }, + { 0, 1, 2, 9, 58, 16, }, + { 2, 1, 2, 9, 58, 24, }, + { 1, 1, 2, 9, 58, 24, }, + { 0, 1, 2, 9, 106, 16, }, + { 2, 1, 2, 9, 106, 24, }, + { 1, 1, 2, 9, 106, 24, }, + { 0, 1, 2, 9, 122, 24, }, + { 2, 1, 2, 9, 122, 24, }, + { 1, 1, 2, 9, 122, 24, }, + { 0, 1, 2, 9, 155, 16, }, + { 2, 1, 2, 9, 155, 24, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type0); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type1[] = { + { 0, 0, 0, 0, 1, 34, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 34, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 34, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 34, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 34, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 34, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 34, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 34, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 34, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 34, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 34, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 24, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 16, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 30, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 32, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 32, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 32, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 32, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 32, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 32, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 32, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 32, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 32, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 30, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 18, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 8, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 28, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 32, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 32, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 32, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 32, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 32, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 32, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 32, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 32, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 32, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 28, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 16, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 6, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 26, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 30, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 30, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 30, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 30, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 30, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 30, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 30, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 30, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 30, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 26, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 14, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 4, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 24, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 28, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 28, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 28, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 28, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 28, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 28, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 28, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 28, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 28, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 24, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 14, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 4, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 22, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 26, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 26, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 26, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 26, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 26, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 26, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 26, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 26, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 26, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 22, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 14, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 4, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 28, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 32, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 32, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 32, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 32, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 32, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 32, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 32, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 16, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 10, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 26, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 30, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 30, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 30, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 30, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 30, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 30, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 30, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 26, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 14, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 8, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 24, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 28, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 28, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 28, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 28, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 28, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 28, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 28, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 24, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 14, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 8, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 22, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 26, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 26, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 26, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 26, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 26, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 26, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 26, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 22, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 14, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 8, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 28, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 32, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 32, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 32, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 32, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 32, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 30, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 28, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 32, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 32, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 32, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 32, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 32, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 32, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 32, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 28, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 32, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 32, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 32, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 32, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 28, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 32, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 32, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 30, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 28, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 28, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 28, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 32, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 32, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 32, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 32, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 26, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 30, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 30, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 30, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 30, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 30, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 30, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 28, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 28, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 30, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 30, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 30, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 30, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 30, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 30, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 30, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 30, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 30, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 26, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 26, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 30, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 30, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 30, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 30, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 24, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 28, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 28, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 28, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 28, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 28, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 28, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 26, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 24, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 28, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 28, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 28, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 28, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 28, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 28, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 28, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 28, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 28, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 24, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 24, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 28, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 28, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 28, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 28, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 22, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 26, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 26, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 26, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 26, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 26, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 26, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 24, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 22, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 26, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 26, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 26, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 26, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 26, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 26, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 26, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 26, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 26, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 22, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 22, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 26, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 26, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 26, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 26, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 28, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 28, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 28, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 30, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 28, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 26, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 30, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 26, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 26, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 30, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 30, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 30, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 28, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 26, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 30, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 20, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 28, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 20, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 22, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 28, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 28, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 28, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 26, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 22, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 28, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 18, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 26, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 18, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 20, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 26, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 26, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 26, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 24, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 20, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 26, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 24, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 24, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 24, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 32, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 26, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 22, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 22, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 22, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 30, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 24, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 20, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 20, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 20, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 28, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 20, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 18, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 18, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 18, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 26, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 18, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type1); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type2[] = { + { 0, 0, 0, 0, 1, 42, }, + { 2, 0, 0, 0, 1, 42, }, + { 1, 0, 0, 0, 1, 42, }, + { 0, 0, 0, 0, 2, 50, }, + { 2, 0, 0, 0, 2, 42, }, + { 1, 0, 0, 0, 2, 42, }, + { 0, 0, 0, 0, 3, 50, }, + { 2, 0, 0, 0, 3, 42, }, + { 1, 0, 0, 0, 3, 42, }, + { 0, 0, 0, 0, 4, 50, }, + { 2, 0, 0, 0, 4, 42, }, + { 1, 0, 0, 0, 4, 42, }, + { 0, 0, 0, 0, 5, 50, }, + { 2, 0, 0, 0, 5, 42, }, + { 1, 0, 0, 0, 5, 42, }, + { 0, 0, 0, 0, 6, 50, }, + { 2, 0, 0, 0, 6, 42, }, + { 1, 0, 0, 0, 6, 42, }, + { 0, 0, 0, 0, 7, 50, }, + { 2, 0, 0, 0, 7, 42, }, + { 1, 0, 0, 0, 7, 42, }, + { 0, 0, 0, 0, 8, 50, }, + { 2, 0, 0, 0, 8, 42, }, + { 1, 0, 0, 0, 8, 42, }, + { 0, 0, 0, 0, 9, 50, }, + { 2, 0, 0, 0, 9, 42, }, + { 1, 0, 0, 0, 9, 42, }, + { 0, 0, 0, 0, 10, 50, }, + { 2, 0, 0, 0, 10, 42, }, + { 1, 0, 0, 0, 10, 42, }, + { 0, 0, 0, 0, 11, 44, }, + { 2, 0, 0, 0, 11, 42, }, + { 1, 0, 0, 0, 11, 42, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 42, }, + { 1, 0, 0, 0, 12, 42, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 42, }, + { 1, 0, 0, 0, 13, 42, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 42, }, + { 0, 0, 0, 1, 1, 32, }, + { 2, 0, 0, 1, 1, 42, }, + { 1, 0, 0, 1, 1, 42, }, + { 0, 0, 0, 1, 2, 42, }, + { 2, 0, 0, 1, 2, 42, }, + { 1, 0, 0, 1, 2, 42, }, + { 0, 0, 0, 1, 3, 42, }, + { 2, 0, 0, 1, 3, 42, }, + { 1, 0, 0, 1, 3, 42, }, + { 0, 0, 0, 1, 4, 42, }, + { 2, 0, 0, 1, 4, 42, }, + { 1, 0, 0, 1, 4, 42, }, + { 0, 0, 0, 1, 5, 42, }, + { 2, 0, 0, 1, 5, 42, }, + { 1, 0, 0, 1, 5, 42, }, + { 0, 0, 0, 1, 6, 42, }, + { 2, 0, 0, 1, 6, 42, }, + { 1, 0, 0, 1, 6, 42, }, + { 0, 0, 0, 1, 7, 42, }, + { 2, 0, 0, 1, 7, 42, }, + { 1, 0, 0, 1, 7, 42, }, + { 0, 0, 0, 1, 8, 42, }, + { 2, 0, 0, 1, 8, 42, }, + { 1, 0, 0, 1, 8, 42, }, + { 0, 0, 0, 1, 9, 42, }, + { 2, 0, 0, 1, 9, 42, }, + { 1, 0, 0, 1, 9, 42, }, + { 0, 0, 0, 1, 10, 42, }, + { 2, 0, 0, 1, 10, 42, }, + { 1, 0, 0, 1, 10, 42, }, + { 0, 0, 0, 1, 11, 32, }, + { 2, 0, 0, 1, 11, 42, }, + { 1, 0, 0, 1, 11, 42, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 42, }, + { 1, 0, 0, 1, 12, 42, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 42, }, + { 1, 0, 0, 1, 13, 42, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 32, }, + { 2, 0, 0, 2, 1, 42, }, + { 1, 0, 0, 2, 1, 42, }, + { 0, 0, 0, 2, 2, 40, }, + { 2, 0, 0, 2, 2, 42, }, + { 1, 0, 0, 2, 2, 42, }, + { 0, 0, 0, 2, 3, 40, }, + { 2, 0, 0, 2, 3, 42, }, + { 1, 0, 0, 2, 3, 42, }, + { 0, 0, 0, 2, 4, 40, }, + { 2, 0, 0, 2, 4, 42, }, + { 1, 0, 0, 2, 4, 42, }, + { 0, 0, 0, 2, 5, 40, }, + { 2, 0, 0, 2, 5, 42, }, + { 1, 0, 0, 2, 5, 42, }, + { 0, 0, 0, 2, 6, 40, }, + { 2, 0, 0, 2, 6, 42, }, + { 1, 0, 0, 2, 6, 42, }, + { 0, 0, 0, 2, 7, 40, }, + { 2, 0, 0, 2, 7, 42, }, + { 1, 0, 0, 2, 7, 42, }, + { 0, 0, 0, 2, 8, 40, }, + { 2, 0, 0, 2, 8, 42, }, + { 1, 0, 0, 2, 8, 42, }, + { 0, 0, 0, 2, 9, 40, }, + { 2, 0, 0, 2, 9, 42, }, + { 1, 0, 0, 2, 9, 42, }, + { 0, 0, 0, 2, 10, 40, }, + { 2, 0, 0, 2, 10, 42, }, + { 1, 0, 0, 2, 10, 42, }, + { 0, 0, 0, 2, 11, 28, }, + { 2, 0, 0, 2, 11, 42, }, + { 1, 0, 0, 2, 11, 42, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 42, }, + { 1, 0, 0, 2, 12, 42, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 42, }, + { 1, 0, 0, 2, 13, 42, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 40, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 40, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 40, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 40, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 40, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 40, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 40, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 40, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 40, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 28, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 32, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 40, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 40, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 40, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 40, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 40, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 40, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 40, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 40, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 40, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 28, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 32, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 40, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 40, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 40, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 40, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 40, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 40, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 40, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 40, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 40, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 28, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 30, }, + { 2, 0, 1, 2, 3, 34, }, + { 1, 0, 1, 2, 3, 34, }, + { 0, 0, 1, 2, 4, 34, }, + { 2, 0, 1, 2, 4, 34, }, + { 1, 0, 1, 2, 4, 34, }, + { 0, 0, 1, 2, 5, 34, }, + { 2, 0, 1, 2, 5, 34, }, + { 1, 0, 1, 2, 5, 34, }, + { 0, 0, 1, 2, 6, 34, }, + { 2, 0, 1, 2, 6, 34, }, + { 1, 0, 1, 2, 6, 34, }, + { 0, 0, 1, 2, 7, 34, }, + { 2, 0, 1, 2, 7, 34, }, + { 1, 0, 1, 2, 7, 34, }, + { 0, 0, 1, 2, 8, 34, }, + { 2, 0, 1, 2, 8, 34, }, + { 1, 0, 1, 2, 8, 34, }, + { 0, 0, 1, 2, 9, 34, }, + { 2, 0, 1, 2, 9, 34, }, + { 1, 0, 1, 2, 9, 34, }, + { 0, 0, 1, 2, 10, 34, }, + { 2, 0, 1, 2, 10, 34, }, + { 1, 0, 1, 2, 10, 34, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 34, }, + { 1, 0, 1, 2, 11, 34, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 34, }, + { 1, 0, 1, 2, 12, 34, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 34, }, + { 1, 0, 1, 2, 13, 34, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 34, }, + { 1, 0, 1, 3, 3, 34, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 34, }, + { 1, 0, 1, 3, 4, 34, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 34, }, + { 1, 0, 1, 3, 5, 34, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 34, }, + { 1, 0, 1, 3, 6, 34, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 34, }, + { 1, 0, 1, 3, 7, 34, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 34, }, + { 1, 0, 1, 3, 8, 34, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 34, }, + { 1, 0, 1, 3, 9, 34, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 34, }, + { 1, 0, 1, 3, 10, 34, }, + { 0, 0, 1, 3, 11, 28, }, + { 2, 0, 1, 3, 11, 34, }, + { 1, 0, 1, 3, 11, 34, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 34, }, + { 1, 0, 1, 3, 12, 34, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 34, }, + { 1, 0, 1, 3, 13, 34, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 30, }, + { 2, 0, 1, 6, 3, 34, }, + { 1, 0, 1, 6, 3, 34, }, + { 0, 0, 1, 6, 4, 34, }, + { 2, 0, 1, 6, 4, 34, }, + { 1, 0, 1, 6, 4, 34, }, + { 0, 0, 1, 6, 5, 34, }, + { 2, 0, 1, 6, 5, 34, }, + { 1, 0, 1, 6, 5, 34, }, + { 0, 0, 1, 6, 6, 34, }, + { 2, 0, 1, 6, 6, 34, }, + { 1, 0, 1, 6, 6, 34, }, + { 0, 0, 1, 6, 7, 34, }, + { 2, 0, 1, 6, 7, 34, }, + { 1, 0, 1, 6, 7, 34, }, + { 0, 0, 1, 6, 8, 34, }, + { 2, 0, 1, 6, 8, 34, }, + { 1, 0, 1, 6, 8, 34, }, + { 0, 0, 1, 6, 9, 34, }, + { 2, 0, 1, 6, 9, 34, }, + { 1, 0, 1, 6, 9, 34, }, + { 0, 0, 1, 6, 10, 34, }, + { 2, 0, 1, 6, 10, 34, }, + { 1, 0, 1, 6, 10, 34, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 34, }, + { 1, 0, 1, 6, 11, 34, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 34, }, + { 1, 0, 1, 6, 12, 34, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 34, }, + { 1, 0, 1, 6, 13, 34, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 30, }, + { 2, 0, 1, 7, 3, 34, }, + { 1, 0, 1, 7, 3, 34, }, + { 0, 0, 1, 7, 4, 34, }, + { 2, 0, 1, 7, 4, 34, }, + { 1, 0, 1, 7, 4, 34, }, + { 0, 0, 1, 7, 5, 34, }, + { 2, 0, 1, 7, 5, 34, }, + { 1, 0, 1, 7, 5, 34, }, + { 0, 0, 1, 7, 6, 34, }, + { 2, 0, 1, 7, 6, 34, }, + { 1, 0, 1, 7, 6, 34, }, + { 0, 0, 1, 7, 7, 34, }, + { 2, 0, 1, 7, 7, 34, }, + { 1, 0, 1, 7, 7, 34, }, + { 0, 0, 1, 7, 8, 34, }, + { 2, 0, 1, 7, 8, 34, }, + { 1, 0, 1, 7, 8, 34, }, + { 0, 0, 1, 7, 9, 34, }, + { 2, 0, 1, 7, 9, 34, }, + { 1, 0, 1, 7, 9, 34, }, + { 0, 0, 1, 7, 10, 34, }, + { 2, 0, 1, 7, 10, 34, }, + { 1, 0, 1, 7, 10, 34, }, + { 0, 0, 1, 7, 11, 28, }, + { 2, 0, 1, 7, 11, 34, }, + { 1, 0, 1, 7, 11, 34, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 34, }, + { 1, 0, 1, 7, 12, 34, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 34, }, + { 1, 0, 1, 7, 13, 34, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 42, }, + { 2, 1, 0, 1, 36, 42, }, + { 1, 1, 0, 1, 36, 42, }, + { 0, 1, 0, 1, 40, 42, }, + { 2, 1, 0, 1, 40, 42, }, + { 1, 1, 0, 1, 40, 42, }, + { 0, 1, 0, 1, 44, 42, }, + { 2, 1, 0, 1, 44, 42, }, + { 1, 1, 0, 1, 44, 42, }, + { 0, 1, 0, 1, 48, 42, }, + { 2, 1, 0, 1, 48, 42, }, + { 1, 1, 0, 1, 48, 42, }, + { 0, 1, 0, 1, 52, 42, }, + { 2, 1, 0, 1, 52, 42, }, + { 1, 1, 0, 1, 52, 42, }, + { 0, 1, 0, 1, 56, 42, }, + { 2, 1, 0, 1, 56, 42, }, + { 1, 1, 0, 1, 56, 42, }, + { 0, 1, 0, 1, 60, 42, }, + { 2, 1, 0, 1, 60, 42, }, + { 1, 1, 0, 1, 60, 42, }, + { 0, 1, 0, 1, 64, 42, }, + { 2, 1, 0, 1, 64, 42, }, + { 1, 1, 0, 1, 64, 42, }, + { 0, 1, 0, 1, 100, 42, }, + { 2, 1, 0, 1, 100, 42, }, + { 1, 1, 0, 1, 100, 42, }, + { 0, 1, 0, 1, 104, 42, }, + { 2, 1, 0, 1, 104, 42, }, + { 1, 1, 0, 1, 104, 42, }, + { 0, 1, 0, 1, 108, 42, }, + { 2, 1, 0, 1, 108, 42, }, + { 1, 1, 0, 1, 108, 42, }, + { 0, 1, 0, 1, 112, 42, }, + { 2, 1, 0, 1, 112, 42, }, + { 1, 1, 0, 1, 112, 42, }, + { 0, 1, 0, 1, 116, 42, }, + { 2, 1, 0, 1, 116, 42, }, + { 1, 1, 0, 1, 116, 42, }, + { 0, 1, 0, 1, 120, 42, }, + { 2, 1, 0, 1, 120, 42, }, + { 1, 1, 0, 1, 120, 42, }, + { 0, 1, 0, 1, 124, 42, }, + { 2, 1, 0, 1, 124, 42, }, + { 1, 1, 0, 1, 124, 42, }, + { 0, 1, 0, 1, 128, 42, }, + { 2, 1, 0, 1, 128, 42, }, + { 1, 1, 0, 1, 128, 42, }, + { 0, 1, 0, 1, 132, 42, }, + { 2, 1, 0, 1, 132, 42, }, + { 1, 1, 0, 1, 132, 42, }, + { 0, 1, 0, 1, 136, 42, }, + { 2, 1, 0, 1, 136, 42, }, + { 1, 1, 0, 1, 136, 42, }, + { 0, 1, 0, 1, 140, 40, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 44, }, + { 2, 1, 0, 1, 149, 44, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 44, }, + { 2, 1, 0, 1, 153, 44, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 44, }, + { 2, 1, 0, 1, 157, 44, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 44, }, + { 2, 1, 0, 1, 161, 44, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 44, }, + { 2, 1, 0, 1, 165, 44, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 32, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 36, }, + { 2, 1, 0, 2, 48, 36, }, + { 1, 1, 0, 2, 48, 36, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 36, }, + { 1, 1, 0, 2, 52, 36, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 32, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 32, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 30, }, + { 2, 1, 0, 2, 140, 30, }, + { 1, 1, 0, 2, 140, 30, }, + { 0, 1, 0, 2, 149, 40, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 40, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 40, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 40, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 42, }, + { 2, 1, 0, 2, 165, 42, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 32, }, + { 2, 1, 0, 3, 36, 32, }, + { 1, 1, 0, 3, 36, 32, }, + { 0, 1, 0, 3, 40, 32, }, + { 2, 1, 0, 3, 40, 32, }, + { 1, 1, 0, 3, 40, 32, }, + { 0, 1, 0, 3, 44, 32, }, + { 2, 1, 0, 3, 44, 32, }, + { 1, 1, 0, 3, 44, 32, }, + { 0, 1, 0, 3, 48, 36, }, + { 2, 1, 0, 3, 48, 36, }, + { 1, 1, 0, 3, 48, 36, }, + { 0, 1, 0, 3, 52, 36, }, + { 2, 1, 0, 3, 52, 36, }, + { 1, 1, 0, 3, 52, 36, }, + { 0, 1, 0, 3, 56, 32, }, + { 2, 1, 0, 3, 56, 32, }, + { 1, 1, 0, 3, 56, 32, }, + { 0, 1, 0, 3, 60, 32, }, + { 2, 1, 0, 3, 60, 32, }, + { 1, 1, 0, 3, 60, 32, }, + { 0, 1, 0, 3, 64, 32, }, + { 2, 1, 0, 3, 64, 32, }, + { 1, 1, 0, 3, 64, 32, }, + { 0, 1, 0, 3, 100, 32, }, + { 2, 1, 0, 3, 100, 32, }, + { 1, 1, 0, 3, 100, 32, }, + { 0, 1, 0, 3, 104, 32, }, + { 2, 1, 0, 3, 104, 32, }, + { 1, 1, 0, 3, 104, 32, }, + { 0, 1, 0, 3, 108, 32, }, + { 2, 1, 0, 3, 108, 32, }, + { 1, 1, 0, 3, 108, 32, }, + { 0, 1, 0, 3, 112, 32, }, + { 2, 1, 0, 3, 112, 32, }, + { 1, 1, 0, 3, 112, 32, }, + { 0, 1, 0, 3, 116, 32, }, + { 2, 1, 0, 3, 116, 32, }, + { 1, 1, 0, 3, 116, 32, }, + { 0, 1, 0, 3, 120, 32, }, + { 2, 1, 0, 3, 120, 32, }, + { 1, 1, 0, 3, 120, 32, }, + { 0, 1, 0, 3, 124, 32, }, + { 2, 1, 0, 3, 124, 32, }, + { 1, 1, 0, 3, 124, 32, }, + { 0, 1, 0, 3, 128, 32, }, + { 2, 1, 0, 3, 128, 32, }, + { 1, 1, 0, 3, 128, 32, }, + { 0, 1, 0, 3, 132, 32, }, + { 2, 1, 0, 3, 132, 32, }, + { 1, 1, 0, 3, 132, 32, }, + { 0, 1, 0, 3, 136, 32, }, + { 2, 1, 0, 3, 136, 32, }, + { 1, 1, 0, 3, 136, 32, }, + { 0, 1, 0, 3, 140, 30, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 40, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 40, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 40, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 40, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 42, }, + { 2, 1, 0, 3, 165, 42, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 32, }, + { 2, 1, 0, 6, 36, 32, }, + { 1, 1, 0, 6, 36, 32, }, + { 0, 1, 0, 6, 40, 32, }, + { 2, 1, 0, 6, 40, 32, }, + { 1, 1, 0, 6, 40, 32, }, + { 0, 1, 0, 6, 44, 32, }, + { 2, 1, 0, 6, 44, 32, }, + { 1, 1, 0, 6, 44, 32, }, + { 0, 1, 0, 6, 48, 36, }, + { 2, 1, 0, 6, 48, 36, }, + { 1, 1, 0, 6, 48, 36, }, + { 0, 1, 0, 6, 52, 36, }, + { 2, 1, 0, 6, 52, 36, }, + { 1, 1, 0, 6, 52, 36, }, + { 0, 1, 0, 6, 56, 32, }, + { 2, 1, 0, 6, 56, 32, }, + { 1, 1, 0, 6, 56, 32, }, + { 0, 1, 0, 6, 60, 32, }, + { 2, 1, 0, 6, 60, 32, }, + { 1, 1, 0, 6, 60, 32, }, + { 0, 1, 0, 6, 64, 32, }, + { 2, 1, 0, 6, 64, 32, }, + { 1, 1, 0, 6, 64, 32, }, + { 0, 1, 0, 6, 100, 32, }, + { 2, 1, 0, 6, 100, 32, }, + { 1, 1, 0, 6, 100, 32, }, + { 0, 1, 0, 6, 104, 32, }, + { 2, 1, 0, 6, 104, 32, }, + { 1, 1, 0, 6, 104, 32, }, + { 0, 1, 0, 6, 108, 32, }, + { 2, 1, 0, 6, 108, 32, }, + { 1, 1, 0, 6, 108, 32, }, + { 0, 1, 0, 6, 112, 32, }, + { 2, 1, 0, 6, 112, 32, }, + { 1, 1, 0, 6, 112, 32, }, + { 0, 1, 0, 6, 116, 32, }, + { 2, 1, 0, 6, 116, 32, }, + { 1, 1, 0, 6, 116, 32, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 32, }, + { 1, 1, 0, 6, 120, 32, }, + { 0, 1, 0, 6, 124, 32, }, + { 2, 1, 0, 6, 124, 32, }, + { 1, 1, 0, 6, 124, 32, }, + { 0, 1, 0, 6, 128, 32, }, + { 2, 1, 0, 6, 128, 32, }, + { 1, 1, 0, 6, 128, 32, }, + { 0, 1, 0, 6, 132, 32, }, + { 2, 1, 0, 6, 132, 32, }, + { 1, 1, 0, 6, 132, 32, }, + { 0, 1, 0, 6, 136, 32, }, + { 2, 1, 0, 6, 136, 32, }, + { 1, 1, 0, 6, 136, 32, }, + { 0, 1, 0, 6, 140, 30, }, + { 2, 1, 0, 6, 140, 30, }, + { 1, 1, 0, 6, 140, 30, }, + { 0, 1, 0, 6, 149, 40, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 40, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 40, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 40, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 42, }, + { 2, 1, 0, 6, 165, 42, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 32, }, + { 2, 1, 0, 7, 36, 32, }, + { 1, 1, 0, 7, 36, 32, }, + { 0, 1, 0, 7, 40, 32, }, + { 2, 1, 0, 7, 40, 32, }, + { 1, 1, 0, 7, 40, 32, }, + { 0, 1, 0, 7, 44, 32, }, + { 2, 1, 0, 7, 44, 32, }, + { 1, 1, 0, 7, 44, 32, }, + { 0, 1, 0, 7, 48, 36, }, + { 2, 1, 0, 7, 48, 36, }, + { 1, 1, 0, 7, 48, 36, }, + { 0, 1, 0, 7, 52, 36, }, + { 2, 1, 0, 7, 52, 36, }, + { 1, 1, 0, 7, 52, 36, }, + { 0, 1, 0, 7, 56, 32, }, + { 2, 1, 0, 7, 56, 32, }, + { 1, 1, 0, 7, 56, 32, }, + { 0, 1, 0, 7, 60, 32, }, + { 2, 1, 0, 7, 60, 32, }, + { 1, 1, 0, 7, 60, 32, }, + { 0, 1, 0, 7, 64, 32, }, + { 2, 1, 0, 7, 64, 32, }, + { 1, 1, 0, 7, 64, 32, }, + { 0, 1, 0, 7, 100, 32, }, + { 2, 1, 0, 7, 100, 32, }, + { 1, 1, 0, 7, 100, 32, }, + { 0, 1, 0, 7, 104, 32, }, + { 2, 1, 0, 7, 104, 32, }, + { 1, 1, 0, 7, 104, 32, }, + { 0, 1, 0, 7, 108, 32, }, + { 2, 1, 0, 7, 108, 32, }, + { 1, 1, 0, 7, 108, 32, }, + { 0, 1, 0, 7, 112, 32, }, + { 2, 1, 0, 7, 112, 32, }, + { 1, 1, 0, 7, 112, 32, }, + { 0, 1, 0, 7, 116, 32, }, + { 2, 1, 0, 7, 116, 32, }, + { 1, 1, 0, 7, 116, 32, }, + { 0, 1, 0, 7, 120, 32, }, + { 2, 1, 0, 7, 120, 32, }, + { 1, 1, 0, 7, 120, 32, }, + { 0, 1, 0, 7, 124, 32, }, + { 2, 1, 0, 7, 124, 32, }, + { 1, 1, 0, 7, 124, 32, }, + { 0, 1, 0, 7, 128, 32, }, + { 2, 1, 0, 7, 128, 32, }, + { 1, 1, 0, 7, 128, 32, }, + { 0, 1, 0, 7, 132, 32, }, + { 2, 1, 0, 7, 132, 32, }, + { 1, 1, 0, 7, 132, 32, }, + { 0, 1, 0, 7, 136, 32, }, + { 2, 1, 0, 7, 136, 32, }, + { 1, 1, 0, 7, 136, 32, }, + { 0, 1, 0, 7, 140, 30, }, + { 2, 1, 0, 7, 140, 30, }, + { 1, 1, 0, 7, 140, 30, }, + { 0, 1, 0, 7, 149, 40, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 40, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 40, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 40, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 42, }, + { 2, 1, 0, 7, 165, 42, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 32, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 36, }, + { 2, 1, 1, 2, 46, 36, }, + { 1, 1, 1, 2, 46, 36, }, + { 0, 1, 1, 2, 54, 36, }, + { 2, 1, 1, 2, 54, 36, }, + { 1, 1, 1, 2, 54, 36, }, + { 0, 1, 1, 2, 62, 32, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 30, }, + { 2, 1, 1, 2, 102, 30, }, + { 1, 1, 1, 2, 102, 30, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 36, }, + { 2, 1, 1, 2, 134, 36, }, + { 1, 1, 1, 2, 134, 36, }, + { 0, 1, 1, 2, 151, 36, }, + { 2, 1, 1, 2, 151, 36, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 40, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 32, }, + { 2, 1, 1, 3, 38, 32, }, + { 1, 1, 1, 3, 38, 32, }, + { 0, 1, 1, 3, 46, 36, }, + { 2, 1, 1, 3, 46, 36, }, + { 1, 1, 1, 3, 46, 36, }, + { 0, 1, 1, 3, 54, 36, }, + { 2, 1, 1, 3, 54, 36, }, + { 1, 1, 1, 3, 54, 36, }, + { 0, 1, 1, 3, 62, 32, }, + { 2, 1, 1, 3, 62, 32, }, + { 1, 1, 1, 3, 62, 32, }, + { 0, 1, 1, 3, 102, 30, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 32, }, + { 2, 1, 1, 3, 110, 32, }, + { 1, 1, 1, 3, 110, 32, }, + { 0, 1, 1, 3, 118, 32, }, + { 2, 1, 1, 3, 118, 32, }, + { 1, 1, 1, 3, 118, 32, }, + { 0, 1, 1, 3, 126, 32, }, + { 2, 1, 1, 3, 126, 32, }, + { 1, 1, 1, 3, 126, 32, }, + { 0, 1, 1, 3, 134, 36, }, + { 2, 1, 1, 3, 134, 36, }, + { 1, 1, 1, 3, 134, 36, }, + { 0, 1, 1, 3, 151, 36, }, + { 2, 1, 1, 3, 151, 36, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 40, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 32, }, + { 2, 1, 1, 6, 38, 32, }, + { 1, 1, 1, 6, 38, 32, }, + { 0, 1, 1, 6, 46, 36, }, + { 2, 1, 1, 6, 46, 36, }, + { 1, 1, 1, 6, 46, 36, }, + { 0, 1, 1, 6, 54, 36, }, + { 2, 1, 1, 6, 54, 36, }, + { 1, 1, 1, 6, 54, 36, }, + { 0, 1, 1, 6, 62, 32, }, + { 2, 1, 1, 6, 62, 32, }, + { 1, 1, 1, 6, 62, 32, }, + { 0, 1, 1, 6, 102, 30, }, + { 2, 1, 1, 6, 102, 30, }, + { 1, 1, 1, 6, 102, 30, }, + { 0, 1, 1, 6, 110, 32, }, + { 2, 1, 1, 6, 110, 32, }, + { 1, 1, 1, 6, 110, 32, }, + { 0, 1, 1, 6, 118, 32, }, + { 2, 1, 1, 6, 118, 32, }, + { 1, 1, 1, 6, 118, 32, }, + { 0, 1, 1, 6, 126, 32, }, + { 2, 1, 1, 6, 126, 32, }, + { 1, 1, 1, 6, 126, 32, }, + { 0, 1, 1, 6, 134, 36, }, + { 2, 1, 1, 6, 134, 36, }, + { 1, 1, 1, 6, 134, 36, }, + { 0, 1, 1, 6, 151, 36, }, + { 2, 1, 1, 6, 151, 36, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 40, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 32, }, + { 2, 1, 1, 7, 38, 32, }, + { 1, 1, 1, 7, 38, 32, }, + { 0, 1, 1, 7, 46, 36, }, + { 2, 1, 1, 7, 46, 36, }, + { 1, 1, 1, 7, 46, 36, }, + { 0, 1, 1, 7, 54, 36, }, + { 2, 1, 1, 7, 54, 36, }, + { 1, 1, 1, 7, 54, 36, }, + { 0, 1, 1, 7, 62, 32, }, + { 2, 1, 1, 7, 62, 32, }, + { 1, 1, 1, 7, 62, 32, }, + { 0, 1, 1, 7, 102, 30, }, + { 2, 1, 1, 7, 102, 30, }, + { 1, 1, 1, 7, 102, 30, }, + { 0, 1, 1, 7, 110, 32, }, + { 2, 1, 1, 7, 110, 32, }, + { 1, 1, 1, 7, 110, 32, }, + { 0, 1, 1, 7, 118, 32, }, + { 2, 1, 1, 7, 118, 32, }, + { 1, 1, 1, 7, 118, 32, }, + { 0, 1, 1, 7, 126, 32, }, + { 2, 1, 1, 7, 126, 32, }, + { 1, 1, 1, 7, 126, 32, }, + { 0, 1, 1, 7, 134, 36, }, + { 2, 1, 1, 7, 134, 36, }, + { 1, 1, 1, 7, 134, 36, }, + { 0, 1, 1, 7, 151, 36, }, + { 2, 1, 1, 7, 151, 36, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 40, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 34, }, + { 2, 1, 2, 4, 42, 34, }, + { 1, 1, 2, 4, 42, 34, }, + { 0, 1, 2, 4, 58, 34, }, + { 2, 1, 2, 4, 58, 34, }, + { 1, 1, 2, 4, 58, 34, }, + { 0, 1, 2, 4, 106, 32, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 34, }, + { 2, 1, 2, 4, 122, 34, }, + { 1, 1, 2, 4, 122, 34, }, + { 0, 1, 2, 4, 155, 34, }, + { 2, 1, 2, 4, 155, 34, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 34, }, + { 2, 1, 2, 5, 42, 34, }, + { 1, 1, 2, 5, 42, 34, }, + { 0, 1, 2, 5, 58, 34, }, + { 2, 1, 2, 5, 58, 34, }, + { 1, 1, 2, 5, 58, 34, }, + { 0, 1, 2, 5, 106, 32, }, + { 2, 1, 2, 5, 106, 32, }, + { 1, 1, 2, 5, 106, 32, }, + { 0, 1, 2, 5, 122, 34, }, + { 2, 1, 2, 5, 122, 34, }, + { 1, 1, 2, 5, 122, 34, }, + { 0, 1, 2, 5, 155, 34, }, + { 2, 1, 2, 5, 155, 34, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 34, }, + { 2, 1, 2, 8, 42, 34, }, + { 1, 1, 2, 8, 42, 34, }, + { 0, 1, 2, 8, 58, 34, }, + { 2, 1, 2, 8, 58, 34, }, + { 1, 1, 2, 8, 58, 34, }, + { 0, 1, 2, 8, 106, 32, }, + { 2, 1, 2, 8, 106, 32, }, + { 1, 1, 2, 8, 106, 32, }, + { 0, 1, 2, 8, 122, 34, }, + { 2, 1, 2, 8, 122, 34, }, + { 1, 1, 2, 8, 122, 34, }, + { 0, 1, 2, 8, 155, 34, }, + { 2, 1, 2, 8, 155, 34, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 34, }, + { 2, 1, 2, 9, 42, 34, }, + { 1, 1, 2, 9, 42, 34, }, + { 0, 1, 2, 9, 58, 34, }, + { 2, 1, 2, 9, 58, 34, }, + { 1, 1, 2, 9, 58, 34, }, + { 0, 1, 2, 9, 106, 32, }, + { 2, 1, 2, 9, 106, 32, }, + { 1, 1, 2, 9, 106, 32, }, + { 0, 1, 2, 9, 122, 34, }, + { 2, 1, 2, 9, 122, 34, }, + { 1, 1, 2, 9, 122, 34, }, + { 0, 1, 2, 9, 155, 34, }, + { 2, 1, 2, 9, 155, 34, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type2); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type3[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 40, }, + { 1, 0, 0, 0, 1, 40, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 40, }, + { 1, 0, 0, 0, 2, 40, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 40, }, + { 1, 0, 0, 0, 3, 40, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 40, }, + { 1, 0, 0, 0, 4, 40, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 40, }, + { 1, 0, 0, 0, 5, 40, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 40, }, + { 1, 0, 0, 0, 6, 40, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 40, }, + { 1, 0, 0, 0, 7, 40, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 40, }, + { 1, 0, 0, 0, 8, 40, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 40, }, + { 1, 0, 0, 0, 9, 40, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 40, }, + { 1, 0, 0, 0, 10, 40, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 40, }, + { 1, 0, 0, 0, 11, 40, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 40, }, + { 1, 0, 0, 0, 12, 40, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 40, }, + { 1, 0, 0, 0, 13, 40, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 40, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 40, }, + { 1, 0, 0, 1, 1, 40, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 40, }, + { 1, 0, 0, 1, 2, 40, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 40, }, + { 1, 0, 0, 1, 3, 40, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 40, }, + { 1, 0, 0, 1, 4, 40, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 40, }, + { 1, 0, 0, 1, 5, 40, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 40, }, + { 1, 0, 0, 1, 6, 40, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 40, }, + { 1, 0, 0, 1, 7, 40, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 40, }, + { 1, 0, 0, 1, 8, 40, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 40, }, + { 1, 0, 0, 1, 9, 40, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 40, }, + { 1, 0, 0, 1, 10, 40, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 40, }, + { 1, 0, 0, 1, 11, 40, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 40, }, + { 1, 0, 0, 1, 12, 40, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 40, }, + { 1, 0, 0, 1, 13, 40, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 40, }, + { 1, 0, 0, 2, 1, 40, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 40, }, + { 1, 0, 0, 2, 2, 40, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 40, }, + { 1, 0, 0, 2, 3, 40, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 40, }, + { 1, 0, 0, 2, 4, 40, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 40, }, + { 1, 0, 0, 2, 5, 40, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 40, }, + { 1, 0, 0, 2, 6, 40, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 40, }, + { 1, 0, 0, 2, 7, 40, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 40, }, + { 1, 0, 0, 2, 8, 40, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 40, }, + { 1, 0, 0, 2, 9, 40, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 40, }, + { 1, 0, 0, 2, 10, 40, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 40, }, + { 1, 0, 0, 2, 11, 40, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 40, }, + { 1, 0, 0, 2, 12, 40, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 40, }, + { 1, 0, 0, 2, 13, 40, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 46, }, + { 2, 0, 1, 2, 3, 40, }, + { 1, 0, 1, 2, 3, 40, }, + { 0, 0, 1, 2, 4, 46, }, + { 2, 0, 1, 2, 4, 40, }, + { 1, 0, 1, 2, 4, 40, }, + { 0, 0, 1, 2, 5, 46, }, + { 2, 0, 1, 2, 5, 40, }, + { 1, 0, 1, 2, 5, 40, }, + { 0, 0, 1, 2, 6, 46, }, + { 2, 0, 1, 2, 6, 40, }, + { 1, 0, 1, 2, 6, 40, }, + { 0, 0, 1, 2, 7, 46, }, + { 2, 0, 1, 2, 7, 40, }, + { 1, 0, 1, 2, 7, 40, }, + { 0, 0, 1, 2, 8, 46, }, + { 2, 0, 1, 2, 8, 40, }, + { 1, 0, 1, 2, 8, 40, }, + { 0, 0, 1, 2, 9, 46, }, + { 2, 0, 1, 2, 9, 40, }, + { 1, 0, 1, 2, 9, 40, }, + { 0, 0, 1, 2, 10, 46, }, + { 2, 0, 1, 2, 10, 40, }, + { 1, 0, 1, 2, 10, 40, }, + { 0, 0, 1, 2, 11, 46, }, + { 2, 0, 1, 2, 11, 40, }, + { 1, 0, 1, 2, 11, 40, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 40, }, + { 1, 0, 1, 2, 12, 40, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 40, }, + { 1, 0, 1, 2, 13, 40, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 46, }, + { 2, 0, 1, 3, 3, 40, }, + { 1, 0, 1, 3, 3, 40, }, + { 0, 0, 1, 3, 4, 46, }, + { 2, 0, 1, 3, 4, 40, }, + { 1, 0, 1, 3, 4, 40, }, + { 0, 0, 1, 3, 5, 46, }, + { 2, 0, 1, 3, 5, 40, }, + { 1, 0, 1, 3, 5, 40, }, + { 0, 0, 1, 3, 6, 46, }, + { 2, 0, 1, 3, 6, 40, }, + { 1, 0, 1, 3, 6, 40, }, + { 0, 0, 1, 3, 7, 46, }, + { 2, 0, 1, 3, 7, 40, }, + { 1, 0, 1, 3, 7, 40, }, + { 0, 0, 1, 3, 8, 46, }, + { 2, 0, 1, 3, 8, 40, }, + { 1, 0, 1, 3, 8, 40, }, + { 0, 0, 1, 3, 9, 46, }, + { 2, 0, 1, 3, 9, 40, }, + { 1, 0, 1, 3, 9, 40, }, + { 0, 0, 1, 3, 10, 46, }, + { 2, 0, 1, 3, 10, 40, }, + { 1, 0, 1, 3, 10, 40, }, + { 0, 0, 1, 3, 11, 46, }, + { 2, 0, 1, 3, 11, 40, }, + { 1, 0, 1, 3, 11, 40, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 40, }, + { 1, 0, 1, 3, 12, 40, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 40, }, + { 1, 0, 1, 3, 13, 40, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 46, }, + { 2, 0, 1, 6, 3, 40, }, + { 1, 0, 1, 6, 3, 40, }, + { 0, 0, 1, 6, 4, 46, }, + { 2, 0, 1, 6, 4, 40, }, + { 1, 0, 1, 6, 4, 40, }, + { 0, 0, 1, 6, 5, 46, }, + { 2, 0, 1, 6, 5, 40, }, + { 1, 0, 1, 6, 5, 40, }, + { 0, 0, 1, 6, 6, 46, }, + { 2, 0, 1, 6, 6, 40, }, + { 1, 0, 1, 6, 6, 40, }, + { 0, 0, 1, 6, 7, 46, }, + { 2, 0, 1, 6, 7, 40, }, + { 1, 0, 1, 6, 7, 40, }, + { 0, 0, 1, 6, 8, 46, }, + { 2, 0, 1, 6, 8, 40, }, + { 1, 0, 1, 6, 8, 40, }, + { 0, 0, 1, 6, 9, 46, }, + { 2, 0, 1, 6, 9, 40, }, + { 1, 0, 1, 6, 9, 40, }, + { 0, 0, 1, 6, 10, 46, }, + { 2, 0, 1, 6, 10, 40, }, + { 1, 0, 1, 6, 10, 40, }, + { 0, 0, 1, 6, 11, 46, }, + { 2, 0, 1, 6, 11, 40, }, + { 1, 0, 1, 6, 11, 40, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 40, }, + { 1, 0, 1, 6, 12, 40, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 40, }, + { 1, 0, 1, 6, 13, 40, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 46, }, + { 2, 0, 1, 7, 3, 40, }, + { 1, 0, 1, 7, 3, 40, }, + { 0, 0, 1, 7, 4, 46, }, + { 2, 0, 1, 7, 4, 40, }, + { 1, 0, 1, 7, 4, 40, }, + { 0, 0, 1, 7, 5, 46, }, + { 2, 0, 1, 7, 5, 40, }, + { 1, 0, 1, 7, 5, 40, }, + { 0, 0, 1, 7, 6, 46, }, + { 2, 0, 1, 7, 6, 40, }, + { 1, 0, 1, 7, 6, 40, }, + { 0, 0, 1, 7, 7, 46, }, + { 2, 0, 1, 7, 7, 40, }, + { 1, 0, 1, 7, 7, 40, }, + { 0, 0, 1, 7, 8, 46, }, + { 2, 0, 1, 7, 8, 40, }, + { 1, 0, 1, 7, 8, 40, }, + { 0, 0, 1, 7, 9, 46, }, + { 2, 0, 1, 7, 9, 40, }, + { 1, 0, 1, 7, 9, 40, }, + { 0, 0, 1, 7, 10, 46, }, + { 2, 0, 1, 7, 10, 40, }, + { 1, 0, 1, 7, 10, 40, }, + { 0, 0, 1, 7, 11, 46, }, + { 2, 0, 1, 7, 11, 40, }, + { 1, 0, 1, 7, 11, 40, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 40, }, + { 1, 0, 1, 7, 12, 40, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 40, }, + { 1, 0, 1, 7, 13, 40, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 40, }, + { 1, 1, 0, 1, 36, 40, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 40, }, + { 1, 1, 0, 1, 40, 40, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 40, }, + { 1, 1, 0, 1, 44, 40, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 40, }, + { 1, 1, 0, 1, 48, 40, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 40, }, + { 1, 1, 0, 1, 52, 40, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 40, }, + { 1, 1, 0, 1, 56, 40, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 40, }, + { 1, 1, 0, 1, 60, 40, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 40, }, + { 1, 1, 0, 1, 64, 40, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 40, }, + { 1, 1, 0, 1, 100, 40, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 40, }, + { 1, 1, 0, 1, 104, 40, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 40, }, + { 1, 1, 0, 1, 108, 40, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 40, }, + { 1, 1, 0, 1, 112, 40, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 40, }, + { 1, 1, 0, 1, 116, 40, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 40, }, + { 1, 1, 0, 1, 120, 40, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 40, }, + { 1, 1, 0, 1, 124, 40, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 40, }, + { 1, 1, 0, 1, 128, 40, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 40, }, + { 1, 1, 0, 1, 132, 40, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 40, }, + { 1, 1, 0, 1, 136, 40, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 40, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 40, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 40, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 40, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 40, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 40, }, + { 1, 1, 0, 2, 36, 40, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 40, }, + { 1, 1, 0, 2, 40, 40, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 40, }, + { 1, 1, 0, 2, 44, 40, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 40, }, + { 1, 1, 0, 2, 48, 40, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 40, }, + { 1, 1, 0, 2, 52, 40, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 40, }, + { 1, 1, 0, 2, 56, 40, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 40, }, + { 1, 1, 0, 2, 60, 40, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 40, }, + { 1, 1, 0, 2, 64, 40, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 40, }, + { 1, 1, 0, 2, 100, 40, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 40, }, + { 1, 1, 0, 2, 104, 40, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 40, }, + { 1, 1, 0, 2, 108, 40, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 40, }, + { 1, 1, 0, 2, 112, 40, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 40, }, + { 1, 1, 0, 2, 116, 40, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 40, }, + { 1, 1, 0, 2, 120, 40, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 40, }, + { 1, 1, 0, 2, 124, 40, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 40, }, + { 1, 1, 0, 2, 128, 40, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 40, }, + { 1, 1, 0, 2, 132, 40, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 40, }, + { 1, 1, 0, 2, 136, 40, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 40, }, + { 1, 1, 0, 2, 140, 40, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 40, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 40, }, + { 1, 1, 0, 3, 36, 40, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 40, }, + { 1, 1, 0, 3, 40, 40, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 40, }, + { 1, 1, 0, 3, 44, 40, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 40, }, + { 1, 1, 0, 3, 48, 40, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 40, }, + { 1, 1, 0, 3, 52, 40, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 40, }, + { 1, 1, 0, 3, 56, 40, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 40, }, + { 1, 1, 0, 3, 60, 40, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 40, }, + { 1, 1, 0, 3, 64, 40, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 40, }, + { 1, 1, 0, 3, 100, 40, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 40, }, + { 1, 1, 0, 3, 104, 40, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 40, }, + { 1, 1, 0, 3, 108, 40, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 40, }, + { 1, 1, 0, 3, 112, 40, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 40, }, + { 1, 1, 0, 3, 116, 40, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 40, }, + { 1, 1, 0, 3, 120, 40, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 40, }, + { 1, 1, 0, 3, 124, 40, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 40, }, + { 1, 1, 0, 3, 128, 40, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 40, }, + { 1, 1, 0, 3, 132, 40, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 40, }, + { 1, 1, 0, 3, 136, 40, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 40, }, + { 1, 1, 0, 3, 140, 40, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 40, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 40, }, + { 1, 1, 0, 6, 36, 40, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 40, }, + { 1, 1, 0, 6, 40, 40, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 40, }, + { 1, 1, 0, 6, 44, 40, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 40, }, + { 1, 1, 0, 6, 48, 40, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 40, }, + { 1, 1, 0, 6, 52, 40, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 40, }, + { 1, 1, 0, 6, 56, 40, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 40, }, + { 1, 1, 0, 6, 60, 40, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 40, }, + { 1, 1, 0, 6, 64, 40, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 40, }, + { 1, 1, 0, 6, 100, 40, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 40, }, + { 1, 1, 0, 6, 104, 40, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 40, }, + { 1, 1, 0, 6, 108, 40, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 40, }, + { 1, 1, 0, 6, 112, 40, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 40, }, + { 1, 1, 0, 6, 116, 40, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 40, }, + { 1, 1, 0, 6, 120, 40, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 40, }, + { 1, 1, 0, 6, 124, 40, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 40, }, + { 1, 1, 0, 6, 128, 40, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 40, }, + { 1, 1, 0, 6, 132, 40, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 40, }, + { 1, 1, 0, 6, 136, 40, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 40, }, + { 1, 1, 0, 6, 140, 40, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 40, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 40, }, + { 1, 1, 0, 7, 36, 40, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 40, }, + { 1, 1, 0, 7, 40, 40, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 40, }, + { 1, 1, 0, 7, 44, 40, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 40, }, + { 1, 1, 0, 7, 48, 40, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 40, }, + { 1, 1, 0, 7, 52, 40, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 40, }, + { 1, 1, 0, 7, 56, 40, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 40, }, + { 1, 1, 0, 7, 60, 40, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 40, }, + { 1, 1, 0, 7, 64, 40, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 40, }, + { 1, 1, 0, 7, 100, 40, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 40, }, + { 1, 1, 0, 7, 104, 40, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 40, }, + { 1, 1, 0, 7, 108, 40, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 40, }, + { 1, 1, 0, 7, 112, 40, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 40, }, + { 1, 1, 0, 7, 116, 40, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 40, }, + { 1, 1, 0, 7, 120, 40, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 40, }, + { 1, 1, 0, 7, 124, 40, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 40, }, + { 1, 1, 0, 7, 128, 40, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 40, }, + { 1, 1, 0, 7, 132, 40, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 40, }, + { 1, 1, 0, 7, 136, 40, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 40, }, + { 1, 1, 0, 7, 140, 40, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 40, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 40, }, + { 1, 1, 1, 2, 38, 40, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 40, }, + { 1, 1, 1, 2, 46, 40, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 40, }, + { 1, 1, 1, 2, 54, 40, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 40, }, + { 1, 1, 1, 2, 62, 40, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 40, }, + { 1, 1, 1, 2, 102, 40, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 40, }, + { 1, 1, 1, 2, 110, 40, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 40, }, + { 1, 1, 1, 2, 118, 40, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 40, }, + { 1, 1, 1, 2, 126, 40, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 40, }, + { 1, 1, 1, 2, 134, 40, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 40, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 40, }, + { 1, 1, 1, 3, 38, 40, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 40, }, + { 1, 1, 1, 3, 46, 40, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 40, }, + { 1, 1, 1, 3, 54, 40, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 40, }, + { 1, 1, 1, 3, 62, 40, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 40, }, + { 1, 1, 1, 3, 102, 40, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 40, }, + { 1, 1, 1, 3, 110, 40, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 40, }, + { 1, 1, 1, 3, 118, 40, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 40, }, + { 1, 1, 1, 3, 126, 40, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 40, }, + { 1, 1, 1, 3, 134, 40, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 40, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 40, }, + { 1, 1, 1, 6, 38, 40, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 40, }, + { 1, 1, 1, 6, 46, 40, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 40, }, + { 1, 1, 1, 6, 54, 40, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 40, }, + { 1, 1, 1, 6, 62, 40, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 40, }, + { 1, 1, 1, 6, 102, 40, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 40, }, + { 1, 1, 1, 6, 110, 40, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 40, }, + { 1, 1, 1, 6, 118, 40, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 40, }, + { 1, 1, 1, 6, 126, 40, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 40, }, + { 1, 1, 1, 6, 134, 40, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 40, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 40, }, + { 1, 1, 1, 7, 38, 40, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 40, }, + { 1, 1, 1, 7, 46, 40, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 40, }, + { 1, 1, 1, 7, 54, 40, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 40, }, + { 1, 1, 1, 7, 62, 40, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 40, }, + { 1, 1, 1, 7, 102, 40, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 40, }, + { 1, 1, 1, 7, 110, 40, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 40, }, + { 1, 1, 1, 7, 118, 40, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 40, }, + { 1, 1, 1, 7, 126, 40, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 40, }, + { 1, 1, 1, 7, 134, 40, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 40, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 40, }, + { 1, 1, 2, 4, 42, 40, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 40, }, + { 1, 1, 2, 4, 58, 40, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 40, }, + { 1, 1, 2, 4, 106, 40, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 40, }, + { 1, 1, 2, 4, 122, 40, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 40, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 40, }, + { 1, 1, 2, 5, 42, 40, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 40, }, + { 1, 1, 2, 5, 58, 40, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 40, }, + { 1, 1, 2, 5, 106, 40, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 40, }, + { 1, 1, 2, 5, 122, 40, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 40, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 40, }, + { 1, 1, 2, 8, 42, 40, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 40, }, + { 1, 1, 2, 8, 58, 40, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 40, }, + { 1, 1, 2, 8, 106, 40, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 40, }, + { 1, 1, 2, 8, 122, 40, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 40, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 40, }, + { 1, 1, 2, 9, 42, 40, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 40, }, + { 1, 1, 2, 9, 58, 40, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 40, }, + { 1, 1, 2, 9, 106, 40, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 40, }, + { 1, 1, 2, 9, 122, 40, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 40, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type3); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type5[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 40, }, + { 1, 0, 0, 0, 1, 40, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 40, }, + { 1, 0, 0, 0, 2, 40, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 40, }, + { 1, 0, 0, 0, 3, 40, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 40, }, + { 1, 0, 0, 0, 4, 40, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 40, }, + { 1, 0, 0, 0, 5, 40, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 40, }, + { 1, 0, 0, 0, 6, 40, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 40, }, + { 1, 0, 0, 0, 7, 40, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 40, }, + { 1, 0, 0, 0, 8, 40, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 40, }, + { 1, 0, 0, 0, 9, 40, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 40, }, + { 1, 0, 0, 0, 10, 40, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 40, }, + { 1, 0, 0, 0, 11, 40, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 40, }, + { 1, 0, 0, 0, 12, 40, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 40, }, + { 1, 0, 0, 0, 13, 40, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 40, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 40, }, + { 1, 0, 0, 1, 1, 40, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 40, }, + { 1, 0, 0, 1, 2, 40, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 40, }, + { 1, 0, 0, 1, 3, 40, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 40, }, + { 1, 0, 0, 1, 4, 40, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 40, }, + { 1, 0, 0, 1, 5, 40, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 40, }, + { 1, 0, 0, 1, 6, 40, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 40, }, + { 1, 0, 0, 1, 7, 40, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 40, }, + { 1, 0, 0, 1, 8, 40, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 40, }, + { 1, 0, 0, 1, 9, 40, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 40, }, + { 1, 0, 0, 1, 10, 40, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 40, }, + { 1, 0, 0, 1, 11, 40, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 40, }, + { 1, 0, 0, 1, 12, 40, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 40, }, + { 1, 0, 0, 1, 13, 40, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 40, }, + { 1, 0, 0, 2, 1, 40, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 40, }, + { 1, 0, 0, 2, 2, 40, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 40, }, + { 1, 0, 0, 2, 3, 40, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 40, }, + { 1, 0, 0, 2, 4, 40, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 40, }, + { 1, 0, 0, 2, 5, 40, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 40, }, + { 1, 0, 0, 2, 6, 40, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 40, }, + { 1, 0, 0, 2, 7, 40, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 40, }, + { 1, 0, 0, 2, 8, 40, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 40, }, + { 1, 0, 0, 2, 9, 40, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 40, }, + { 1, 0, 0, 2, 10, 40, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 40, }, + { 1, 0, 0, 2, 11, 40, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 40, }, + { 1, 0, 0, 2, 12, 40, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 40, }, + { 1, 0, 0, 2, 13, 40, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 46, }, + { 2, 0, 1, 2, 3, 40, }, + { 1, 0, 1, 2, 3, 40, }, + { 0, 0, 1, 2, 4, 46, }, + { 2, 0, 1, 2, 4, 40, }, + { 1, 0, 1, 2, 4, 40, }, + { 0, 0, 1, 2, 5, 46, }, + { 2, 0, 1, 2, 5, 40, }, + { 1, 0, 1, 2, 5, 40, }, + { 0, 0, 1, 2, 6, 46, }, + { 2, 0, 1, 2, 6, 40, }, + { 1, 0, 1, 2, 6, 40, }, + { 0, 0, 1, 2, 7, 46, }, + { 2, 0, 1, 2, 7, 40, }, + { 1, 0, 1, 2, 7, 40, }, + { 0, 0, 1, 2, 8, 46, }, + { 2, 0, 1, 2, 8, 40, }, + { 1, 0, 1, 2, 8, 40, }, + { 0, 0, 1, 2, 9, 46, }, + { 2, 0, 1, 2, 9, 40, }, + { 1, 0, 1, 2, 9, 40, }, + { 0, 0, 1, 2, 10, 46, }, + { 2, 0, 1, 2, 10, 40, }, + { 1, 0, 1, 2, 10, 40, }, + { 0, 0, 1, 2, 11, 46, }, + { 2, 0, 1, 2, 11, 40, }, + { 1, 0, 1, 2, 11, 40, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 40, }, + { 1, 0, 1, 2, 12, 40, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 40, }, + { 1, 0, 1, 2, 13, 40, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 46, }, + { 2, 0, 1, 3, 3, 40, }, + { 1, 0, 1, 3, 3, 40, }, + { 0, 0, 1, 3, 4, 46, }, + { 2, 0, 1, 3, 4, 40, }, + { 1, 0, 1, 3, 4, 40, }, + { 0, 0, 1, 3, 5, 46, }, + { 2, 0, 1, 3, 5, 40, }, + { 1, 0, 1, 3, 5, 40, }, + { 0, 0, 1, 3, 6, 46, }, + { 2, 0, 1, 3, 6, 40, }, + { 1, 0, 1, 3, 6, 40, }, + { 0, 0, 1, 3, 7, 46, }, + { 2, 0, 1, 3, 7, 40, }, + { 1, 0, 1, 3, 7, 40, }, + { 0, 0, 1, 3, 8, 46, }, + { 2, 0, 1, 3, 8, 40, }, + { 1, 0, 1, 3, 8, 40, }, + { 0, 0, 1, 3, 9, 46, }, + { 2, 0, 1, 3, 9, 40, }, + { 1, 0, 1, 3, 9, 40, }, + { 0, 0, 1, 3, 10, 46, }, + { 2, 0, 1, 3, 10, 40, }, + { 1, 0, 1, 3, 10, 40, }, + { 0, 0, 1, 3, 11, 46, }, + { 2, 0, 1, 3, 11, 40, }, + { 1, 0, 1, 3, 11, 40, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 40, }, + { 1, 0, 1, 3, 12, 40, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 40, }, + { 1, 0, 1, 3, 13, 40, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 46, }, + { 2, 0, 1, 6, 3, 40, }, + { 1, 0, 1, 6, 3, 40, }, + { 0, 0, 1, 6, 4, 46, }, + { 2, 0, 1, 6, 4, 40, }, + { 1, 0, 1, 6, 4, 40, }, + { 0, 0, 1, 6, 5, 46, }, + { 2, 0, 1, 6, 5, 40, }, + { 1, 0, 1, 6, 5, 40, }, + { 0, 0, 1, 6, 6, 46, }, + { 2, 0, 1, 6, 6, 40, }, + { 1, 0, 1, 6, 6, 40, }, + { 0, 0, 1, 6, 7, 46, }, + { 2, 0, 1, 6, 7, 40, }, + { 1, 0, 1, 6, 7, 40, }, + { 0, 0, 1, 6, 8, 46, }, + { 2, 0, 1, 6, 8, 40, }, + { 1, 0, 1, 6, 8, 40, }, + { 0, 0, 1, 6, 9, 46, }, + { 2, 0, 1, 6, 9, 40, }, + { 1, 0, 1, 6, 9, 40, }, + { 0, 0, 1, 6, 10, 46, }, + { 2, 0, 1, 6, 10, 40, }, + { 1, 0, 1, 6, 10, 40, }, + { 0, 0, 1, 6, 11, 46, }, + { 2, 0, 1, 6, 11, 40, }, + { 1, 0, 1, 6, 11, 40, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 40, }, + { 1, 0, 1, 6, 12, 40, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 40, }, + { 1, 0, 1, 6, 13, 40, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 46, }, + { 2, 0, 1, 7, 3, 40, }, + { 1, 0, 1, 7, 3, 40, }, + { 0, 0, 1, 7, 4, 46, }, + { 2, 0, 1, 7, 4, 40, }, + { 1, 0, 1, 7, 4, 40, }, + { 0, 0, 1, 7, 5, 46, }, + { 2, 0, 1, 7, 5, 40, }, + { 1, 0, 1, 7, 5, 40, }, + { 0, 0, 1, 7, 6, 46, }, + { 2, 0, 1, 7, 6, 40, }, + { 1, 0, 1, 7, 6, 40, }, + { 0, 0, 1, 7, 7, 46, }, + { 2, 0, 1, 7, 7, 40, }, + { 1, 0, 1, 7, 7, 40, }, + { 0, 0, 1, 7, 8, 46, }, + { 2, 0, 1, 7, 8, 40, }, + { 1, 0, 1, 7, 8, 40, }, + { 0, 0, 1, 7, 9, 46, }, + { 2, 0, 1, 7, 9, 40, }, + { 1, 0, 1, 7, 9, 40, }, + { 0, 0, 1, 7, 10, 46, }, + { 2, 0, 1, 7, 10, 40, }, + { 1, 0, 1, 7, 10, 40, }, + { 0, 0, 1, 7, 11, 46, }, + { 2, 0, 1, 7, 11, 40, }, + { 1, 0, 1, 7, 11, 40, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 40, }, + { 1, 0, 1, 7, 12, 40, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 40, }, + { 1, 0, 1, 7, 13, 40, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 40, }, + { 1, 1, 0, 1, 36, 40, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 40, }, + { 1, 1, 0, 1, 40, 40, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 40, }, + { 1, 1, 0, 1, 44, 40, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 40, }, + { 1, 1, 0, 1, 48, 40, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 40, }, + { 1, 1, 0, 1, 52, 40, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 40, }, + { 1, 1, 0, 1, 56, 40, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 40, }, + { 1, 1, 0, 1, 60, 40, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 40, }, + { 1, 1, 0, 1, 64, 40, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 40, }, + { 1, 1, 0, 1, 100, 40, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 40, }, + { 1, 1, 0, 1, 104, 40, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 40, }, + { 1, 1, 0, 1, 108, 40, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 40, }, + { 1, 1, 0, 1, 112, 40, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 40, }, + { 1, 1, 0, 1, 116, 40, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 40, }, + { 1, 1, 0, 1, 120, 40, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 40, }, + { 1, 1, 0, 1, 124, 40, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 40, }, + { 1, 1, 0, 1, 128, 40, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 40, }, + { 1, 1, 0, 1, 132, 40, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 40, }, + { 1, 1, 0, 1, 136, 40, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 40, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 40, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 40, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 40, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 40, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 40, }, + { 1, 1, 0, 2, 36, 40, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 40, }, + { 1, 1, 0, 2, 40, 40, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 40, }, + { 1, 1, 0, 2, 44, 40, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 40, }, + { 1, 1, 0, 2, 48, 40, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 40, }, + { 1, 1, 0, 2, 52, 40, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 40, }, + { 1, 1, 0, 2, 56, 40, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 40, }, + { 1, 1, 0, 2, 60, 40, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 40, }, + { 1, 1, 0, 2, 64, 40, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 40, }, + { 1, 1, 0, 2, 100, 40, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 40, }, + { 1, 1, 0, 2, 104, 40, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 40, }, + { 1, 1, 0, 2, 108, 40, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 40, }, + { 1, 1, 0, 2, 112, 40, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 40, }, + { 1, 1, 0, 2, 116, 40, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 40, }, + { 1, 1, 0, 2, 120, 40, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 40, }, + { 1, 1, 0, 2, 124, 40, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 40, }, + { 1, 1, 0, 2, 128, 40, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 40, }, + { 1, 1, 0, 2, 132, 40, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 40, }, + { 1, 1, 0, 2, 136, 40, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 40, }, + { 1, 1, 0, 2, 140, 40, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 40, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 40, }, + { 1, 1, 0, 3, 36, 40, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 40, }, + { 1, 1, 0, 3, 40, 40, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 40, }, + { 1, 1, 0, 3, 44, 40, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 40, }, + { 1, 1, 0, 3, 48, 40, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 40, }, + { 1, 1, 0, 3, 52, 40, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 40, }, + { 1, 1, 0, 3, 56, 40, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 40, }, + { 1, 1, 0, 3, 60, 40, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 40, }, + { 1, 1, 0, 3, 64, 40, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 40, }, + { 1, 1, 0, 3, 100, 40, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 40, }, + { 1, 1, 0, 3, 104, 40, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 40, }, + { 1, 1, 0, 3, 108, 40, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 40, }, + { 1, 1, 0, 3, 112, 40, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 40, }, + { 1, 1, 0, 3, 116, 40, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 40, }, + { 1, 1, 0, 3, 120, 40, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 40, }, + { 1, 1, 0, 3, 124, 40, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 40, }, + { 1, 1, 0, 3, 128, 40, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 40, }, + { 1, 1, 0, 3, 132, 40, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 40, }, + { 1, 1, 0, 3, 136, 40, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 40, }, + { 1, 1, 0, 3, 140, 40, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 40, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 40, }, + { 1, 1, 0, 6, 36, 40, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 40, }, + { 1, 1, 0, 6, 40, 40, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 40, }, + { 1, 1, 0, 6, 44, 40, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 40, }, + { 1, 1, 0, 6, 48, 40, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 40, }, + { 1, 1, 0, 6, 52, 40, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 40, }, + { 1, 1, 0, 6, 56, 40, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 40, }, + { 1, 1, 0, 6, 60, 40, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 40, }, + { 1, 1, 0, 6, 64, 40, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 40, }, + { 1, 1, 0, 6, 100, 40, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 40, }, + { 1, 1, 0, 6, 104, 40, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 40, }, + { 1, 1, 0, 6, 108, 40, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 40, }, + { 1, 1, 0, 6, 112, 40, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 40, }, + { 1, 1, 0, 6, 116, 40, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 40, }, + { 1, 1, 0, 6, 120, 40, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 40, }, + { 1, 1, 0, 6, 124, 40, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 40, }, + { 1, 1, 0, 6, 128, 40, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 40, }, + { 1, 1, 0, 6, 132, 40, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 40, }, + { 1, 1, 0, 6, 136, 40, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 40, }, + { 1, 1, 0, 6, 140, 40, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 40, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 40, }, + { 1, 1, 0, 7, 36, 40, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 40, }, + { 1, 1, 0, 7, 40, 40, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 40, }, + { 1, 1, 0, 7, 44, 40, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 40, }, + { 1, 1, 0, 7, 48, 40, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 40, }, + { 1, 1, 0, 7, 52, 40, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 40, }, + { 1, 1, 0, 7, 56, 40, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 40, }, + { 1, 1, 0, 7, 60, 40, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 40, }, + { 1, 1, 0, 7, 64, 40, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 40, }, + { 1, 1, 0, 7, 100, 40, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 40, }, + { 1, 1, 0, 7, 104, 40, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 40, }, + { 1, 1, 0, 7, 108, 40, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 40, }, + { 1, 1, 0, 7, 112, 40, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 40, }, + { 1, 1, 0, 7, 116, 40, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 40, }, + { 1, 1, 0, 7, 120, 40, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 40, }, + { 1, 1, 0, 7, 124, 40, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 40, }, + { 1, 1, 0, 7, 128, 40, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 40, }, + { 1, 1, 0, 7, 132, 40, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 40, }, + { 1, 1, 0, 7, 136, 40, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 40, }, + { 1, 1, 0, 7, 140, 40, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 40, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 40, }, + { 1, 1, 1, 2, 38, 40, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 40, }, + { 1, 1, 1, 2, 46, 40, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 40, }, + { 1, 1, 1, 2, 54, 40, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 40, }, + { 1, 1, 1, 2, 62, 40, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 40, }, + { 1, 1, 1, 2, 102, 40, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 40, }, + { 1, 1, 1, 2, 110, 40, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 40, }, + { 1, 1, 1, 2, 118, 40, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 40, }, + { 1, 1, 1, 2, 126, 40, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 40, }, + { 1, 1, 1, 2, 134, 40, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 40, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 40, }, + { 1, 1, 1, 3, 38, 40, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 40, }, + { 1, 1, 1, 3, 46, 40, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 40, }, + { 1, 1, 1, 3, 54, 40, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 40, }, + { 1, 1, 1, 3, 62, 40, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 40, }, + { 1, 1, 1, 3, 102, 40, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 40, }, + { 1, 1, 1, 3, 110, 40, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 40, }, + { 1, 1, 1, 3, 118, 40, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 40, }, + { 1, 1, 1, 3, 126, 40, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 40, }, + { 1, 1, 1, 3, 134, 40, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 40, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 40, }, + { 1, 1, 1, 6, 38, 40, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 40, }, + { 1, 1, 1, 6, 46, 40, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 40, }, + { 1, 1, 1, 6, 54, 40, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 40, }, + { 1, 1, 1, 6, 62, 40, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 40, }, + { 1, 1, 1, 6, 102, 40, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 40, }, + { 1, 1, 1, 6, 110, 40, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 40, }, + { 1, 1, 1, 6, 118, 40, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 40, }, + { 1, 1, 1, 6, 126, 40, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 40, }, + { 1, 1, 1, 6, 134, 40, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 40, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 40, }, + { 1, 1, 1, 7, 38, 40, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 40, }, + { 1, 1, 1, 7, 46, 40, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 40, }, + { 1, 1, 1, 7, 54, 40, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 40, }, + { 1, 1, 1, 7, 62, 40, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 40, }, + { 1, 1, 1, 7, 102, 40, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 40, }, + { 1, 1, 1, 7, 110, 40, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 40, }, + { 1, 1, 1, 7, 118, 40, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 40, }, + { 1, 1, 1, 7, 126, 40, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 40, }, + { 1, 1, 1, 7, 134, 40, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 40, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 40, }, + { 1, 1, 2, 4, 42, 40, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 40, }, + { 1, 1, 2, 4, 58, 40, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 40, }, + { 1, 1, 2, 4, 106, 40, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 40, }, + { 1, 1, 2, 4, 122, 40, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 40, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 40, }, + { 1, 1, 2, 5, 42, 40, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 40, }, + { 1, 1, 2, 5, 58, 40, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 40, }, + { 1, 1, 2, 5, 106, 40, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 40, }, + { 1, 1, 2, 5, 122, 40, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 40, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 40, }, + { 1, 1, 2, 8, 42, 40, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 40, }, + { 1, 1, 2, 8, 58, 40, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 40, }, + { 1, 1, 2, 8, 106, 40, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 40, }, + { 1, 1, 2, 8, 122, 40, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 40, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 40, }, + { 1, 1, 2, 9, 42, 40, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 40, }, + { 1, 1, 2, 9, 58, 40, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 40, }, + { 1, 1, 2, 9, 106, 40, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 40, }, + { 1, 1, 2, 9, 122, 40, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 40, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type5); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type7[] = { + { 0, 0, 0, 0, 1, 44, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 52, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 52, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 52, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 52, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 52, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 52, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 52, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 52, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 52, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 44, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 38, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 38, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 34, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 34, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 44, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 44, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 44, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 44, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 44, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 44, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 44, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 44, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 44, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 32, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 30, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 42, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 42, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 42, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 42, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 42, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 42, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 42, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 42, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 42, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 30, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 28, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 40, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 40, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 40, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 40, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 40, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 40, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 40, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 40, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 40, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 28, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 36, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 40, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 40, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 40, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 40, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 40, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 40, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 40, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 34, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 34, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 38, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 38, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 38, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 38, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 38, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 38, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 38, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 32, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 32, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 36, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 36, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 36, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 36, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 36, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 36, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 36, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 30, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 32, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 36, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 36, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 36, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 36, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 36, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 36, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 36, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 30, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 38, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 38, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 38, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 38, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 38, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 38, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 38, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 38, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 36, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 36, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 36, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 36, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 36, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 36, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 36, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 36, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 36, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 36, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 36, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 36, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 36, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 36, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 36, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 36, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 36, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 36, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 36, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 36, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 36, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 36, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 36, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 36, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 36, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 36, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 36, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 36, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 36, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 36, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 36, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 36, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 36, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 34, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 32, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 38, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 38, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 38, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 38, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 34, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 34, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 34, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 34, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 34, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 34, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 34, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 34, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 34, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 34, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 34, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 34, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 34, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 34, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 34, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 34, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 34, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 34, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 32, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 30, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 36, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 36, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 36, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 36, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 32, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 32, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 32, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 32, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 32, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 32, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 32, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 32, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 32, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 32, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 32, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 32, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 32, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 32, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 32, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 32, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 32, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 30, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 28, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 34, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 34, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 34, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 34, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 30, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 30, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 30, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 30, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 30, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 30, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 30, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 30, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 30, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 30, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 30, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 30, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 30, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 30, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 30, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 30, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 30, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 30, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 28, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 26, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 32, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 32, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 32, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 32, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 32, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 30, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 30, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 38, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 38, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 38, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 38, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 32, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 30, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 30, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 28, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 28, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 36, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 36, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 36, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 36, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 30, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 30, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 28, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 28, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 26, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 26, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 34, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 34, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 34, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 34, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 28, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 28, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 26, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 26, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 24, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 24, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 32, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 32, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 32, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 32, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 26, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 26, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 26, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 26, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 28, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 28, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 28, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 24, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 24, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 26, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 26, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 26, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 22, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 22, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 24, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 24, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 24, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 20, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 20, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 22, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 22, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 22, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type7); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type8[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 46, }, + { 1, 0, 0, 0, 1, 46, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 46, }, + { 1, 0, 0, 0, 2, 46, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 46, }, + { 1, 0, 0, 0, 3, 46, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 46, }, + { 1, 0, 0, 0, 4, 46, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 46, }, + { 1, 0, 0, 0, 5, 46, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 46, }, + { 1, 0, 0, 0, 6, 46, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 46, }, + { 1, 0, 0, 0, 7, 46, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 46, }, + { 1, 0, 0, 0, 8, 46, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 46, }, + { 1, 0, 0, 0, 9, 46, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 46, }, + { 1, 0, 0, 0, 10, 46, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 46, }, + { 1, 0, 0, 0, 11, 46, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 46, }, + { 1, 0, 0, 0, 12, 46, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 46, }, + { 1, 0, 0, 0, 13, 46, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 46, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 46, }, + { 1, 0, 0, 1, 1, 46, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 46, }, + { 1, 0, 0, 1, 2, 46, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 46, }, + { 1, 0, 0, 1, 3, 46, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 46, }, + { 1, 0, 0, 1, 4, 46, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 46, }, + { 1, 0, 0, 1, 5, 46, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 46, }, + { 1, 0, 0, 1, 6, 46, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 46, }, + { 1, 0, 0, 1, 7, 46, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 46, }, + { 1, 0, 0, 1, 8, 46, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 46, }, + { 1, 0, 0, 1, 9, 46, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 46, }, + { 1, 0, 0, 1, 10, 46, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 46, }, + { 1, 0, 0, 1, 11, 46, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 46, }, + { 1, 0, 0, 1, 12, 46, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 46, }, + { 1, 0, 0, 1, 13, 46, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 46, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 46, }, + { 1, 0, 0, 2, 1, 46, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 46, }, + { 1, 0, 0, 2, 2, 46, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 46, }, + { 1, 0, 0, 2, 3, 46, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 46, }, + { 1, 0, 0, 2, 4, 46, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 46, }, + { 1, 0, 0, 2, 5, 46, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 46, }, + { 1, 0, 0, 2, 6, 46, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 46, }, + { 1, 0, 0, 2, 7, 46, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 46, }, + { 1, 0, 0, 2, 8, 46, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 46, }, + { 1, 0, 0, 2, 9, 46, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 46, }, + { 1, 0, 0, 2, 10, 46, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 46, }, + { 1, 0, 0, 2, 11, 46, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 46, }, + { 1, 0, 0, 2, 12, 46, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 46, }, + { 1, 0, 0, 2, 13, 46, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 46, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 46, }, + { 1, 0, 0, 3, 1, 46, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 46, }, + { 1, 0, 0, 3, 2, 46, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 46, }, + { 1, 0, 0, 3, 3, 46, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 46, }, + { 1, 0, 0, 3, 4, 46, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 46, }, + { 1, 0, 0, 3, 5, 46, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 46, }, + { 1, 0, 0, 3, 6, 46, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 46, }, + { 1, 0, 0, 3, 7, 46, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 46, }, + { 1, 0, 0, 3, 8, 46, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 46, }, + { 1, 0, 0, 3, 9, 46, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 46, }, + { 1, 0, 0, 3, 10, 46, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 46, }, + { 1, 0, 0, 3, 11, 46, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 46, }, + { 1, 0, 0, 3, 12, 46, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 46, }, + { 1, 0, 0, 3, 13, 46, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 46, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 46, }, + { 1, 0, 0, 6, 1, 46, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 46, }, + { 1, 0, 0, 6, 2, 46, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 46, }, + { 1, 0, 0, 6, 3, 46, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 46, }, + { 1, 0, 0, 6, 4, 46, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 46, }, + { 1, 0, 0, 6, 5, 46, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 46, }, + { 1, 0, 0, 6, 6, 46, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 46, }, + { 1, 0, 0, 6, 7, 46, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 46, }, + { 1, 0, 0, 6, 8, 46, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 46, }, + { 1, 0, 0, 6, 9, 46, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 46, }, + { 1, 0, 0, 6, 10, 46, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 46, }, + { 1, 0, 0, 6, 11, 46, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 46, }, + { 1, 0, 0, 6, 12, 46, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 46, }, + { 1, 0, 0, 6, 13, 46, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 46, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 46, }, + { 1, 0, 0, 7, 1, 46, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 46, }, + { 1, 0, 0, 7, 2, 46, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 46, }, + { 1, 0, 0, 7, 3, 46, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 46, }, + { 1, 0, 0, 7, 4, 46, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 46, }, + { 1, 0, 0, 7, 5, 46, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 46, }, + { 1, 0, 0, 7, 6, 46, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 46, }, + { 1, 0, 0, 7, 7, 46, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 46, }, + { 1, 0, 0, 7, 8, 46, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 46, }, + { 1, 0, 0, 7, 9, 46, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 46, }, + { 1, 0, 0, 7, 10, 46, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 46, }, + { 1, 0, 0, 7, 11, 46, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 46, }, + { 1, 0, 0, 7, 12, 46, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 46, }, + { 1, 0, 0, 7, 13, 46, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 46, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 30, }, + { 2, 0, 1, 2, 3, 34, }, + { 1, 0, 1, 2, 3, 34, }, + { 0, 0, 1, 2, 4, 34, }, + { 2, 0, 1, 2, 4, 34, }, + { 1, 0, 1, 2, 4, 34, }, + { 0, 0, 1, 2, 5, 34, }, + { 2, 0, 1, 2, 5, 34, }, + { 1, 0, 1, 2, 5, 34, }, + { 0, 0, 1, 2, 6, 34, }, + { 2, 0, 1, 2, 6, 34, }, + { 1, 0, 1, 2, 6, 34, }, + { 0, 0, 1, 2, 7, 34, }, + { 2, 0, 1, 2, 7, 34, }, + { 1, 0, 1, 2, 7, 34, }, + { 0, 0, 1, 2, 8, 34, }, + { 2, 0, 1, 2, 8, 34, }, + { 1, 0, 1, 2, 8, 34, }, + { 0, 0, 1, 2, 9, 34, }, + { 2, 0, 1, 2, 9, 34, }, + { 1, 0, 1, 2, 9, 34, }, + { 0, 0, 1, 2, 10, 34, }, + { 2, 0, 1, 2, 10, 34, }, + { 1, 0, 1, 2, 10, 34, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 34, }, + { 1, 0, 1, 2, 11, 34, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 34, }, + { 1, 0, 1, 2, 12, 34, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 34, }, + { 1, 0, 1, 2, 13, 34, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 34, }, + { 1, 0, 1, 3, 3, 34, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 34, }, + { 1, 0, 1, 3, 4, 34, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 34, }, + { 1, 0, 1, 3, 5, 34, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 34, }, + { 1, 0, 1, 3, 6, 34, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 34, }, + { 1, 0, 1, 3, 7, 34, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 34, }, + { 1, 0, 1, 3, 8, 34, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 34, }, + { 1, 0, 1, 3, 9, 34, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 34, }, + { 1, 0, 1, 3, 10, 34, }, + { 0, 0, 1, 3, 11, 28, }, + { 2, 0, 1, 3, 11, 34, }, + { 1, 0, 1, 3, 11, 34, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 34, }, + { 1, 0, 1, 3, 12, 34, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 34, }, + { 1, 0, 1, 3, 13, 34, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 30, }, + { 2, 0, 1, 6, 3, 34, }, + { 1, 0, 1, 6, 3, 34, }, + { 0, 0, 1, 6, 4, 34, }, + { 2, 0, 1, 6, 4, 34, }, + { 1, 0, 1, 6, 4, 34, }, + { 0, 0, 1, 6, 5, 34, }, + { 2, 0, 1, 6, 5, 34, }, + { 1, 0, 1, 6, 5, 34, }, + { 0, 0, 1, 6, 6, 34, }, + { 2, 0, 1, 6, 6, 34, }, + { 1, 0, 1, 6, 6, 34, }, + { 0, 0, 1, 6, 7, 34, }, + { 2, 0, 1, 6, 7, 34, }, + { 1, 0, 1, 6, 7, 34, }, + { 0, 0, 1, 6, 8, 34, }, + { 2, 0, 1, 6, 8, 34, }, + { 1, 0, 1, 6, 8, 34, }, + { 0, 0, 1, 6, 9, 34, }, + { 2, 0, 1, 6, 9, 34, }, + { 1, 0, 1, 6, 9, 34, }, + { 0, 0, 1, 6, 10, 34, }, + { 2, 0, 1, 6, 10, 34, }, + { 1, 0, 1, 6, 10, 34, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 34, }, + { 1, 0, 1, 6, 11, 34, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 34, }, + { 1, 0, 1, 6, 12, 34, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 34, }, + { 1, 0, 1, 6, 13, 34, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 30, }, + { 2, 0, 1, 7, 3, 34, }, + { 1, 0, 1, 7, 3, 34, }, + { 0, 0, 1, 7, 4, 34, }, + { 2, 0, 1, 7, 4, 34, }, + { 1, 0, 1, 7, 4, 34, }, + { 0, 0, 1, 7, 5, 34, }, + { 2, 0, 1, 7, 5, 34, }, + { 1, 0, 1, 7, 5, 34, }, + { 0, 0, 1, 7, 6, 34, }, + { 2, 0, 1, 7, 6, 34, }, + { 1, 0, 1, 7, 6, 34, }, + { 0, 0, 1, 7, 7, 34, }, + { 2, 0, 1, 7, 7, 34, }, + { 1, 0, 1, 7, 7, 34, }, + { 0, 0, 1, 7, 8, 34, }, + { 2, 0, 1, 7, 8, 34, }, + { 1, 0, 1, 7, 8, 34, }, + { 0, 0, 1, 7, 9, 34, }, + { 2, 0, 1, 7, 9, 34, }, + { 1, 0, 1, 7, 9, 34, }, + { 0, 0, 1, 7, 10, 34, }, + { 2, 0, 1, 7, 10, 34, }, + { 1, 0, 1, 7, 10, 34, }, + { 0, 0, 1, 7, 11, 28, }, + { 2, 0, 1, 7, 11, 34, }, + { 1, 0, 1, 7, 11, 34, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 34, }, + { 1, 0, 1, 7, 12, 34, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 34, }, + { 1, 0, 1, 7, 13, 34, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 46, }, + { 1, 1, 0, 1, 36, 46, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 46, }, + { 1, 1, 0, 1, 40, 46, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 46, }, + { 1, 1, 0, 1, 44, 46, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 46, }, + { 1, 1, 0, 1, 48, 46, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 46, }, + { 1, 1, 0, 1, 52, 46, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 46, }, + { 1, 1, 0, 1, 56, 46, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 46, }, + { 1, 1, 0, 1, 60, 46, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 46, }, + { 1, 1, 0, 1, 64, 46, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 46, }, + { 1, 1, 0, 1, 100, 46, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 46, }, + { 1, 1, 0, 1, 104, 46, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 46, }, + { 1, 1, 0, 1, 108, 46, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 46, }, + { 1, 1, 0, 1, 112, 46, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 46, }, + { 1, 1, 0, 1, 116, 46, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 46, }, + { 1, 1, 0, 1, 120, 46, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 46, }, + { 1, 1, 0, 1, 124, 46, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 46, }, + { 1, 1, 0, 1, 128, 46, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 46, }, + { 1, 1, 0, 1, 132, 46, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 46, }, + { 1, 1, 0, 1, 136, 46, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 46, }, + { 1, 1, 0, 1, 140, 46, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 46, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 46, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 46, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 46, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 46, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 46, }, + { 1, 1, 0, 2, 36, 46, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 46, }, + { 1, 1, 0, 2, 40, 46, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 46, }, + { 1, 1, 0, 2, 44, 46, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 46, }, + { 1, 1, 0, 2, 48, 46, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 46, }, + { 1, 1, 0, 2, 52, 46, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 46, }, + { 1, 1, 0, 2, 56, 46, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 46, }, + { 1, 1, 0, 2, 60, 46, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 46, }, + { 1, 1, 0, 2, 64, 46, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 46, }, + { 1, 1, 0, 2, 100, 46, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 46, }, + { 1, 1, 0, 2, 104, 46, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 46, }, + { 1, 1, 0, 2, 108, 46, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 46, }, + { 1, 1, 0, 2, 112, 46, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 46, }, + { 1, 1, 0, 2, 116, 46, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 46, }, + { 1, 1, 0, 2, 120, 46, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 46, }, + { 1, 1, 0, 2, 124, 46, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 46, }, + { 1, 1, 0, 2, 128, 46, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 46, }, + { 1, 1, 0, 2, 132, 46, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 46, }, + { 1, 1, 0, 2, 136, 46, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 46, }, + { 1, 1, 0, 2, 140, 46, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 46, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 46, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 46, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 46, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 46, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 46, }, + { 1, 1, 0, 3, 36, 46, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 46, }, + { 1, 1, 0, 3, 40, 46, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 46, }, + { 1, 1, 0, 3, 44, 46, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 46, }, + { 1, 1, 0, 3, 48, 46, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 46, }, + { 1, 1, 0, 3, 52, 46, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 46, }, + { 1, 1, 0, 3, 56, 46, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 46, }, + { 1, 1, 0, 3, 60, 46, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 46, }, + { 1, 1, 0, 3, 64, 46, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 46, }, + { 1, 1, 0, 3, 100, 46, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 46, }, + { 1, 1, 0, 3, 104, 46, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 46, }, + { 1, 1, 0, 3, 108, 46, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 46, }, + { 1, 1, 0, 3, 112, 46, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 46, }, + { 1, 1, 0, 3, 116, 46, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 46, }, + { 1, 1, 0, 3, 120, 46, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 46, }, + { 1, 1, 0, 3, 124, 46, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 46, }, + { 1, 1, 0, 3, 128, 46, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 46, }, + { 1, 1, 0, 3, 132, 46, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 46, }, + { 1, 1, 0, 3, 136, 46, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 46, }, + { 1, 1, 0, 3, 140, 46, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 46, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 46, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 46, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 46, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 46, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 46, }, + { 1, 1, 0, 6, 36, 46, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 46, }, + { 1, 1, 0, 6, 40, 46, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 46, }, + { 1, 1, 0, 6, 44, 46, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 46, }, + { 1, 1, 0, 6, 48, 46, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 46, }, + { 1, 1, 0, 6, 52, 46, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 46, }, + { 1, 1, 0, 6, 56, 46, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 46, }, + { 1, 1, 0, 6, 60, 46, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 46, }, + { 1, 1, 0, 6, 64, 46, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 46, }, + { 1, 1, 0, 6, 100, 46, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 46, }, + { 1, 1, 0, 6, 104, 46, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 46, }, + { 1, 1, 0, 6, 108, 46, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 46, }, + { 1, 1, 0, 6, 112, 46, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 46, }, + { 1, 1, 0, 6, 116, 46, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 46, }, + { 1, 1, 0, 6, 120, 46, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 46, }, + { 1, 1, 0, 6, 124, 46, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 46, }, + { 1, 1, 0, 6, 128, 46, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 46, }, + { 1, 1, 0, 6, 132, 46, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 46, }, + { 1, 1, 0, 6, 136, 46, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 46, }, + { 1, 1, 0, 6, 140, 46, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 46, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 46, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 46, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 46, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 46, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 46, }, + { 1, 1, 0, 7, 36, 46, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 46, }, + { 1, 1, 0, 7, 40, 46, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 46, }, + { 1, 1, 0, 7, 44, 46, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 46, }, + { 1, 1, 0, 7, 48, 46, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 46, }, + { 1, 1, 0, 7, 52, 46, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 46, }, + { 1, 1, 0, 7, 56, 46, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 46, }, + { 1, 1, 0, 7, 60, 46, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 46, }, + { 1, 1, 0, 7, 64, 46, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 46, }, + { 1, 1, 0, 7, 100, 46, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 46, }, + { 1, 1, 0, 7, 104, 46, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 46, }, + { 1, 1, 0, 7, 108, 46, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 46, }, + { 1, 1, 0, 7, 112, 46, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 46, }, + { 1, 1, 0, 7, 116, 46, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 46, }, + { 1, 1, 0, 7, 120, 46, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 46, }, + { 1, 1, 0, 7, 124, 46, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 46, }, + { 1, 1, 0, 7, 128, 46, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 46, }, + { 1, 1, 0, 7, 132, 46, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 46, }, + { 1, 1, 0, 7, 136, 46, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 46, }, + { 1, 1, 0, 7, 140, 46, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 46, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 46, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 46, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 46, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 46, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 46, }, + { 1, 1, 1, 2, 38, 46, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 46, }, + { 1, 1, 1, 2, 46, 46, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 46, }, + { 1, 1, 1, 2, 54, 46, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 46, }, + { 1, 1, 1, 2, 62, 46, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 46, }, + { 1, 1, 1, 2, 102, 46, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 46, }, + { 1, 1, 1, 2, 110, 46, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 46, }, + { 1, 1, 1, 2, 118, 46, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 46, }, + { 1, 1, 1, 2, 126, 46, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 46, }, + { 1, 1, 1, 2, 134, 46, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 46, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 46, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 46, }, + { 1, 1, 1, 3, 38, 46, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 46, }, + { 1, 1, 1, 3, 46, 46, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 46, }, + { 1, 1, 1, 3, 54, 46, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 46, }, + { 1, 1, 1, 3, 62, 46, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 46, }, + { 1, 1, 1, 3, 102, 46, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 46, }, + { 1, 1, 1, 3, 110, 46, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 46, }, + { 1, 1, 1, 3, 118, 46, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 46, }, + { 1, 1, 1, 3, 126, 46, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 46, }, + { 1, 1, 1, 3, 134, 46, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 46, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 46, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 46, }, + { 1, 1, 1, 6, 38, 46, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 46, }, + { 1, 1, 1, 6, 46, 46, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 46, }, + { 1, 1, 1, 6, 54, 46, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 46, }, + { 1, 1, 1, 6, 62, 46, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 46, }, + { 1, 1, 1, 6, 102, 46, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 46, }, + { 1, 1, 1, 6, 110, 46, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 46, }, + { 1, 1, 1, 6, 118, 46, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 46, }, + { 1, 1, 1, 6, 126, 46, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 46, }, + { 1, 1, 1, 6, 134, 46, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 46, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 46, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 46, }, + { 1, 1, 1, 7, 38, 46, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 46, }, + { 1, 1, 1, 7, 46, 46, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 46, }, + { 1, 1, 1, 7, 54, 46, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 46, }, + { 1, 1, 1, 7, 62, 46, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 46, }, + { 1, 1, 1, 7, 102, 46, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 46, }, + { 1, 1, 1, 7, 110, 46, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 46, }, + { 1, 1, 1, 7, 118, 46, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 46, }, + { 1, 1, 1, 7, 126, 46, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 46, }, + { 1, 1, 1, 7, 134, 46, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 46, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 46, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 46, }, + { 1, 1, 2, 4, 42, 46, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 46, }, + { 1, 1, 2, 4, 58, 46, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 46, }, + { 1, 1, 2, 4, 106, 46, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 46, }, + { 1, 1, 2, 4, 122, 46, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 46, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 46, }, + { 1, 1, 2, 5, 42, 46, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 46, }, + { 1, 1, 2, 5, 58, 46, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 46, }, + { 1, 1, 2, 5, 106, 46, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 46, }, + { 1, 1, 2, 5, 122, 46, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 46, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 46, }, + { 1, 1, 2, 8, 42, 46, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 46, }, + { 1, 1, 2, 8, 58, 46, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 46, }, + { 1, 1, 2, 8, 106, 46, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 46, }, + { 1, 1, 2, 8, 122, 46, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 46, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 46, }, + { 1, 1, 2, 9, 42, 46, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 46, }, + { 1, 1, 2, 9, 58, 46, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 46, }, + { 1, 1, 2, 9, 106, 46, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 46, }, + { 1, 1, 2, 9, 122, 46, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 46, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type8); + +static const u8 +rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, + 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19}, + {0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, + 11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18}, + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19}, +}; + +static const u8 +rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 15, 15, 15, 16, 16, 17, 18}, + {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11, + 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11, + 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20}, +}; + +static const u8 +rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17}, + {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11, + 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11, + 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20}, +}; + +static const u8 +rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 23, 24}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 11, 11, 11, 11, 12, 12, 13, 13, 14}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 11, + 12, 13, 14, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21}, + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19}, +}; + +static const u8 +rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 12, 13, + 14, 15, 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, +}; + +static const u8 rtw8814a_pwrtrk_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, + 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, + 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 rtw8814a_pwrtrk_type0_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type0_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type0_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type0_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type0_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type0_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type0_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type0_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type0_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type0_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type0_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type0_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type0_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type0_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type0_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type0_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type0_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, 12, + 12, 13, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16}, + {0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 9, 9, 10, 10, 10, 10, + 11, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, + 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, + {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 20, 20, 21, 21, 21, 21, 21}, + {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 11, 12, 13, + 13, 13, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21}, + {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20}, + {0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 11, + 12, 13, 13, 13, 13, 14, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12, + 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 11, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, + 15, 15, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 20}, + {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, + 15, 16, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, + 15, 16, 17, 18, 19, 19, 20, 20, 20, 20, 20, 20, 20}, +}; + +static const u8 rtw8814a_pwrtrk_type2_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, + 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, + 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8, + 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10, + 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9, + 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type2_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type2_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type2_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type2_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type2_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type2_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type2_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type2_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type2_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type2_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type2_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type2_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type2_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type2_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type2_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type2_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 3, 3, 3, 4, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 13, 14, + 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 4, 5, 6, 7, 7, 8, 7, 8, 10, 11, 12, 12, 13, 13, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 13, 14, 15, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 10, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 11, 12, + 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 0, 1, 1, 2, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 3, 3, 4, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13}, + {0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 9, 10, 11, 13, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 10, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 9, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 1, 2, 2, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 0, 2, 3, 4, 5, 6, 8, 8, 9, 9, 11, 12, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 2, 3, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12}, + {0, 2, 3, 4, 5, 7, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 17, + 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18}, + {0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 10, 11, 11, 12, 13, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 3, 3, 3, 5, 5, 6, 6, 8, 8, 9, 10, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 11, 12, 13, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 3, 3, 4, 5, 5, 6, 7, 7, 8, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 rtw8814a_pwrtrk_type5_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 9, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 111, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 11, 11, 10, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 2, 3, 4, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type5_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type5_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type5_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type5_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type5_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type5_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type5_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type5_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type5_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type5_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type5_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type5_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type5_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type5_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type5_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type5_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 rtw8814a_pwrtrk_type7_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type7_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type7_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type7_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type7_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type7_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type7_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type7_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type7_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type7_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type7_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type7_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type7_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type7_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type7_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type7_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type7_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, + 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 3, 4, 4, 5, 6, 7, 7, 8, 10, 11, 12, 13, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 4, 5, 5, 6, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, + 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 10, 11, 11, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 10, 10, 11, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 10, 11, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 3, 4, 4, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 rtw8814a_pwrtrk_type8_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, + 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, + 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8, + 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10, + 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9, + 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type8_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type8_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type8_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type8_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type8_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type8_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type8_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type8_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type8_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type8_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type8_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type8_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type8_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type8_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type8_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type8_2g_cck_a_p, +}; + +static const struct rtw_pwr_seq_cmd init_power_on_8814a[] = { + {0x10c2, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8814a[] = { + {0x0012, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), 0}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), 0}, + {0x0023, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), 0}, + {0x0046, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0062, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), 0}, + {0x0301, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0071, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8814a[] = { + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0006, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), 0}, + {0x00F0, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(7), 0}, + {0x0081, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0x30, 0x20}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(0), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8814a[] = { + {0x0c00, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x04}, + {0x0e00, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x04}, + {0x1002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), 0}, + {0x0002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_US}, + {0x1002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x001F, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0007, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x28}, + {0x0008, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0x02, 0}, + {0x0066, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(7), 0}, + {0x0041, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), 0}, + {0x0042, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x004e, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, + {0x0041, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), 0}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(1), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8814a[] = { + {0x0003, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0080, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x01}, + {0x0081, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x30}, + {0x0045, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0046, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0xff}, + {0x0047, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, + {0x0012, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), 0}, + {0x0023, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, + {0x0008, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0007, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x20}, + {0x001f, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0020, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0021, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0076, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0091, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xA0, 0xA0}, + {0x0070, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[] = { + init_power_on_8814a, + trans_carddis_to_cardemu_8814a, + trans_cardemu_to_act_8814a, + NULL +}; + +const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[] = { + trans_act_to_cardemu_8814a, + trans_cardemu_to_carddis_8814a, + NULL +}; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h new file mode 100644 index 000000000000..69fb87e36c48 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2025 Realtek Corporation + */ + +#ifndef __RTW8814A_TABLE_H__ +#define __RTW8814A_TABLE_H__ + +extern const struct rtw_table rtw8814a_mac_tbl; +extern const struct rtw_table rtw8814a_agc_tbl; +extern const struct rtw_table rtw8814a_bb_tbl; +extern const struct rtw_table rtw8814a_bb_pg_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type0_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type2_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type3_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type4_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type5_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type7_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type8_tbl; +extern const struct rtw_table rtw8814a_rf_a_tbl; +extern const struct rtw_table rtw8814a_rf_b_tbl; +extern const struct rtw_table rtw8814a_rf_c_tbl; +extern const struct rtw_table rtw8814a_rf_d_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type0_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type1_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type2_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type3_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type5_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type7_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type8_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl; +extern const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[]; +extern const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[]; + +#endif From 44e3b0b79351f75541990597310ce1110cade410 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:57 +0200 Subject: [PATCH 330/465] wifi: rtw88: Add rtw8814a.{c,h} JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1a75457846424b77978d0ba1feb836913ed60637 Author: Bitterblue Smith Date: Fri Mar 7 02:24:40 2025 +0200 wifi: rtw88: Add rtw8814a.{c,h} These contain all the logic for the RTL8814A chip. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5d3b8c03-63c1-4f20-860a-89d424badad8@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/rtw8814a.c | 2257 +++++++++++++++++ drivers/net/wireless/realtek/rtw88/rtw8814a.h | 62 + 2 files changed, 2319 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814a.c create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814a.h diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c new file mode 100644 index 000000000000..cfd35d40d46e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c @@ -0,0 +1,2257 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include +#include "main.h" +#include "coex.h" +#include "tx.h" +#include "phy.h" +#include "rtw8814a.h" +#include "rtw8814a_table.h" +#include "rtw88xxa.h" +#include "reg.h" +#include "debug.h" +#include "efuse.h" +#include "regd.h" +#include "usb.h" + +static void rtw8814a_efuse_grant(struct rtw_dev *rtwdev, bool on) +{ + if (on) { + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON); + + rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR); + rtw_write16_set(rtwdev, REG_SYS_CLKR, + BIT_LOADER_CLK_EN | BIT_ANA8M); + } else { + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF); + } +} + +static void rtw8814a_read_rfe_type(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + + if (!(efuse->rfe_option & BIT(7))) + return; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) + efuse->rfe_option = 0; + else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + efuse->rfe_option = 1; +} + +static void rtw8814a_read_amplifier_type(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + + switch (efuse->rfe_option) { + case 1: + /* Internal 2G */ + efuse->pa_type_2g = 0; + efuse->lna_type_2g = 0; + /* External 5G */ + efuse->pa_type_5g = BIT(0); + efuse->lna_type_5g = BIT(3); + break; + case 2 ... 5: + /* External everything */ + efuse->pa_type_2g = BIT(4); + efuse->lna_type_2g = BIT(3); + efuse->pa_type_5g = BIT(0); + efuse->lna_type_5g = BIT(3); + break; + case 6: + efuse->lna_type_5g = BIT(3); + break; + default: + break; + } +} + +static void rtw8814a_read_rf_type(struct rtw_dev *rtwdev, + struct rtw8814a_efuse *map) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct rtw_hal *hal = &rtwdev->hal; + + switch (map->trx_antenna_option) { + case 0xff: /* 4T4R */ + case 0xee: /* 3T3R */ + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + rtwusb->udev->speed != USB_SPEED_SUPER) + hal->rf_type = RF_2T2R; + else + hal->rf_type = RF_3T3R; + + break; + case 0x66: /* 2T2R */ + case 0x6f: /* 2T4R */ + default: + hal->rf_type = RF_2T2R; + break; + } + + hal->rf_path_num = 4; + hal->rf_phy_num = 4; + + if (hal->rf_type == RF_3T3R) { + hal->antenna_rx = BB_PATH_ABC; + hal->antenna_tx = BB_PATH_ABC; + } else { + hal->antenna_rx = BB_PATH_AB; + hal->antenna_tx = BB_PATH_AB; + } +} + +static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + struct rtw_hal *hal = &rtwdev->hal; + + efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) | + BIT(RTW_CHANNEL_WIDTH_40) | + BIT(RTW_CHANNEL_WIDTH_80); + efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT; + + if (hal->rf_type == RF_3T3R) + efuse->hw_cap.nss = 3; + else + efuse->hw_cap.nss = 2; + + rtw_dbg(rtwdev, RTW_DBG_EFUSE, + "hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n", + efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl, + efuse->hw_cap.ant_num, efuse->hw_cap.nss); +} + +static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + struct rtw8814a_efuse *map; + int i; + + if (rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE)) + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, + log_map, rtwdev->chip->log_efuse_size, true); + + map = (struct rtw8814a_efuse *)log_map; + + efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(4)); + efuse->rfe_option = map->rfe_option; + efuse->rf_board_option = map->rf_board_option; + efuse->crystal_cap = map->xtal_k; + efuse->channel_plan = map->channel_plan; + efuse->country_code[0] = map->country_code[0]; + efuse->country_code[1] = map->country_code[1]; + efuse->bt_setting = map->rf_bt_setting; + efuse->regd = map->rf_board_option & 0x7; + efuse->thermal_meter[RF_PATH_A] = map->thermal_meter; + efuse->thermal_meter_k = map->thermal_meter; + efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g; + efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g; + + rtw8814a_read_rfe_type(rtwdev); + rtw8814a_read_amplifier_type(rtwdev); + + /* Override rtw_chip_parameter_setup() */ + rtw8814a_read_rf_type(rtwdev, map); + + rtw8814a_init_hwcap(rtwdev); + + for (i = 0; i < 4; i++) + efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; + + switch (rtw_hci_type(rtwdev)) { + case RTW_HCI_TYPE_USB: + ether_addr_copy(efuse->addr, map->u.mac_addr); + break; + case RTW_HCI_TYPE_PCIE: + ether_addr_copy(efuse->addr, map->e.mac_addr); + break; + case RTW_HCI_TYPE_SDIO: + default: + /* unsupported now */ + return -EOPNOTSUPP; + } + + return 0; +} + +static void rtw8814a_init_rfe_reg(struct rtw_dev *rtwdev) +{ + u8 rfe_option = rtwdev->efuse.rfe_option; + + if (rfe_option == 2 || rfe_option == 1) { + rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf); + rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xf0); + } else if (rfe_option == 0) { + rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf); + rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xc0); + } +} + +#define RTW_TXSCALE_SIZE 37 +static const u32 rtw8814a_txscale_tbl[RTW_TXSCALE_SIZE] = { + 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8, + 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180, + 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab, + 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe +}; + +static u32 rtw8814a_get_bb_swing(struct rtw_dev *rtwdev, u8 band, u8 rf_path) +{ + static const u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6}; + struct rtw_efuse *efuse = &rtwdev->efuse; + u8 tx_bb_swing; + + if (band == RTW_BAND_2G) + tx_bb_swing = efuse->tx_bb_swing_setting_2g; + else + tx_bb_swing = efuse->tx_bb_swing_setting_5g; + + tx_bb_swing >>= 2 * rf_path; + tx_bb_swing &= 0x3; + + return swing2setting[tx_bb_swing]; +} + +static u8 rtw8814a_get_swing_index(struct rtw_dev *rtwdev) +{ + u32 swing, table_value; + u8 i; + + swing = rtw8814a_get_bb_swing(rtwdev, rtwdev->hal.current_band_type, + RF_PATH_A); + + for (i = 0; i < ARRAY_SIZE(rtw8814a_txscale_tbl); i++) { + table_value = rtw8814a_txscale_tbl[i]; + if (swing == table_value) + return i; + } + + return 24; +} + +static void rtw8814a_pwrtrack_init(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 path; + + dm_info->default_ofdm_index = rtw8814a_get_swing_index(rtwdev); + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { + ewma_thermal_init(&dm_info->avg_thermal[path]); + dm_info->delta_power_index[path] = 0; + dm_info->delta_power_index_last[path] = 0; + } + dm_info->pwr_trk_triggered = false; + dm_info->pwr_trk_init_trigger = true; + dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; +} + +static void rtw8814a_config_trx_path(struct rtw_dev *rtwdev) +{ + /* RX CCK disable 2R CCA */ + rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, + BIT_CCK0_2RX | BIT_CCK0_MRC); + /* pathB tx on, path A/C/D tx off */ + rtw_write32_mask(rtwdev, REG_CCK_RX, 0xf0000000, 0x4); + /* pathB rx */ + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5); +} + +static void rtw8814a_config_cck_rx_antenna_init(struct rtw_dev *rtwdev) +{ + /* CCK 2R CCA parameters */ + + /* Disable Ant diversity */ + rtw_write32_mask(rtwdev, REG_RXSB, BIT_RXSB_ANA_DIV, 0x0); + /* Concurrent CCA at LSB & USB */ + rtw_write32_mask(rtwdev, REG_CCA, BIT_CCA_CO, 0); + /* RX path diversity enable */ + rtw_write32_mask(rtwdev, REG_ANTSEL, BIT_ANT_BYCO, 0); + /* r_en_mrc_antsel */ + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_DIS_CO_PATHSEL, 0); + /* MBC weighting */ + rtw_write32_mask(rtwdev, REG_CCA_MF, BIT_MBC_WIN, 1); + /* 2R CCA only */ + rtw_write32_mask(rtwdev, REG_CCKTX, BIT_CMB_CCA_2R, 1); +} + +static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev) +{ + u32 crystal_cap, val32; + u8 val8, rf_path; + + /* power on BB/RF domain */ + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_USBA); + else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) + rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_PCIEA); + + rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2, + BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB); + + /* Power on RF paths A..D */ + val8 = BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB; + rtw_write8(rtwdev, REG_RF_CTRL, val8); + rtw_write8(rtwdev, REG_RF_CTRL1, val8); + rtw_write8(rtwdev, REG_RF_CTRL2, val8); + rtw_write8(rtwdev, REG_RF_CTRL3, val8); + + rtw_load_table(rtwdev, rtwdev->chip->bb_tbl); + rtw_load_table(rtwdev, rtwdev->chip->agc_tbl); + + crystal_cap = rtwdev->efuse.crystal_cap & 0x3F; + crystal_cap |= crystal_cap << 6; + rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000, crystal_cap); + + rtw8814a_config_trx_path(rtwdev); + + for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++) + rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[rf_path]); + + val32 = rtw_read_rf(rtwdev, RF_PATH_A, RF_RCK1_V1, RFREG_MASK); + rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK1_V1, RFREG_MASK, val32); + rtw_write_rf(rtwdev, RF_PATH_C, RF_RCK1_V1, RFREG_MASK, val32); + rtw_write_rf(rtwdev, RF_PATH_D, RF_RCK1_V1, RFREG_MASK, val32); + + rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); + + rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xFF); + + rtw_write32(rtwdev, REG_BAR_MODE_CTRL, 0x0201ffff); + + rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA); + + rtw_write8(rtwdev, REG_NAV_CTRL + 2, 0); + + rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(5)); + + rtw8814a_config_cck_rx_antenna_init(rtwdev); + + rtw_phy_init(rtwdev); + rtw8814a_pwrtrack_init(rtwdev); + + rtw8814a_init_rfe_reg(rtwdev); + + rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT(3)); + + rtw_write8(rtwdev, REG_NAV_CTRL + 2, 235); + + /* enable Tx report. */ + rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, 0x1F); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) { + /* Reset USB mode switch setting */ + rtw_write8(rtwdev, REG_SYS_SDIO_CTRL, 0x0); + rtw_write8(rtwdev, REG_ACLK_MON, 0x0); + } +} + +static void rtw8814ae_enable_rf_1_2v(struct rtw_dev *rtwdev) +{ + /* This is for fullsize card, because GPIO7 there is floating. + * We should pull GPIO7 high to enable RF 1.2V Switch Power Supply + */ + + /* 1. set 0x40[1:0] to 0, BIT_GPIOSEL=0, select pin as GPIO */ + rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(1) | BIT(0)); + + /* 2. set 0x44[31] to 0 + * mode=0: data port; + * mode=1 and BIT_GPIO_IO_SEL=0: interrupt mode; + */ + rtw_write8_clr(rtwdev, REG_GPIO_PIN_CTRL + 3, BIT(7)); + + /* 3. data mode + * 3.1 set 0x44[23] to 1 + * sel=0: input; + * sel=1: output; + */ + rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 2, BIT(7)); + + /* 3.2 set 0x44[15] to 1 + * output high value; + */ + rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 1, BIT(7)); +} + +static int rtw8814a_mac_init(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_write16(rtwdev, REG_CR, + MAC_TRX_ENABLE | BIT_MAC_SEC_EN | BIT_32K_CAL_TMR_EN); + + rtw_load_table(rtwdev, rtwdev->chip->mac_tbl); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + rtw_write8(rtwdev, REG_AUTO_LLT_V1 + 3, + rtwdev->chip->usb_tx_agg_desc_num << 1); + + rtw_write32(rtwdev, REG_HIMR0, 0); + rtw_write32(rtwdev, REG_HIMR1, 0); + + rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xfffff); + + rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030); + + rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff); + rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400); + rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff); + + rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x36); + rtw_write8(rtwdev, REG_MAX_AGGR_NUM + 1, 0x36); + + /* Set Spec SIFS (used in NAV) */ + rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a); + rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a); + + /* Set SIFS for CCK */ + rtw_write16(rtwdev, REG_SIFS, 0x100a); + + /* Set SIFS for OFDM */ + rtw_write16(rtwdev, REG_SIFS + 2, 0x100a); + + /* TXOP */ + rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B); + rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F); + rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324); + rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226); + + rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7)); + + rtw_write8(rtwdev, REG_ACKTO, 0x80); + + rtw_write16(rtwdev, REG_BCN_CTRL, + BIT_DIS_TSF_UDT | (BIT_DIS_TSF_UDT << 8)); + rtw_write32_mask(rtwdev, REG_TBTT_PROHIBIT, 0xfffff, WLAN_TBTT_TIME); + rtw_write8(rtwdev, REG_DRVERLYINT, 0x05); + rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); + rtw_write16(rtwdev, REG_BCNTCFG, 0x4413); + rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xFF); + + rtw_write32(rtwdev, REG_FAST_EDCA_VOVI_SETTING, 0x08070807); + rtw_write32(rtwdev, REG_FAST_EDCA_BEBK_SETTING, 0x08070807); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + rtwusb->udev->speed == USB_SPEED_SUPER) { + /* Disable U1/U2 Mode to avoid 2.5G spur in USB3.0. */ + rtw_write8_clr(rtwdev, REG_USB_MOD, BIT(4) | BIT(3)); + /* To avoid usb 3.0 H2C fail. */ + rtw_write16(rtwdev, 0xf002, 0); + + rtw_write8_clr(rtwdev, REG_SW_AMPDU_BURST_MODE_CTRL, + BIT_PRE_TX_CMD); + } else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) { + rtw8814ae_enable_rf_1_2v(rtwdev); + + /* Force the antenna b to wifi. */ + rtw_write8_set(rtwdev, REG_PAD_CTRL1, BIT(2)); + rtw_write8_set(rtwdev, REG_PAD_CTRL1 + 1, BIT(0)); + rtw_write8_set(rtwdev, REG_LED_CFG + 3, + (BIT(27) | BIT_DPDT_WL_SEL) >> 24); + } + + return 0; +} + +static void rtw8814a_set_rfe_reg_24g(struct rtw_dev *rtwdev) +{ + switch (rtwdev->efuse.rfe_option) { + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77707770); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x72); + + break; + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x77); + + break; + case 0: + default: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + /* Is it not necessary to set REG_RFE_PINMUX_D ? */ + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x77); + + break; + } +} + +static void rtw8814a_set_rfe_reg_5g(struct rtw_dev *rtwdev) +{ + switch (rtwdev->efuse.rfe_option) { + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x37); + + break; + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x33); + + break; + case 0: + default: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x54775477); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x54); + + break; + } +} + +static void rtw8814a_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band) +{ + rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_A)); + rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_B)); + rtw_write32_mask(rtwdev, REG_TXSCALE_C, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_C)); + rtw_write32_mask(rtwdev, REG_TXSCALE_D, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_D)); + rtw8814a_pwrtrack_init(rtwdev); +} + +static void rtw8814a_set_bw_reg_adc(struct rtw_dev *rtwdev, u8 bw) +{ + u32 adc = 0; + + if (bw == RTW_CHANNEL_WIDTH_20) + adc = 0; + else if (bw == RTW_CHANNEL_WIDTH_40) + adc = 1; + else if (bw == RTW_CHANNEL_WIDTH_80) + adc = 2; + + rtw_write32_mask(rtwdev, REG_ADCCLK, BIT(1) | BIT(0), adc); +} + +static void rtw8814a_set_bw_reg_agc(struct rtw_dev *rtwdev, u8 new_band, u8 bw) +{ + u32 agc = 7; + + if (bw == RTW_CHANNEL_WIDTH_20) { + agc = 6; + } else if (bw == RTW_CHANNEL_WIDTH_40) { + if (new_band == RTW_BAND_5G) + agc = 8; + else + agc = 7; + } else if (bw == RTW_CHANNEL_WIDTH_80) { + agc = 3; + } + + rtw_write32_mask(rtwdev, REG_CCASEL, 0xf000, agc); +} + +static void rtw8814a_switch_band(struct rtw_dev *rtwdev, u8 new_band, u8 bw) +{ + /* Clear 0x1000[16], When this bit is set to 0, CCK and OFDM + * are disabled, and clock are gated. Otherwise, CCK and OFDM + * are enabled. + */ + rtw_write8_clr(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB); + + if (new_band == RTW_BAND_2G) { + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 0); + + rtw8814a_set_rfe_reg_24g(rtwdev); + + rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x2); + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5); + + rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x3); + + rtw_write8(rtwdev, REG_CCK_CHECK, 0); + + rtw_write32_mask(rtwdev, 0xa80, BIT(18), 0); + } else { + rtw_write8(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN); + + /* Enable CCK Tx function, even when CCK is off */ + rtw_write32_mask(rtwdev, 0xa80, BIT(18), 1); + + rtw8814a_set_rfe_reg_5g(rtwdev); + + rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0xf); + + rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x2); + } + + rtw8814a_set_channel_bb_swing(rtwdev, new_band); + + rtw8814a_set_bw_reg_adc(rtwdev, bw); + rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw); + + rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB); +} + +static void rtw8814a_switch_channel(struct rtw_dev *rtwdev, u8 channel) +{ + struct rtw_hal *hal = &rtwdev->hal; + u32 fc_area, rf_mod_ag, cfgch; + u8 path; + + switch (channel) { + case 36 ... 48: + fc_area = 0x494; + break; + case 50 ... 64: + fc_area = 0x453; + break; + case 100 ... 116: + fc_area = 0x452; + break; + default: + if (channel >= 118) + fc_area = 0x412; + else + fc_area = 0x96a; + break; + } + + rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, fc_area); + + for (path = 0; path < hal->rf_path_num; path++) { + switch (channel) { + case 36 ... 64: + rf_mod_ag = 0x101; + break; + case 100 ... 140: + rf_mod_ag = 0x301; + break; + default: + if (channel > 140) + rf_mod_ag = 0x501; + else + rf_mod_ag = 0x000; + break; + } + + cfgch = (rf_mod_ag << 8) | channel; + + rtw_write_rf(rtwdev, path, RF_CFGCH, + RF18_RFSI_MASK | RF18_BAND_MASK | RF18_CHANNEL_MASK, cfgch); + } + + switch (channel) { + case 36 ... 64: + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 1); + break; + case 100 ... 144: + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 2); + break; + default: + if (channel >= 149) + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 3); + + break; + } +} + +static void rtw8814a_24g_cck_tx_dfir(struct rtw_dev *rtwdev, u8 channel) +{ + if (channel >= 1 && channel <= 11) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1317); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000204); + } else if (channel >= 12 && channel <= 13) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1217); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000305); + } else if (channel == 14) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x00000E17); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000000); + } +} + +static void rtw8814a_set_bw_reg_mac(struct rtw_dev *rtwdev, u8 bw) +{ + u16 val16 = rtw_read16(rtwdev, REG_WMAC_TRXPTCL_CTL); + + val16 &= ~BIT_RFMOD; + if (bw == RTW_CHANNEL_WIDTH_80) + val16 |= BIT_RFMOD_80M; + else if (bw == RTW_CHANNEL_WIDTH_40) + val16 |= BIT_RFMOD_40M; + + rtw_write16(rtwdev, REG_WMAC_TRXPTCL_CTL, val16); +} + +static void rtw8814a_set_bw_rf(struct rtw_dev *rtwdev, u8 bw) +{ + u8 path; + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { + switch (bw) { + case RTW_CHANNEL_WIDTH_5: + case RTW_CHANNEL_WIDTH_10: + case RTW_CHANNEL_WIDTH_20: + default: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 3); + break; + case RTW_CHANNEL_WIDTH_40: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 1); + break; + case RTW_CHANNEL_WIDTH_80: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 0); + break; + } + } +} + +static void rtw8814a_adc_clk(struct rtw_dev *rtwdev) +{ + static const u32 rxiqc_reg[2][4] = { + { REG_RX_IQC_AB_A, REG_RX_IQC_AB_B, + REG_RX_IQC_AB_C, REG_RX_IQC_AB_D }, + { REG_RX_IQC_CD_A, REG_RX_IQC_CD_B, + REG_RX_IQC_CD_C, REG_RX_IQC_CD_D } + }; + u32 bb_reg_8fc, bb_reg_808, rxiqc[4]; + u32 i = 0, mac_active = 1; + u8 mac_reg_522; + + if (rtwdev->hal.cut_version != RTW_CHIP_VER_CUT_A) + return; + + /* 1 Step1. MAC TX pause */ + mac_reg_522 = rtw_read8(rtwdev, REG_TXPAUSE); + bb_reg_8fc = rtw_read32(rtwdev, REG_DBGSEL); + bb_reg_808 = rtw_read32(rtwdev, REG_RXPSEL); + rtw_write8(rtwdev, REG_TXPAUSE, 0x3f); + + /* 1 Step 2. Backup rxiqc & rxiqc = 0 */ + for (i = 0; i < 4; i++) { + rxiqc[i] = rtw_read32(rtwdev, rxiqc_reg[0][i]); + rtw_write32(rtwdev, rxiqc_reg[0][i], 0x0); + rtw_write32(rtwdev, rxiqc_reg[1][i], 0x0); + } + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x3); + i = 0; + + /* 1 Step 3. Monitor MAC IDLE */ + rtw_write32(rtwdev, REG_DBGSEL, 0x0); + while (mac_active) { + mac_active = rtw_read32(rtwdev, REG_DBGRPT) & 0x803e0008; + i++; + if (i > 1000) + break; + } + + /* 1 Step 4. ADC clk flow */ + rtw_write8(rtwdev, REG_RXPSEL, 0x11); + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1); + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x3); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1); + + /* 0xc1c/0xe1c/0x181c/0x1a1c[4] must=1 to ensure table can be + * written when bbrstb=0 + * 0xc60/0xe60/0x1860/0x1a60[15] always = 1 after this line + * 0xc60/0xe60/0x1860/0x1a60[14] always = 0 bcz its error in A-cut + */ + + /* power_off/clk_off @ anapar_state=idle mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x01808003); + + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0); + /* [19] = 1 to turn off ADC */ + rtw_write32(rtwdev, REG_CK_MONHA, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHB, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHC, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHD, 0x0D080058); + + /* power_on/clk_off */ + /* [19] = 0 to turn on ADC */ + rtw_write32(rtwdev, REG_CK_MONHA, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHB, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHC, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHD, 0x0D000058); + + /* power_on/clk_on @ anapar_state=BT mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032); + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x1); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1); + + /* recover original setting @ anapar_state=BT mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032); + + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003); + + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0); + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x0); + + /* 1 Step 5. Recover MAC TX & IQC */ + rtw_write8(rtwdev, REG_TXPAUSE, mac_reg_522); + rtw_write32(rtwdev, REG_DBGSEL, bb_reg_8fc); + rtw_write32(rtwdev, REG_RXPSEL, bb_reg_808); + for (i = 0; i < 4; i++) { + rtw_write32(rtwdev, rxiqc_reg[0][i], rxiqc[i]); + rtw_write32(rtwdev, rxiqc_reg[1][i], 0x01000000); + } + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x0); +} + +static void rtw8814a_spur_calibration_ch140(struct rtw_dev *rtwdev, u8 channel) +{ + struct rtw_hal *hal = &rtwdev->hal; + + /* Add for 8814AE module ch140 MP Rx */ + if (channel == 140) { + if (hal->ch_param[0] == 0) + hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL); + if (hal->ch_param[1] == 0) + hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH); + + rtw_write32(rtwdev, REG_CCASEL, 0x75438170); + rtw_write32(rtwdev, REG_PDMFTH, 0x79a18a0a); + } else { + if (rtw_read32(rtwdev, REG_CCASEL) == 0x75438170 && + hal->ch_param[0] != 0) + rtw_write32(rtwdev, REG_CCASEL, hal->ch_param[0]); + + if (rtw_read32(rtwdev, REG_PDMFTH) == 0x79a18a0a && + hal->ch_param[1] != 0) + rtw_write32(rtwdev, REG_PDMFTH, hal->ch_param[1]); + + hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL); + hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH); + } +} + +static void rtw8814a_set_nbi_reg(struct rtw_dev *rtwdev, u32 tone_idx) +{ + /* tone_idx X 10 */ + static const u32 nbi_128[] = { + 25, 55, 85, 115, 135, + 155, 185, 205, 225, 245, + 265, 285, 305, 335, 355, + 375, 395, 415, 435, 455, + 485, 505, 525, 555, 585, 615, 635 + }; + u32 reg_idx = 0; + u32 i; + + for (i = 0; i < ARRAY_SIZE(nbi_128); i++) { + if (tone_idx < nbi_128[i]) { + reg_idx = i + 1; + break; + } + } + + rtw_write32_mask(rtwdev, REG_NBI_SETTING, 0xfc000, reg_idx); +} + +static void rtw8814a_nbi_setting(struct rtw_dev *rtwdev, u32 ch, u32 f_intf) +{ + u32 fc, int_distance, tone_idx; + + fc = 2412 + (ch - 1) * 5; + int_distance = abs_diff(fc, f_intf); + + /* 10 * (int_distance / 0.3125) */ + tone_idx = int_distance << 5; + + rtw8814a_set_nbi_reg(rtwdev, tone_idx); + + rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 1); +} + +static void rtw8814a_spur_nbi_setting(struct rtw_dev *rtwdev) +{ + u8 primary_channel = rtwdev->hal.primary_channel; + u8 rfe_type = rtwdev->efuse.rfe_option; + + if (rfe_type != 0 && rfe_type != 1 && rfe_type != 6 && rfe_type != 7) + return; + + if (primary_channel == 14) + rtw8814a_nbi_setting(rtwdev, primary_channel, 2480); + else if (primary_channel >= 4 && primary_channel <= 8) + rtw8814a_nbi_setting(rtwdev, primary_channel, 2440); + else + rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 0); +} + +/* A workaround to eliminate the 5280 MHz & 5600 MHz & 5760 MHz spur of 8814A */ +static void rtw8814a_spur_calibration(struct rtw_dev *rtwdev, u8 channel, u8 bw) +{ + u8 rfe_type = rtwdev->efuse.rfe_option; + bool reset_nbi_csi = true; + + if (rfe_type == 0) { + switch (bw) { + case RTW_CHANNEL_WIDTH_40: + if (channel == 54 || channel == 118) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x3e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } else if (channel == 151) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_80: + if (channel == 58 || channel == 122) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x3a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(0), 1); + + reset_nbi_csi = false; + } else if (channel == 155) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x5a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_20: + if (channel == 153) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(16), 1); + + reset_nbi_csi = false; + } + + rtw8814a_spur_calibration_ch140(rtwdev, channel); + break; + default: + break; + } + } else if (rfe_type == 1 || rfe_type == 2) { + switch (bw) { + case RTW_CHANNEL_WIDTH_20: + if (channel == 153) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1E >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(16), 1); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_40: + if (channel == 151) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_80: + if (channel == 155) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x5a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + default: + break; + } + } + + if (reset_nbi_csi) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0xfc >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, BIT(0), 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + } + + rtw8814a_spur_nbi_setting(rtwdev); +} + +static void rtw8814a_set_bw_mode(struct rtw_dev *rtwdev, u8 new_band, + u8 channel, u8 bw, u8 primary_chan_idx) +{ + u8 txsc40 = 0, txsc20, txsc; + + rtw8814a_set_bw_reg_mac(rtwdev, bw); + + txsc20 = primary_chan_idx; + if (bw == RTW_CHANNEL_WIDTH_80) { + if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST) + txsc40 = RTW_SC_40_UPPER; + else + txsc40 = RTW_SC_40_LOWER; + } + + txsc = BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40); + rtw_write8(rtwdev, REG_DATA_SC, txsc); + + rtw8814a_set_bw_reg_adc(rtwdev, bw); + rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw); + + if (bw == RTW_CHANNEL_WIDTH_80) { + rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc); + } else if (bw == RTW_CHANNEL_WIDTH_40) { + rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc); + + if (txsc == RTW_SC_20_UPPER) + rtw_write32_set(rtwdev, REG_RXSB, BIT(4)); + else + rtw_write32_clr(rtwdev, REG_RXSB, BIT(4)); + } + + rtw8814a_set_bw_rf(rtwdev, bw); + + rtw8814a_adc_clk(rtwdev); + + rtw8814a_spur_calibration(rtwdev, channel, bw); +} + +static void rtw8814a_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, + u8 primary_chan_idx) +{ + u8 old_band, new_band; + + if (rtw_read8(rtwdev, REG_CCK_CHECK) & BIT_CHECK_CCK_EN) + old_band = RTW_BAND_5G; + else + old_band = RTW_BAND_2G; + + if (channel > 14) + new_band = RTW_BAND_5G; + else + new_band = RTW_BAND_2G; + + if (new_band != old_band) + rtw8814a_switch_band(rtwdev, new_band, bw); + + rtw8814a_switch_channel(rtwdev, channel); + + rtw8814a_24g_cck_tx_dfir(rtwdev, channel); + + rtw8814a_set_bw_mode(rtwdev, new_band, channel, bw, primary_chan_idx); +} + +static s8 rtw8814a_cck_rx_pwr(u8 lna_idx, u8 vga_idx) +{ + s8 rx_pwr_all = 0; + + switch (lna_idx) { + case 7: + rx_pwr_all = -38 - 2 * vga_idx; + break; + case 5: + rx_pwr_all = -28 - 2 * vga_idx; + break; + case 3: + rx_pwr_all = -8 - 2 * vga_idx; + break; + case 2: + rx_pwr_all = -1 - 2 * vga_idx; + break; + default: + break; + } + + return rx_pwr_all; +} + +static void rtw8814a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, + struct rtw_rx_pkt_stat *pkt_stat) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + struct rtw_jaguar_phy_status_rpt *rpt; + u8 gain[RTW_RF_PATH_MAX], rssi, i; + s8 rx_pwr_db, middle1, middle2; + s8 snr[RTW_RF_PATH_MAX]; + s8 evm[RTW_RF_PATH_MAX]; + u8 rfmode, subchannel; + u8 lna, vga; + s8 cfo[2]; + + rpt = (struct rtw_jaguar_phy_status_rpt *)phy_status; + + pkt_stat->bw = RTW_CHANNEL_WIDTH_20; + + if (pkt_stat->rate <= DESC_RATE11M) { + lna = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_LNA_IDX); + vga = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_VGA_IDX); + + rx_pwr_db = rtw8814a_cck_rx_pwr(lna, vga); + + pkt_stat->rx_power[RF_PATH_A] = rx_pwr_db; + pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); + dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; + pkt_stat->signal_power = rx_pwr_db; + } else { /* OFDM rate */ + gain[RF_PATH_A] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_A); + gain[RF_PATH_B] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_B); + gain[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_GAIN_C); + gain[RF_PATH_D] = le32_get_bits(rpt->w6, RTW_JGRPHY_W6_GAIN_D); + + snr[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXSNR_A); + snr[RF_PATH_B] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXSNR_B); + snr[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_C); + snr[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_D); + + evm[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_1); + evm[RF_PATH_B] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_2); + evm[RF_PATH_C] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXEVM_3); + evm[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXEVM_4); + + if (pkt_stat->rate <= DESC_RATE54M) + evm[RF_PATH_A] = le32_get_bits(rpt->w6, + RTW_JGRPHY_W6_SIGEVM); + + for (i = RF_PATH_A; i < RTW_RF_PATH_MAX; i++) { + pkt_stat->rx_power[i] = gain[i] - 110; + + rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[i], 1); + dm_info->rssi[i] = rssi; + + pkt_stat->rx_snr[i] = snr[i]; + dm_info->rx_snr[i] = snr[i] >> 1; + + pkt_stat->rx_evm[i] = evm[i]; + evm[i] = max_t(s8, -127, evm[i]); + dm_info->rx_evm_dbm[i] = abs(evm[i]) >> 1; + } + + rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, + RTW_RF_PATH_MAX); + pkt_stat->rssi = rssi; + + /* When power saving is enabled the hardware sometimes + * reports unbelievably high gain for paths A and C + * (e.g. one frame 64 68 68 72, the next frame 106 66 88 72, + * the next 66 66 68 72), so use the second lowest gain + * instead of the highest. + */ + middle1 = max(min(gain[RF_PATH_A], gain[RF_PATH_B]), + min(gain[RF_PATH_C], gain[RF_PATH_D])); + middle2 = min(max(gain[RF_PATH_A], gain[RF_PATH_B]), + max(gain[RF_PATH_C], gain[RF_PATH_D])); + rx_pwr_db = min(middle1, middle2); + rx_pwr_db -= 110; + pkt_stat->signal_power = rx_pwr_db; + + rfmode = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_R_RFMOD); + subchannel = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_SUB_CHNL); + + if (rfmode == 1 && subchannel == 0) { + pkt_stat->bw = RTW_CHANNEL_WIDTH_40; + } else if (rfmode == 2) { + if (subchannel == 0) + pkt_stat->bw = RTW_CHANNEL_WIDTH_80; + else if (subchannel == 9 || subchannel == 10) + pkt_stat->bw = RTW_CHANNEL_WIDTH_40; + } + + cfo[RF_PATH_A] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_A); + cfo[RF_PATH_B] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_B); + + for (i = RF_PATH_A; i < 2; i++) { + pkt_stat->cfo_tail[i] = cfo[i]; + dm_info->cfo_tail[i] = (cfo[i] * 5) >> 1; + } + } +} + +static void +rtw8814a_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +{ + struct rtw_hal *hal = &rtwdev->hal; + u32 txagc_table_wd; + u8 rate, pwr_index; + int j; + + for (j = 0; j < rtw_rate_size[rs]; j++) { + rate = rtw_rate_section[rs][j]; + + pwr_index = hal->tx_pwr_tbl[path][rate] + 2; + if (pwr_index > rtwdev->chip->max_power_index) + pwr_index = rtwdev->chip->max_power_index; + + txagc_table_wd = 0x00801000; + txagc_table_wd |= (pwr_index << 24) | (path << 8) | rate; + + rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd); + + /* first time to turn on the txagc table + * second to write the addr0 + */ + if (rate == DESC_RATE1M) + rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd); + } +} + +static void rtw8814a_set_tx_power_index(struct rtw_dev *rtwdev) +{ + struct rtw_hal *hal = &rtwdev->hal; + int path; + + for (path = 0; path < hal->rf_path_num; path++) { + if (hal->current_band_type == RTW_BAND_2G) + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_CCK); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_OFDM); + + if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) + continue; + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_1S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_1S); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_2S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_2S); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_3S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_3S); + } +} + +static void rtw8814a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) +{ +} + +static void rtw8814a_false_alarm_statistics(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u32 cck_fa_cnt, ofdm_fa_cnt; + u32 crc32_cnt, cca32_cnt; + u32 cck_enable; + + cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28); + cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK); + ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM); + + dm_info->cck_fa_cnt = cck_fa_cnt; + dm_info->ofdm_fa_cnt = ofdm_fa_cnt; + dm_info->total_fa_cnt = ofdm_fa_cnt; + if (cck_enable) + dm_info->total_fa_cnt += cck_fa_cnt; + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK); + dm_info->cck_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->cck_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM); + dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT); + dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT); + dm_info->vht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->vht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM); + dm_info->ofdm_cca_cnt = u32_get_bits(cca32_cnt, MASKHWORD); + dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt; + if (cck_enable) { + cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK); + dm_info->cck_cca_cnt = u32_get_bits(cca32_cnt, MASKLWORD); + dm_info->total_cca_cnt += dm_info->cck_cca_cnt; + } + + rtw_write32_set(rtwdev, REG_FAS, BIT(17)); + rtw_write32_clr(rtwdev, REG_FAS, BIT(17)); + rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, BIT(15)); + rtw_write32_set(rtwdev, REG_CCK0_FAREPORT, BIT(15)); + rtw_write32_set(rtwdev, REG_CNTRST, BIT(0)); + rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0)); +} + +#define MAC_REG_NUM_8814 2 +#define BB_REG_NUM_8814 14 +#define RF_REG_NUM_8814 1 + +static void rtw8814a_iqk_backup_mac_bb(struct rtw_dev *rtwdev, + u32 *mac_backup, u32 *bb_backup, + const u32 *mac_regs, + const u32 *bb_regs) +{ + u32 i; + + /* save MACBB default value */ + for (i = 0; i < MAC_REG_NUM_8814; i++) + mac_backup[i] = rtw_read32(rtwdev, mac_regs[i]); + + for (i = 0; i < BB_REG_NUM_8814; i++) + bb_backup[i] = rtw_read32(rtwdev, bb_regs[i]); +} + +static void rtw8814a_iqk_backup_rf(struct rtw_dev *rtwdev, + u32 rf_backup[][4], const u32 *rf_regs) +{ + u32 i; + + /* Save RF Parameters */ + for (i = 0; i < RF_REG_NUM_8814; i++) { + rf_backup[i][RF_PATH_A] = rtw_read_rf(rtwdev, RF_PATH_A, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_B] = rtw_read_rf(rtwdev, RF_PATH_B, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_C] = rtw_read_rf(rtwdev, RF_PATH_C, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_D] = rtw_read_rf(rtwdev, RF_PATH_D, + rf_regs[i], RFREG_MASK); + } +} + +static void rtw8814a_iqk_afe_setting(struct rtw_dev *rtwdev, bool do_iqk) +{ + if (do_iqk) { + /* IQK AFE setting RX_WAIT_CCA mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x0e808003); + } else { + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003); + } + + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1); + + rtw_write8_set(rtwdev, REG_GNT_BT, BIT(2) | BIT(1)); + rtw_write8_clr(rtwdev, REG_GNT_BT, BIT(2) | BIT(1)); + + rtw_write32_set(rtwdev, REG_CCK_RPT_FORMAT, BIT(2)); + rtw_write32_clr(rtwdev, REG_CCK_RPT_FORMAT, BIT(2)); +} + +static void rtw8814a_iqk_restore_mac_bb(struct rtw_dev *rtwdev, + u32 *mac_backup, u32 *bb_backup, + const u32 *mac_regs, + const u32 *bb_regs) +{ + u32 i; + + /* Reload MacBB Parameters */ + for (i = 0; i < MAC_REG_NUM_8814; i++) + rtw_write32(rtwdev, mac_regs[i], mac_backup[i]); + + for (i = 0; i < BB_REG_NUM_8814; i++) + rtw_write32(rtwdev, bb_regs[i], bb_backup[i]); +} + +static void rtw8814a_iqk_restore_rf(struct rtw_dev *rtwdev, + const u32 rf_backup[][4], + const u32 *rf_regs) +{ + u32 i; + + rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_C, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_D, RF_LUTWE, RFREG_MASK, 0x0); + + rtw_write_rf(rtwdev, RF_PATH_A, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_B, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_C, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_D, RF_RXBB2, RFREG_MASK, 0x88001); + + for (i = 0; i < RF_REG_NUM_8814; i++) { + rtw_write_rf(rtwdev, RF_PATH_A, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_A]); + rtw_write_rf(rtwdev, RF_PATH_B, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_B]); + rtw_write_rf(rtwdev, RF_PATH_C, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_C]); + rtw_write_rf(rtwdev, RF_PATH_D, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_D]); + } +} + +static void rtw8814a_iqk_reset_nctl(struct rtw_dev *rtwdev) +{ + rtw_write32(rtwdev, 0x1b00, 0xf8000000); + rtw_write32(rtwdev, 0x1b80, 0x00000006); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000); + rtw_write32(rtwdev, 0x1b80, 0x00000002); +} + +static void rtw8814a_iqk_configure_mac(struct rtw_dev *rtwdev) +{ + rtw_write8(rtwdev, REG_TXPAUSE, 0x3f); + rtw_write32_clr(rtwdev, REG_BCN_CTRL, + (BIT_EN_BCN_FUNCTION << 8) | BIT_EN_BCN_FUNCTION); + + /* RX ante off */ + rtw_write8(rtwdev, REG_RXPSEL, 0x00); + /* CCA off */ + rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf, 0xe); + /* CCK RX path off */ + rtw_write32_set(rtwdev, REG_PRECTRL, BIT_IQ_WGT); + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777); + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, BIT_RFE_SELSW0_D, 0x77); + rtw_write32_mask(rtwdev, REG_PSD, BIT_PSD_INI, 0x0); + + rtw_write32_mask(rtwdev, REG_RFE_INV0, 0xf, 0x0); +} + +static void rtw8814a_lok_one_shot(struct rtw_dev *rtwdev, u8 path) +{ + u32 lok_temp1, lok_temp2; + bool lok_ready; + u8 ii; + + /* ADC Clock source */ + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + /* LOK: CMD ID = 0 + * {0xf8000011, 0xf8000021, 0xf8000041, 0xf8000081} + */ + rtw_write32(rtwdev, 0x1b00, 0xf8000001 | (BIT(path) << 4)); + + usleep_range(1000, 1100); + + if (read_poll_timeout(!rtw_read32_mask, lok_ready, lok_ready, + 1000, 10000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, "==>S%d LOK timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + + rtw_write_rf(rtwdev, path, RF_DTXLOK, RFREG_MASK, 0x08400); + + return; + } + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + rtw_write32(rtwdev, 0x1bd4, 0x003f0001); + + lok_temp2 = rtw_read32_mask(rtwdev, 0x1bfc, 0x003e0000); + lok_temp2 = (lok_temp2 + 0x10) & 0x1f; + + lok_temp1 = rtw_read32_mask(rtwdev, 0x1bfc, 0x0000003e); + lok_temp1 = (lok_temp1 + 0x10) & 0x1f; + + for (ii = 1; ii < 5; ii++) { + lok_temp1 += (lok_temp1 & BIT(4 - ii)) << (ii * 2); + lok_temp2 += (lok_temp2 & BIT(4 - ii)) << (ii * 2); + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, + "path %d lok_temp1 = %#x, lok_temp2 = %#x\n", + path, lok_temp1 >> 4, lok_temp2 >> 4); + + rtw_write_rf(rtwdev, path, RF_DTXLOK, 0x07c00, lok_temp1 >> 4); + rtw_write_rf(rtwdev, path, RF_DTXLOK, 0xf8000, lok_temp2 >> 4); +} + +static void rtw8814a_iqk_tx_one_shot(struct rtw_dev *rtwdev, u8 path, + u32 *tx_matrix, bool *tx_ok) +{ + u8 bw = rtwdev->hal.current_band_width; + u8 cal_retry; + u32 iqk_cmd; + + for (cal_retry = 0; cal_retry < 4; cal_retry++) { + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + + iqk_cmd = 0xf8000001 | ((bw + 3) << 8) | (BIT(path) << 4); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "TXK_Trigger = %#x\n", iqk_cmd); + + rtw_write32(rtwdev, 0x1b00, iqk_cmd); + + usleep_range(10000, 11000); + + if (read_poll_timeout(!rtw_read32_mask, *tx_ok, *tx_ok, + 1000, 20000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, + "tx iqk S%d timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + } else { + *tx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26)); + + if (*tx_ok) + break; + } + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b00 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b00)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b08 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b08)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> cal_retry = %x\n", + path, cal_retry); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + + if (*tx_ok) { + *tx_matrix = rtw_read32(rtwdev, 0x1b38); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n", + path, *tx_matrix); + } +} + +static void rtw8814a_iqk_rx_one_shot(struct rtw_dev *rtwdev, u8 path, + u32 *tx_matrix, bool *tx_ok) +{ + static const u16 iqk_apply[RTW_RF_PATH_MAX] = { + REG_TXAGCIDX, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D + }; + u8 band = rtwdev->hal.current_band_type; + u8 bw = rtwdev->hal.current_band_width; + u32 rx_matrix; + u8 cal_retry; + u32 iqk_cmd; + bool rx_ok; + + for (cal_retry = 0; cal_retry < 4; cal_retry++) { + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + + if (band == RTW_BAND_2G) { + rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x1); + rtw_write_rf(rtwdev, path, RF_GAINTX, 0xfffff, 0x51ce1); + + switch (path) { + case 0: + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_B, + 0x54775477); + break; + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_C, + 0x54775477); + break; + case 3: + rtw_write32(rtwdev, REG_RFE_INVSEL_D, 0x75400000); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, + 0x77777777); + break; + } + } + + iqk_cmd = 0xf8000001 | ((9 - bw) << 8) | (BIT(path) << 4); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "RXK_Trigger = 0x%x\n", iqk_cmd); + + rtw_write32(rtwdev, 0x1b00, iqk_cmd); + + usleep_range(10000, 11000); + + if (read_poll_timeout(!rtw_read32_mask, rx_ok, rx_ok, + 1000, 20000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, + "rx iqk S%d timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + } else { + rx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26)); + + if (rx_ok) + break; + } + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b00 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b00)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b08 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b08)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> cal_retry = %x\n", + path, cal_retry); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + + if (rx_ok) { + rtw_write32(rtwdev, 0x1b3c, 0x20000000); + rx_matrix = rtw_read32(rtwdev, 0x1b3c); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n", + path, rx_matrix); + } + + if (*tx_ok) + rtw_write32(rtwdev, 0x1b38, *tx_matrix); + else + rtw_write32_mask(rtwdev, iqk_apply[path], BIT(0), 0x0); + + if (!rx_ok) + rtw_write32_mask(rtwdev, iqk_apply[path], + BIT(11) | BIT(10), 0x0); + + if (band == RTW_BAND_2G) + rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x0); +} + +static void rtw8814a_iqk(struct rtw_dev *rtwdev) +{ + u8 band = rtwdev->hal.current_band_type; + u8 bw = rtwdev->hal.current_band_width; + u32 tx_matrix[RTW_RF_PATH_MAX]; + bool tx_ok[RTW_RF_PATH_MAX]; + u8 path; + + rtw_dbg(rtwdev, RTW_DBG_RFK, "IQK band = %d GHz bw = %d MHz\n", + band == RTW_BAND_2G ? 2 : 5, (1 << (bw + 1)) * 10); + + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_B, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_C, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_D, RF_TXMOD, BIT(19), 0x1); + + rtw_write32_mask(rtwdev, REG_TXAGCIDX, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_B, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_C, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_D, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + + if (band == RTW_BAND_5G) + rtw_write32(rtwdev, 0x1b00, 0xf8000ff1); + else + rtw_write32(rtwdev, 0x1b00, 0xf8000ef1); + + usleep_range(1000, 1100); + + rtw_write32(rtwdev, 0x810, 0x20101063); + rtw_write32(rtwdev, REG_DAC_RSTB, 0x0B00C000); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_lok_one_shot(rtwdev, path); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_iqk_tx_one_shot(rtwdev, path, + &tx_matrix[path], &tx_ok[path]); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_iqk_rx_one_shot(rtwdev, path, + &tx_matrix[path], &tx_ok[path]); +} + +static void rtw8814a_do_iqk(struct rtw_dev *rtwdev) +{ + static const u32 backup_mac_reg[MAC_REG_NUM_8814] = {0x520, 0x550}; + static const u32 backup_bb_reg[BB_REG_NUM_8814] = { + 0xa14, 0x808, 0x838, 0x90c, 0x810, 0xcb0, 0xeb0, + 0x18b4, 0x1ab4, 0x1abc, 0x9a4, 0x764, 0xcbc, 0x910 + }; + static const u32 backup_rf_reg[RF_REG_NUM_8814] = {0x0}; + u32 rf_backup[RF_REG_NUM_8814][RTW_RF_PATH_MAX]; + u32 mac_backup[MAC_REG_NUM_8814]; + u32 bb_backup[BB_REG_NUM_8814]; + + rtw8814a_iqk_backup_mac_bb(rtwdev, mac_backup, bb_backup, + backup_mac_reg, backup_bb_reg); + rtw8814a_iqk_afe_setting(rtwdev, true); + rtw8814a_iqk_backup_rf(rtwdev, rf_backup, backup_rf_reg); + rtw8814a_iqk_configure_mac(rtwdev); + rtw8814a_iqk(rtwdev); + rtw8814a_iqk_reset_nctl(rtwdev); /* for 3-wire to BB use */ + rtw8814a_iqk_afe_setting(rtwdev, false); + rtw8814a_iqk_restore_mac_bb(rtwdev, mac_backup, bb_backup, + backup_mac_reg, backup_bb_reg); + rtw8814a_iqk_restore_rf(rtwdev, rf_backup, backup_rf_reg); +} + +static void rtw8814a_phy_calibration(struct rtw_dev *rtwdev) +{ + rtw8814a_do_iqk(rtwdev); +} + +static void rtw8814a_coex_cfg_init(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type, + u8 pos_type) +{ + /* Override rtw_coex_coex_ctrl_owner(). RF path C does not + * function when BIT_LTE_MUX_CTRL_PATH is set. + */ + rtw_write8_clr(rtwdev, REG_SYS_SDIO_CTRL + 3, + BIT_LTE_MUX_CTRL_PATH >> 24); +} + +static void rtw8814a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_rfe_type(struct rtw_dev *rtwdev) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_coex_rfe *coex_rfe = &coex->rfe; + + /* Only needed to make rtw8814a_coex_cfg_ant_switch() run. */ + coex_rfe->ant_switch_exist = true; +} + +static void rtw8814a_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) +{ +} + +static void rtw8814a_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) +{ +} + +static void rtw8814a_txagc_swing_offset(struct rtw_dev *rtwdev, u8 path, + u8 tx_pwr_idx_offset, + s8 *txagc_idx, u8 *swing_idx) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 swing_upper_bound = dm_info->default_ofdm_index + 10; + s8 delta_pwr_idx = dm_info->delta_power_index[path]; + u8 swing_index = dm_info->default_ofdm_index; + u8 max_tx_pwr_idx_offset = 0xf; + u8 swing_lower_bound = 0; + s8 agc_index = 0; + + tx_pwr_idx_offset = min_t(u8, tx_pwr_idx_offset, max_tx_pwr_idx_offset); + + if (delta_pwr_idx >= 0) { + if (delta_pwr_idx <= tx_pwr_idx_offset) { + agc_index = delta_pwr_idx; + swing_index = dm_info->default_ofdm_index; + } else if (delta_pwr_idx > tx_pwr_idx_offset) { + agc_index = tx_pwr_idx_offset; + swing_index = dm_info->default_ofdm_index + + delta_pwr_idx - tx_pwr_idx_offset; + swing_index = min_t(u8, swing_index, swing_upper_bound); + } + } else { + if (dm_info->default_ofdm_index > abs(delta_pwr_idx)) + swing_index = + dm_info->default_ofdm_index + delta_pwr_idx; + else + swing_index = swing_lower_bound; + swing_index = max_t(u8, swing_index, swing_lower_bound); + + agc_index = 0; + } + + if (swing_index >= RTW_TXSCALE_SIZE) { + rtw_warn(rtwdev, "swing index overflow\n"); + swing_index = RTW_TXSCALE_SIZE - 1; + } + *txagc_idx = agc_index; + *swing_idx = swing_index; +} + +static void rtw8814a_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 path, + u8 pwr_idx_offset) +{ + static const u32 txagc_reg[RTW_RF_PATH_MAX] = { + REG_TX_AGC_A, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D + }; + static const u32 txscale_reg[RTW_RF_PATH_MAX] = { + REG_TXSCALE_A, REG_TXSCALE_B, REG_TXSCALE_C, REG_TXSCALE_D + }; + s8 txagc_idx; + u8 swing_idx; + + rtw8814a_txagc_swing_offset(rtwdev, path, pwr_idx_offset, + &txagc_idx, &swing_idx); + rtw_write32_mask(rtwdev, txagc_reg[path], GENMASK(29, 25), + txagc_idx); + rtw_write32_mask(rtwdev, txscale_reg[path], BB_SWING_MASK, + rtw8814a_txscale_tbl[swing_idx]); +} + +static void rtw8814a_pwrtrack_set(struct rtw_dev *rtwdev, u8 path) +{ + u8 max_pwr_idx = rtwdev->chip->max_power_index; + u8 band_width = rtwdev->hal.current_band_width; + u8 channel = rtwdev->hal.current_channel; + u8 tx_rate = rtwdev->dm_info.tx_rate; + u8 regd = rtw_regd_get(rtwdev); + u8 pwr_idx_offset, tx_pwr_idx; + + tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, tx_rate, + band_width, channel, regd); + + tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx); + + pwr_idx_offset = max_pwr_idx - tx_pwr_idx; + + rtw8814a_pwrtrack_set_pwr(rtwdev, path, pwr_idx_offset); +} + +static void rtw8814a_phy_pwrtrack_path(struct rtw_dev *rtwdev, + struct rtw_swing_table *swing_table, + u8 path) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 power_idx_cur, power_idx_last; + u8 delta; + + /* 8814A only has one thermal meter at PATH A */ + delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A); + + power_idx_last = dm_info->delta_power_index[path]; + power_idx_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, swing_table, + path, RF_PATH_A, delta); + + /* if delta of power indexes are the same, just skip */ + if (power_idx_cur == power_idx_last) + return; + + dm_info->delta_power_index[path] = power_idx_cur; + rtw8814a_pwrtrack_set(rtwdev, path); +} + +static void rtw8814a_phy_pwrtrack(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + struct rtw_swing_table swing_table; + u8 thermal_value, path; + + rtw_phy_config_swing_table(rtwdev, &swing_table); + + if (rtwdev->efuse.thermal_meter[RF_PATH_A] == 0xff) + return; + + thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00); + + rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A); + + if (dm_info->pwr_trk_init_trigger) + dm_info->pwr_trk_init_trigger = false; + else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value, + RF_PATH_A)) + goto iqk; + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) + rtw8814a_phy_pwrtrack_path(rtwdev, &swing_table, path); + +iqk: + if (rtw_phy_pwrtrack_need_iqk(rtwdev)) + rtw8814a_do_iqk(rtwdev); +} + +static void rtw8814a_pwr_track(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + + if (!dm_info->pwr_trk_triggered) { + rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, + GENMASK(17, 16), 0x03); + dm_info->pwr_trk_triggered = true; + return; + } + + rtw8814a_phy_pwrtrack(rtwdev); + dm_info->pwr_trk_triggered = false; +} + +static void rtw8814a_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) +{ + static const u8 pd[CCK_PD_LV_MAX] = {0x40, 0x83, 0xcd, 0xdd, 0xed}; + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + + /* Override rtw_phy_cck_pd_lv_link(). It implements something + * like type 2/3/4. We need type 1 here. + */ + if (rtw_is_assoc(rtwdev)) { + if (dm_info->min_rssi > 60) { + new_lvl = CCK_PD_LV3; + } else if (dm_info->min_rssi > 35) { + new_lvl = CCK_PD_LV2; + } else if (dm_info->min_rssi > 20) { + if (dm_info->cck_fa_avg > 500) + new_lvl = CCK_PD_LV2; + else if (dm_info->cck_fa_avg < 250) + new_lvl = CCK_PD_LV1; + else + return; + } else { + new_lvl = CCK_PD_LV1; + } + } + + rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n", + dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl); + + if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl) + return; + + dm_info->cck_fa_avg = CCK_FA_AVG_RESET; + dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl; + + rtw_write8(rtwdev, REG_CCK_PD_TH, pd[new_lvl]); +} + +static void rtw8814a_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 led_gpio_cfg; + + led_gpio_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); + led_gpio_cfg |= BIT(16) | BIT(17) | BIT(21) | BIT(22); + + if (brightness == LED_OFF) { + led_gpio_cfg |= BIT(8) | BIT(9) | BIT(13) | BIT(14); + } else { + led_gpio_cfg &= ~(BIT(8) | BIT(9) | BIT(13) | BIT(14)); + led_gpio_cfg &= ~(BIT(0) | BIT(1) | BIT(5) | BIT(6)); + } + + rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg); +} + +static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + + fill_txdesc_checksum_common(txdesc, words); +} + +static const struct rtw_chip_ops rtw8814a_ops = { + .power_on = rtw_power_on, + .power_off = rtw_power_off, + .phy_set_param = rtw8814a_phy_set_param, + .read_efuse = rtw8814a_read_efuse, + .query_phy_status = rtw8814a_query_phy_status, + .set_channel = rtw8814a_set_channel, + .mac_init = rtw8814a_mac_init, + .read_rf = rtw_phy_read_rf, + .write_rf = rtw_phy_write_rf_reg_sipi, + .set_tx_power_index = rtw8814a_set_tx_power_index, + .set_antenna = NULL, + .cfg_ldo25 = rtw8814a_cfg_ldo25, + .efuse_grant = rtw8814a_efuse_grant, + .false_alarm_statistics = rtw8814a_false_alarm_statistics, + .phy_calibration = rtw8814a_phy_calibration, + .cck_pd_set = rtw8814a_phy_cck_pd_set, + .pwr_track = rtw8814a_pwr_track, + .config_bfee = NULL, + .set_gid_table = NULL, + .cfg_csi_rate = NULL, + .led_set = rtw8814a_led_set, + .fill_txdesc_checksum = rtw8814a_fill_txdesc_checksum, + + .coex_set_init = rtw8814a_coex_cfg_init, + .coex_set_ant_switch = rtw8814a_coex_cfg_ant_switch, + .coex_set_gnt_fix = rtw8814a_coex_cfg_gnt_fix, + .coex_set_gnt_debug = rtw8814a_coex_cfg_gnt_debug, + .coex_set_rfe_type = rtw8814a_coex_cfg_rfe_type, + .coex_set_wl_tx_power = rtw8814a_coex_cfg_wl_tx_power, + .coex_set_wl_rx_gain = rtw8814a_coex_cfg_wl_rx_gain, +}; + +static const struct rtw_rqpn rqpn_table_8814a[] = { + /* SDIO */ + {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, /* vo vi */ + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, /* be bk */ + RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, /* mg hi */ + /* PCIE */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 2 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, + RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 3 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 4 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, +}; + +static const struct rtw_prioq_addrs prioq_addrs_8814a = { + .prio[RTW_DMA_MAPPING_EXTRA] = { + .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2, + }, + .prio[RTW_DMA_MAPPING_LOW] = { + .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2, + }, + .prio[RTW_DMA_MAPPING_NORMAL] = { + .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2, + }, + .prio[RTW_DMA_MAPPING_HIGH] = { + .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2, + }, + .wsize = true, +}; + +static const struct rtw_page_table page_table_8814a[] = { + /* SDIO */ + {0, 0, 0, 0, 0}, /* hq nq lq exq gapq */ + /* PCIE */ + {32, 32, 32, 32, 0}, + /* USB, 2 bulk out */ + {32, 32, 32, 32, 0}, + /* USB, 3 bulk out */ + {32, 32, 32, 32, 0}, + /* USB, 4 bulk out */ + {32, 32, 32, 32, 0}, +}; + +static const struct rtw_intf_phy_para_table phy_para_table_8814a = {}; + +static const struct rtw_hw_reg rtw8814a_dig[] = { + [0] = { .addr = 0xc50, .mask = 0x7f }, + [1] = { .addr = 0xe50, .mask = 0x7f }, + [2] = { .addr = 0x1850, .mask = 0x7f }, + [3] = { .addr = 0x1a50, .mask = 0x7f }, +}; + +static const struct rtw_rfe_def rtw8814a_rfe_defs[] = { + [0] = { .phy_pg_tbl = &rtw8814a_bb_pg_type0_tbl, + .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type0_tbl, + .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_type0_tbl }, + [1] = { .phy_pg_tbl = &rtw8814a_bb_pg_tbl, + .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type1_tbl, + .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_tbl }, +}; + +/* rssi in percentage % (dbm = % - 100) */ +static const u8 wl_rssi_step_8814a[] = {60, 50, 44, 30}; +static const u8 bt_rssi_step_8814a[] = {30, 30, 30, 30}; + +/* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ +static const struct coex_rf_para rf_para_tx_8814a[] = { + {0, 0, false, 7}, /* for normal */ + {0, 16, false, 7}, /* for WL-CPT */ + {4, 0, true, 1}, + {3, 6, true, 1}, + {2, 9, true, 1}, + {1, 13, true, 1} +}; + +static const struct coex_rf_para rf_para_rx_8814a[] = { + {0, 0, false, 7}, /* for normal */ + {0, 16, false, 7}, /* for WL-CPT */ + {4, 0, true, 1}, + {3, 6, true, 1}, + {2, 9, true, 1}, + {1, 13, true, 1} +}; + +static_assert(ARRAY_SIZE(rf_para_tx_8814a) == ARRAY_SIZE(rf_para_rx_8814a)); + +const struct rtw_chip_info rtw8814a_hw_spec = { + .ops = &rtw8814a_ops, + .id = RTW_CHIP_TYPE_8814A, + .fw_name = "rtw88/rtw8814a_fw.bin", + .wlan_cpu = RTW_WCPU_11AC, + .tx_pkt_desc_sz = 40, + .tx_buf_desc_sz = 16, + .rx_pkt_desc_sz = 24, + .rx_buf_desc_sz = 8, + .phy_efuse_size = 1024, + .log_efuse_size = 512, + .ptct_efuse_size = 0, + .txff_size = (2048 - 10) * TX_PAGE_SIZE, + .rxff_size = 23552, + .rsvd_drv_pg_num = 8, + .band = RTW_BAND_2G | RTW_BAND_5G, + .page_size = TX_PAGE_SIZE, + .csi_buf_pg_num = 0, + .dig_min = 0x1c, + .txgi_factor = 1, + .is_pwr_by_rate_dec = true, + .rx_ldpc = true, + .max_power_index = 0x3f, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2, + .usb_tx_agg_desc_num = 3, + .hw_feature_report = false, + .c2h_ra_report_size = 6, + .old_datarate_fb_limit = false, + .ht_supported = true, + .vht_supported = true, + .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK), + .sys_func_en = 0xDC, + .pwr_on_seq = card_enable_flow_8814a, + .pwr_off_seq = card_disable_flow_8814a, + .rqpn_table = rqpn_table_8814a, + .prioq_addrs = &prioq_addrs_8814a, + .page_table = page_table_8814a, + .intf_table = &phy_para_table_8814a, + .dig = rtw8814a_dig, + .dig_cck = NULL, + .rf_base_addr = {0x2800, 0x2c00, 0x3800, 0x3c00}, + .rf_sipi_addr = {0xc90, 0xe90, 0x1890, 0x1a90}, + .ltecoex_addr = NULL, + .mac_tbl = &rtw8814a_mac_tbl, + .agc_tbl = &rtw8814a_agc_tbl, + .bb_tbl = &rtw8814a_bb_tbl, + .rf_tbl = {&rtw8814a_rf_a_tbl, &rtw8814a_rf_b_tbl, + &rtw8814a_rf_c_tbl, &rtw8814a_rf_d_tbl}, + .rfe_defs = rtw8814a_rfe_defs, + .rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs), + .iqk_threshold = 8, + .max_scan_ie_len = IEEE80211_MAX_DATA_LEN, + + .coex_para_ver = 0, + .bt_desired_ver = 0, + .scbd_support = false, + .new_scbd10_def = false, + .ble_hid_profile_support = false, + .wl_mimo_ps_support = false, + .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, + .bt_rssi_type = COEX_BTRSSI_RATIO, + .ant_isolation = 15, + .rssi_tolerance = 2, + .wl_rssi_step = wl_rssi_step_8814a, + .bt_rssi_step = bt_rssi_step_8814a, + .table_sant_num = 0, + .table_sant = NULL, + .table_nsant_num = 0, + .table_nsant = NULL, + .tdma_sant_num = 0, + .tdma_sant = NULL, + .tdma_nsant_num = 0, + .tdma_nsant = NULL, + .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8814a), + .wl_rf_para_tx = rf_para_tx_8814a, + .wl_rf_para_rx = rf_para_rx_8814a, + .bt_afh_span_bw20 = 0x24, + .bt_afh_span_bw40 = 0x36, + .afh_5g_num = 0, + .afh_5g = NULL, + .coex_info_hw_regs_num = 0, + .coex_info_hw_regs = NULL, +}; +EXPORT_SYMBOL(rtw8814a_hw_spec); + +MODULE_FIRMWARE("rtw88/rtw8814a_fw.bin"); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814a driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.h b/drivers/net/wireless/realtek/rtw88/rtw8814a.h new file mode 100644 index 000000000000..c57c7c8f915e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2025 Realtek Corporation + */ + +#ifndef __RTW8814A_H__ +#define __RTW8814A_H__ + +struct rtw8814au_efuse { + u8 vid[2]; /* 0xd0 */ + u8 pid[2]; /* 0xd2 */ + u8 res[4]; /* 0xd4 */ + u8 mac_addr[ETH_ALEN]; /* 0xd8 */ +} __packed; + +struct rtw8814ae_efuse { + u8 mac_addr[ETH_ALEN]; /* 0xd0 */ + u8 vid[2]; /* 0xd6 */ + u8 did[2]; /* 0xd8 */ + u8 svid[2]; /* 0xda */ + u8 smid[2]; /* 0xdc */ +} __packed; + +struct rtw8814a_efuse { + __le16 rtl_id; + u8 res0[0x0c]; + u8 usb_mode; /* 0x0e */ + u8 res1; + + /* power index for four RF paths */ + struct rtw_txpwr_idx txpwr_idx_table[4]; + + u8 channel_plan; /* 0xb8 */ + u8 xtal_k; /* 0xb9 */ + u8 thermal_meter; /* 0xba */ + u8 iqk_lck; /* 0xbb */ + u8 pa_type; /* 0xbc */ + u8 lna_type_2g[2]; /* 0xbd */ + u8 lna_type_5g[2]; /* 0xbf */ + u8 rf_board_option; /* 0xc1 */ + u8 res2; + u8 rf_bt_setting; /* 0xc3 */ + u8 eeprom_version; /* 0xc4 */ + u8 eeprom_customer_id; /* 0xc5 */ + u8 tx_bb_swing_setting_2g; /* 0xc6 */ + u8 tx_bb_swing_setting_5g; /* 0xc7 */ + u8 res3; + u8 trx_antenna_option; /* 0xc9 */ + u8 rfe_option; /* 0xca */ + u8 country_code[2]; /* 0xcb */ + u8 res4[3]; + union { + struct rtw8814au_efuse u; + struct rtw8814ae_efuse e; + }; + u8 res5[0x122]; /* 0xde */ +} __packed; + +static_assert(sizeof(struct rtw8814a_efuse) == 512); + +extern const struct rtw_chip_info rtw8814a_hw_spec; + +#endif From 42d86f4d0ca2ad10fb0ae2471906d03c0e06b513 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 331/465] wifi: rtw88: Add rtw8814ae.c JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dad8e879310211c1e02f09c35f169388bccbfa42 Author: Bitterblue Smith Date: Fri Mar 7 02:25:09 2025 +0200 wifi: rtw88: Add rtw8814ae.c This is the entry point for the new module rtw88_8814ae. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/74ebab2f-a23e-4d87-935f-0af2b5cba672@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/realtek/rtw88/rtw8814ae.c | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814ae.c diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814ae.c b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c new file mode 100644 index 000000000000..54d2e20a7764 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include +#include +#include "pci.h" +#include "rtw8814a.h" + +static const struct pci_device_id rtw_8814ae_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8813), + .driver_data = (kernel_ulong_t)&rtw8814a_hw_spec + }, + {} +}; +MODULE_DEVICE_TABLE(pci, rtw_8814ae_id_table); + +static struct pci_driver rtw_8814ae_driver = { + .name = "rtw_8814ae", + .id_table = rtw_8814ae_id_table, + .probe = rtw_pci_probe, + .remove = rtw_pci_remove, + .driver.pm = &rtw_pm_ops, + .shutdown = rtw_pci_shutdown, +}; +module_pci_driver(rtw_8814ae_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814ae driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 0fee0b24c3c048ae63b3f151c7f07706e0a02814 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 332/465] wifi: rtw88: Add rtw8814au.c JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bad060e8a425182809bfc2586a2e7f5ccd1a994d Author: Bitterblue Smith Date: Fri Mar 7 02:25:37 2025 +0200 wifi: rtw88: Add rtw8814au.c This is the entry point for the new module rtw88_8814au. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/71457787-5a9e-4ead-a62c-22ca44e00b89@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/realtek/rtw88/rtw8814au.c | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8814au.c diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814au.c b/drivers/net/wireless/realtek/rtw88/rtw8814au.c new file mode 100644 index 000000000000..afe045fb84de --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814au.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8814a.h" +#include "usb.h" + +static const struct usb_device_id rtw_8814au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8813, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9054, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1817, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1852, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1853, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0026, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0106, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa834, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa833, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8814au_id_table); + +static struct usb_driver rtw_8814au_driver = { + .name = "rtw_8814au", + .id_table = rtw_8814au_id_table, + .probe = rtw_usb_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8814au_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814au driver"); +MODULE_LICENSE("Dual BSD/GPL"); From b2d2c050abdda77ae18018c36e528fbde1c6a3f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 333/465] wifi: rtw88: Enable the new RTL8814AE/RTL8814AU drivers JIRA: https://issues.redhat.com/browse/RHEL-89168 commit deb3ddeb18652118956fb581a39ac299e1ee5623 Author: Bitterblue Smith Date: Fri Mar 7 02:26:25 2025 +0200 wifi: rtw88: Enable the new RTL8814AE/RTL8814AU drivers RTL8814A is a wifi 5 chip with 4 RF paths (chains), 3 spatial streams, and probably no Bluetooth. The USB-based RTL8814AU can reach 800 Mbps in the 5 GHz band in USB 3 mode. In USB 2 mode it only uses 2 spatial streams. The PCI-based RTL8814AE is not as popular and didn't get as much testing so it's unclear how fast it goes. It's more like a bonus on top of the RTL8814AU support. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5795b0a7-511e-40b5-ac36-476b63f174c7@gmail.com Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/Kconfig | 25 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw88/Makefile | 9 ++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index ab21b8059e0b..3736f290bd42 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -54,6 +54,9 @@ config RTW88_8812A tristate select RTW88_88XXA +config RTW88_8814A + tristate + config RTW88_8822BE tristate "Realtek 8822BE PCI wireless network adapter" depends on PCI @@ -222,6 +225,28 @@ config RTW88_8812AU 802.11ac USB wireless network adapter +config RTW88_8814AE + tristate "Realtek 8814AE PCI wireless network adapter" + depends on PCI + select RTW88_CORE + select RTW88_PCI + select RTW88_8814A + help + Select this option will enable support for 8814AE chipset + + 802.11ac PCIe wireless network adapter + +config RTW88_8814AU + tristate "Realtek 8814AU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8814A + help + Select this option will enable support for 8814AU chipset + + 802.11ac USB wireless network adapter + config RTW88_DEBUG bool "Realtek rtw88 debug support" depends on RTW88_CORE diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 0cbbb366e393..0b3da05a2938 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -94,6 +94,15 @@ rtw88_8821au-objs := rtw8821au.o obj-$(CONFIG_RTW88_8812AU) += rtw88_8812au.o rtw88_8812au-objs := rtw8812au.o +obj-$(CONFIG_RTW88_8814A) += rtw88_8814a.o +rtw88_8814a-objs := rtw8814a.o rtw8814a_table.o + +obj-$(CONFIG_RTW88_8814AE) += rtw88_8814ae.o +rtw88_8814ae-objs := rtw8814ae.o + +obj-$(CONFIG_RTW88_8814AU) += rtw88_8814au.o +rtw88_8814au-objs := rtw8814au.o + obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o rtw88_pci-objs := pci.o From 9bcd9dd9095dd35d7eb6522bcf77c554ed7eb681 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 334/465] wifi: rtw88: Add __nonstring annotations for unterminated strings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d58ad77d5cc2a6d04db622a898e54d93fc7002a2 Author: Kees Cook Date: Mon Mar 10 15:22:58 2025 -0700 wifi: rtw88: Add __nonstring annotations for unterminated strings When a character array without a terminating NUL character has a static initializer, GCC 15's -Wunterminated-string-initialization will only warn if the array lacks the "nonstring" attribute[1]. Mark the arrays with __nonstring to and correctly identify the char array as "not a C string" and thereby eliminate the warning. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1] Cc: Ping-Ke Shih Cc: Johannes Berg Cc: linux-wireless@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250310222257.work.866-kees@kernel.org Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/realtek/rtw88/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index c15e0f55d09a..02343e059fd9 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -835,7 +835,7 @@ struct rtw_vif { }; struct rtw_regulatory { - char alpha2[2]; + char alpha2[2] __nonstring; u8 txpwr_regd_2g; u8 txpwr_regd_5g; }; From c4ebc986e3e21d662b272a931efe0a72d36e4882 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 335/465] wifi: nl80211: store chandef on the correct link when starting CAC JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ea841520c50f5f7c72c8070e3b79e1927b94fabf Author: Aditya Kumar Singh Date: Fri Mar 14 09:38:53 2025 +0530 wifi: nl80211: store chandef on the correct link when starting CAC Link ID to store chandef is still being used as 0 even in case of MLO which is incorrect. This leads to issue during CAC completion where link 0 as well gets stopped. Fixes: 0b7798232eee ("wifi: cfg80211/mac80211: use proper link ID for DFS") Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250314-fix_starting_cac_during_mlo-v1-1-3b51617d7ea5@oss.qualcomm.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2c546ef2b7c8..97285d2fb914 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10189,7 +10189,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - wdev->links[0].ap.chandef = chandef; + wdev->links[link_id].ap.chandef = chandef; break; case NL80211_IFTYPE_ADHOC: wdev->u.ibss.chandef = chandef; From c6fdbe2da87eae8f49731d0482883611453e132a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 336/465] wifi: iwlwifi: mld: Rename WIPHY_DEBUGFS_HANDLER_WRAPPER to WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d4c6ca39d6661c7877c3012db7ac1e4714732a71 Author: Pagadala Yesu Anjaneyulu Date: Thu Mar 13 00:22:24 2025 +0200 wifi: iwlwifi: mld: Rename WIPHY_DEBUGFS_HANDLER_WRAPPER to WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER Renamed the macro WIPHY_DEBUGFS_HANDLER_WRAPPER to WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER to better reflect its purpose as a write handler. Additionally, updated the corresponding macro WIPHY_DEBUGFS_HANDLER_WRAPPER_MLD to WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD for consistency. This change does not alter the functionality but enhances the maintainability of the code. Signed-off-by: Pagadala Yesu Anjaneyulu Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.bb8a1d7907c8.I53325f2f37ccaad2b212d35d10616e06c1555e48@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h index 04c536db8b16..13d97184a31f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include "iface.h" #include "sta.h" @@ -67,7 +67,7 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .release = _iwl_dbgfs_release, \ } -#define WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ struct file *file, char *buf, \ size_t count, void *data) \ @@ -97,7 +97,7 @@ static inline struct iwl_mld *iwl_mld_from_vif(struct ieee80211_vif *vif) } #define WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \ -WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos) \ @@ -121,7 +121,7 @@ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ .llseek = generic_file_llseek, \ } -#define WIPHY_DEBUGFS_HANDLER_WRAPPER_MLD(name) \ +#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \ static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ struct file *file, char *buf, \ size_t count, void *data) \ @@ -132,7 +132,7 @@ static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ } #define WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ -WIPHY_DEBUGFS_HANDLER_WRAPPER_MLD(name) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos) \ @@ -170,7 +170,7 @@ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ } #define WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \ -WIPHY_DEBUGFS_HANDLER_WRAPPER(name) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos) \ From b01300ea43c3ae4a0220e03ac6ffe5d89ffb4381 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 337/465] wifi: iwlwifi: mld: Add support for WIPHY_DEBUGFS_READ_FILE_OPS_MLD macro JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4b8130a722eb3634a8cde1e8cb1b113e488df5ac Author: Pagadala Yesu Anjaneyulu Date: Thu Mar 13 00:22:25 2025 +0200 wifi: iwlwifi: mld: Add support for WIPHY_DEBUGFS_READ_FILE_OPS_MLD macro Introduced the WIPHY_DEBUGFS_READ_FILE_OPS_MLD macro to enable reading data from the driver while holding the wiphy lock. This will enable read operations with wiphy locked. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.b0ddb6b0a144.I1fab63f2c6f52fea61cc5d7b27775aed58adfd8d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/debugfs.h | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h index 13d97184a31f..900aaed90775 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h @@ -121,6 +121,16 @@ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ .llseek = generic_file_llseek, \ } +#define WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \ +static ssize_t iwl_dbgfs_##name##_read_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_read(mld, buf, count); \ +} + #define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \ static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ struct file *file, char *buf, \ @@ -147,6 +157,40 @@ static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ NULL); \ } +#define WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ +WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \ +static ssize_t __iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct iwl_mld *mld = data->arg; \ + \ + if (!data->read_done) { \ + data->read_done = true; \ + data->rlen = wiphy_locked_debugfs_read(mld->wiphy, \ + file, data->buf, sizeof(data->buf), \ + user_buf, count, ppos, \ + iwl_dbgfs_##name##_read_handler, NULL); \ + return data->rlen; \ + } \ + \ + if (data->rlen < 0) \ + return data->rlen; \ + return simple_read_from_buffer(user_buf, count, ppos, \ + data->buf, data->rlen); \ +} + +#define WIPHY_DEBUGFS_READ_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = __iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + #define WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(name, bufsz) \ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ From 6d679f2ebefc32dbecb30128fe77ebfa36ca2519 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 338/465] wifi: iwlwifi: mld: Ensure wiphy lock is held during debugfs read operations JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8301e2636c106f28f0deca5d73fe72015db6acb2 Author: Pagadala Yesu Anjaneyulu Date: Thu Mar 13 00:22:26 2025 +0200 wifi: iwlwifi: mld: Ensure wiphy lock is held during debugfs read operations The WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD macro is intended to call read/write handlers with the wiphy lock held. However, the current implementation uses the MLD_DEBUGFS_READ_WRAPPER macro, which does not hold the wiphy lock during read operations. This fix updates the WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD macro to use the WIPHY_DEBUGFS_READ_WRAPPER_MLD macro instead, ensuring that the wiphy lock is held during both read and write operations. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.2001d2335e9d.I607a8bd12efc6d1190cef1fca44279dbdd2756ea@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 3 +-- drivers/net/wireless/intel/iwlwifi/mld/debugfs.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 1d4b2ad5d388..c67dbbf575d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -178,8 +178,7 @@ iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf, } static ssize_t -iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, size_t count, - char *buf) +iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count) { return scnprintf(buf, count, "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h index 900aaed90775..eeba35342ba1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h @@ -204,10 +204,10 @@ static ssize_t __iwl_dbgfs_##name##_read(struct file *file, \ #define WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(name, bufsz) \ MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ - MLD_DEBUGFS_READ_WRAPPER(name) \ + WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = __iwl_dbgfs_##name##_write, \ - .read = _iwl_dbgfs_##name##_read, \ + .read = __iwl_dbgfs_##name##_read, \ .open = _iwl_dbgfs_##name##_open, \ .llseek = generic_file_llseek, \ .release = _iwl_dbgfs_release, \ From 8aaa78a0d1fde25f0b6a65cdcf1b66d778537243 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:58 +0200 Subject: [PATCH 339/465] wifi: iwlwifi: mld: add support for DHC_TOOLS_UMAC_GET_TAS_STATUS command JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b611cf6b57a8091a2ebbe415f421865407968ef2 Author: Pagadala Yesu Anjaneyulu Date: Thu Mar 13 00:22:27 2025 +0200 wifi: iwlwifi: mld: add support for DHC_TOOLS_UMAC_GET_TAS_STATUS command Add debugfs file in mld to retrieve TAS status per radio, TAS block list, current mcc, OEM name and OEM allowed list. This will add ability to get TAS status to user application via debugfs and required for debugging. Add the required API definitions and some debug host command utils. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.66524c6ea198.I1625135284fc075148a55dd9ac629e94ca881fe4@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/fw/api/dhc.h | 97 ++++++++ .../net/wireless/intel/iwlwifi/fw/dhc-utils.h | 75 ++++++ .../net/wireless/intel/iwlwifi/mld/debugfs.c | 232 ++++++++++++++++++ 3 files changed, 404 insertions(+) create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h index dbe06f3fc662..b6d79c678cd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h @@ -15,6 +15,19 @@ enum iwl_dhc_table_id { * @DHC_TABLE_INTEGRATION: select the integration table */ DHC_TABLE_INTEGRATION = 2 << DHC_TABLE_MASK_POS, + /** + * @DHC_TABLE_TOOLS: select the tools table + */ + DHC_TABLE_TOOLS = 0, +}; + +/** + * enum iwl_dhc_umac_tools_table - tools operations + * @DHC_TOOLS_UMAC_GET_TAS_STATUS: Get TAS status. + * See @struct iwl_dhc_tas_status_resp + */ +enum iwl_dhc_umac_tools_table { + DHC_TOOLS_UMAC_GET_TAS_STATUS = 0, }; /** @@ -58,6 +71,90 @@ struct iwl_dhc_cmd { __le32 data[]; } __packed; /* DHC_CMD_API_S */ +/** + * struct iwl_dhc_payload_hdr - DHC payload header + * @version: a version of a payload + * @reserved: reserved for alignment + */ +struct iwl_dhc_payload_hdr { + u8 version; + u8 reserved[3]; +} __packed; /* DHC_PAYLOAD_HDR_API_S_VER_1 */ + +/** + * struct iwl_dhc_tas_status_per_radio - TAS status per radio + * @band: &PHY_BAND_5 for high band, PHY_BAND_24 for low band and + * &PHY_BAND_6 for ultra high band. + * @static_status: TAS statically enabled or disabled + * @static_disable_reason: TAS static disable reason, uses + * &enum iwl_tas_statically_disabled_reason + * @near_disconnection: is TAS currently near disconnection per radio + * @dynamic_status_ant_a: Antenna A current TAS status. + * uses &enum iwl_tas_dyna_status + * @dynamic_status_ant_b: Antenna B current TAS status. + * uses &enum iwl_tas_dyna_status + * @max_reg_pwr_limit_ant_a: Antenna A regulatory power limits in dBm + * @max_reg_pwr_limit_ant_b: Antenna B regulatory power limits in dBm + * @sar_limit_ant_a: Antenna A SAR limit per radio in dBm + * @sar_limit_ant_b: Antenna B SAR limit per radio in dBm + * @reserved: reserved for alignment + */ +struct iwl_dhc_tas_status_per_radio { + u8 band; + u8 static_status; + u8 static_disable_reason; + u8 near_disconnection; + u8 dynamic_status_ant_a; + u8 dynamic_status_ant_b; + __le16 max_reg_pwr_limit_ant_a; + __le16 max_reg_pwr_limit_ant_b; + __le16 sar_limit_ant_a; + __le16 sar_limit_ant_b; + u8 reserved[2]; +} __packed; /* DHC_TAS_STATUS_PER_RADIO_S_VER_1 */ + +/** + * struct iwl_dhc_tas_status_resp - Response to DHC_TOOLS_UMAC_GET_TAS_STATUS + * @header: DHC payload header, uses &struct iwl_dhc_payload_hdr + * @tas_config_info: see @struct bios_value_u32 + * @mcc_block_list: block listed country codes + * @tas_status_radio: TAS status, uses &struct iwl_dhc_tas_status_per_radio + * @curr_mcc: current mcc + * @valid_radio_mask: represent entry in tas_status_per_radio is valid. + * @reserved: reserved for alignment + */ +struct iwl_dhc_tas_status_resp { + struct iwl_dhc_payload_hdr header; + struct bios_value_u32 tas_config_info; + __le16 mcc_block_list[IWL_WTAS_BLACK_LIST_MAX]; + struct iwl_dhc_tas_status_per_radio tas_status_radio[2]; + __le16 curr_mcc; + u8 valid_radio_mask; + u8 reserved; +} __packed; /* DHC_TAS_STATUS_RSP_API_S_VER_1 */ + +/** + * struct iwl_dhc_cmd_resp_v1 - debug host command response + * @status: status of the command + * @data: the response data + */ +struct iwl_dhc_cmd_resp_v1 { + __le32 status; + __le32 data[]; +} __packed; /* DHC_RESP_API_S_VER_1 */ + +/** + * struct iwl_dhc_cmd_resp - debug host command response + * @status: status of the command + * @descriptor: command descriptor (index_and_mask) returned + * @data: the response data + */ +struct iwl_dhc_cmd_resp { + __le32 status; + __le32 descriptor; + __le32 data[]; +} __packed; /* DHC_RESP_API_S_VER_2 and DHC_RESP_API_S_VER_3 */ + /** * enum iwl_dhc_twt_operation_type - describes the TWT operation type * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h new file mode 100644 index 000000000000..983acee5cd7d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2021, 2025 Intel Corporation + */ +#ifndef __iwl_fw_dhc_utils_h__ +#define __iwl_fw_dhc_utils_h__ + +#include +#include "fw/img.h" +#include "api/commands.h" +#include "api/dhc.h" + +/** + * iwl_dhc_resp_status - return status of DHC response + * @fw: firwmware image information + * @pkt: response packet, must not be %NULL + * + * Returns: the status value of the DHC command or (u32)-1 if the + * response was too short. + */ +static inline u32 iwl_dhc_resp_status(const struct iwl_fw *fw, + struct iwl_rx_packet *pkt) +{ + if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND, 1) >= 2) { + struct iwl_dhc_cmd_resp *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return (u32)-1; + + return le32_to_cpu(resp->status); + } else { + struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return (u32)-1; + + return le32_to_cpu(resp->status); + } +} + +/** + * iwl_dhc_resp_data - return data pointer of DHC response + * @fw: firwmware image information + * @pkt: response packet, must not be %NULL + * @len: where to store the length + * + * Returns: The data pointer, or an ERR_PTR() if the data was + * not valid (too short). + */ +static inline void *iwl_dhc_resp_data(const struct iwl_fw *fw, + struct iwl_rx_packet *pkt, + unsigned int *len) +{ + if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND, 1) >= 2) { + struct iwl_dhc_cmd_resp *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return ERR_PTR(-EINVAL); + + *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp); + return (void *)&resp->data; + } else { + struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return ERR_PTR(-EINVAL); + + *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp); + return (void *)&resp->data; + } +} + +#endif /* __iwl_fw_dhc_utils_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index c67dbbf575d7..14330daa6d13 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -21,6 +21,8 @@ #include "fw/api/rs.h" #include "fw/api/dhc.h" #include "fw/api/rfi.h" +#include "fw/dhc-utils.h" +#include #define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \ _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld) @@ -206,11 +208,240 @@ iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count) #define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\ IWL_RFI_DESENSE_BUF_SIZE + 32) +static size_t iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp *resp, + size_t count, u8 *buf) +{ + const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { + [TAS_DISABLED_DUE_TO_BIOS] = + "Due To BIOS", + [TAS_DISABLED_DUE_TO_SAR_6DBM] = + "Due To SAR Limit Less Than 6 dBm", + [TAS_DISABLED_REASON_INVALID] = + "N/A", + [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] = + "Due to table source invalid" + }; + const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { + [TAS_DYNA_INACTIVE] = "INACTIVE", + [TAS_DYNA_INACTIVE_MVM_MODE] = + "inactive due to mvm mode", + [TAS_DYNA_INACTIVE_TRIGGER_MODE] = + "inactive due to trigger mode", + [TAS_DYNA_INACTIVE_BLOCK_LISTED] = + "inactive due to block listed", + [TAS_DYNA_INACTIVE_UHB_NON_US] = + "inactive due to uhb non US", + [TAS_DYNA_ACTIVE] = "ACTIVE", + }; + ssize_t pos = 0; + + if (resp->header.version != 1) { + pos += scnprintf(buf + pos, count - pos, + "Unsupported TAS response version:%d", + resp->header.version); + return pos; + } + + pos += scnprintf(buf + pos, count - pos, "TAS Report\n"); + switch (resp->tas_config_info.table_source) { + case BIOS_SOURCE_NONE: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE NONE "); + break; + case BIOS_SOURCE_ACPI: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE ACPI "); + break; + case BIOS_SOURCE_UEFI: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE UEFI "); + break; + default: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE UNKNOWN (%d) ", + resp->tas_config_info.table_source); + break; + } + + pos += scnprintf(buf + pos, count - pos, + "revision is: %d data is: 0x%08x\n", + resp->tas_config_info.table_revision, + resp->tas_config_info.value); + pos += scnprintf(buf + pos, count - pos, "Current MCC: 0x%x\n", + le16_to_cpu(resp->curr_mcc)); + + pos += scnprintf(buf + pos, count - pos, "Block list entries:"); + for (int i = 0; i < ARRAY_SIZE(resp->mcc_block_list); i++) + pos += scnprintf(buf + pos, count - pos, " 0x%x", + le16_to_cpu(resp->mcc_block_list[i])); + + pos += scnprintf(buf + pos, count - pos, + "\nDo TAS Support Dual Radio?: %s\n", + hweight8(resp->valid_radio_mask) > 1 ? + "TRUE" : "FALSE"); + + for (int i = 0; i < ARRAY_SIZE(resp->tas_status_radio); i++) { + int tmp; + unsigned long dynamic_status; + + if (!(resp->valid_radio_mask & BIT(i))) + continue; + + pos += scnprintf(buf + pos, count - pos, + "TAS report for radio:%d\n", i + 1); + pos += scnprintf(buf + pos, count - pos, + "Static status: %sabled\n", + resp->tas_status_radio[i].static_status ? + "En" : "Dis"); + if (!resp->tas_status_radio[i].static_status) { + u8 static_disable_reason = + resp->tas_status_radio[i].static_disable_reason; + + pos += scnprintf(buf + pos, count - pos, + "\tStatic Disabled Reason: "); + if (static_disable_reason >= TAS_DISABLED_REASON_MAX) { + pos += scnprintf(buf + pos, count - pos, + "unsupported value (%d)\n", + static_disable_reason); + continue; + } + + pos += scnprintf(buf + pos, count - pos, + "%s (%d)\n", + tas_dis_reason[static_disable_reason], + static_disable_reason); + continue; + } + + pos += scnprintf(buf + pos, count - pos, "\tANT A %s and ", + (resp->tas_status_radio[i].dynamic_status_ant_a + & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); + + pos += scnprintf(buf + pos, count - pos, "ANT B %s for ", + (resp->tas_status_radio[i].dynamic_status_ant_b + & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); + + switch (resp->tas_status_radio[i].band) { + case PHY_BAND_5: + pos += scnprintf(buf + pos, count - pos, "HB\n"); + break; + case PHY_BAND_24: + pos += scnprintf(buf + pos, count - pos, "LB\n"); + break; + case PHY_BAND_6: + pos += scnprintf(buf + pos, count - pos, "UHB\n"); + break; + default: + pos += scnprintf(buf + pos, count - pos, + "Unsupported band (%d)\n", + resp->tas_status_radio[i].band); + break; + } + + pos += scnprintf(buf + pos, count - pos, + "Is near disconnection?: %s\n", + resp->tas_status_radio[i].near_disconnection ? + "True" : "False"); + + pos += scnprintf(buf + pos, count - pos, + "Dynamic status antenna A:\n"); + dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_a; + for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); + } + pos += scnprintf(buf + pos, count - pos, + "\nDynamic status antenna B:\n"); + dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_b; + for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); + } + + tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_a); + pos += scnprintf(buf + pos, count - pos, + "Max antenna A regulatory pwr limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_b); + pos += scnprintf(buf + pos, count - pos, + "Max antenna B regulatory pwr limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + + tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_a); + pos += scnprintf(buf + pos, count - pos, + "Antenna A SAR limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_b); + pos += scnprintf(buf + pos, count - pos, + "Antenna B SAR limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + } + + return pos; +} + +static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf, + size_t count) +{ + struct iwl_dhc_cmd cmd = { + .index_and_mask = cpu_to_le32(DHC_TABLE_TOOLS | + DHC_TARGET_UMAC | + DHC_TOOLS_UMAC_GET_TAS_STATUS), + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LEGACY_GROUP, DEBUG_HOST_COMMAND), + .flags = CMD_WANT_SKB, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; + struct iwl_dhc_tas_status_resp *resp = NULL; + ssize_t pos = 0; + u32 resp_len; + u32 status; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + return ret; + + pos += scnprintf(buf + pos, count - pos, "\nOEM name: %s\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: ""); + pos += scnprintf(buf + pos, count - pos, + "\tVendor In Approved List: %s\n", + iwl_is_tas_approved() ? "YES" : "NO"); + + status = iwl_dhc_resp_status(mld->fwrt.fw, hcmd.resp_pkt); + if (status != 1) { + pos += scnprintf(buf + pos, count - pos, + "response status is not success: %d\n", + status); + goto out; + } + + resp = iwl_dhc_resp_data(mld->fwrt.fw, hcmd.resp_pkt, &resp_len); + if (IS_ERR(resp) || resp_len != sizeof(*resp)) { + pos += scnprintf(buf + pos, count - pos, + "Invalid size for TAS response (%u instead of %zd)\n", + resp_len, sizeof(*resp)); + goto out; + } + + pos += iwl_mld_dump_tas_resp(resp, count - pos, buf + pos); + +out: + iwl_free_resp(&hcmd); + return pos; +} + WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10); WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10); WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32); WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10); WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8); +WIPHY_DEBUGFS_READ_FILE_OPS_MLD(tas_get_status, 2048); static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld, size_t count, u8 *buf) @@ -307,6 +538,7 @@ iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600); MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200); MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(tas_get_status, debugfs_dir, 0400); #ifdef CONFIG_THERMAL MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200); MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200); From 9b89c3c955077c139529e354ba46947f63544d6f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 340/465] wifi: iwlwifi: mld: remove IWL_MLD_EMLSR_BLOCKED_FW JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4d7236f968f27af942347fe1c8363764cdee63f5 Author: Miri Korenblit Date: Thu Mar 13 00:22:28 2025 +0200 wifi: iwlwifi: mld: remove IWL_MLD_EMLSR_BLOCKED_FW The channel load logic moves from the FW to the driver. - Implement the logic: allow EMLSR only if the candidate primary link is active and if its average channel load exceeds the threshold. - Remove IWL_MLD_EMLSR_BLOCKED_FW. Instead, treat ESR_RECOMMEND_LEAVE in the EMLSR_RECOMMENDATION notif as an EXIT reason. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.6729a8d67815.Iab39bf0982d8cdbb0db701d31854101c2fcf3b64@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/intel/iwlwifi/mld/constants.h | 2 +- .../net/wireless/intel/iwlwifi/mld/iface.h | 12 +- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 13 +- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 130 ++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mld/mlo.h | 5 +- .../net/wireless/intel/iwlwifi/mld/stats.c | 10 +- 6 files changed, 124 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h index 9a24996014b8..2a59b29b75cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h @@ -68,7 +68,7 @@ #define IWL_MLD_LOW_RSSI_THRESH_160MHZ -72 #define IWL_MLD_ENTER_EMLSR_TPT_THRESH 400 -#define IWL_MLD_CHAN_LOAD_THRESH 2 /* in percentage */ +#define IWL_MLD_EXIT_EMLSR_CHAN_LOAD 2 /* in percentage */ #define IWL_MLD_FTM_INITIATOR_ALGO IWL_TOF_ALGO_TYPE_MAX_LIKE #define IWL_MLD_FTM_INITIATOR_DYNACK true diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 57910660ed18..550ae3c9d766 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -24,7 +24,6 @@ enum iwl_mld_cca_40mhz_wa_status { * * @IWL_MLD_EMLSR_BLOCKED_PREVENTION: Prevent repeated EMLSR enter/exit * @IWL_MLD_EMLSR_BLOCKED_WOWLAN: WOWLAN is preventing EMLSR - * @IWL_MLD_EMLSR_BLOCKED_FW: FW did not recommend MLO * @IWL_MLD_EMLSR_BLOCKED_ROC: remain-on-channel is preventing EMLSR * @IWL_MLD_EMLSR_BLOCKED_NON_BSS: An active non-BSS interface's link is * preventing EMLSR @@ -36,11 +35,10 @@ enum iwl_mld_cca_40mhz_wa_status { enum iwl_mld_emlsr_blocked { IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1, IWL_MLD_EMLSR_BLOCKED_WOWLAN = 0x2, - IWL_MLD_EMLSR_BLOCKED_FW = 0x4, - IWL_MLD_EMLSR_BLOCKED_ROC = 0x8, - IWL_MLD_EMLSR_BLOCKED_NON_BSS = 0x10, - IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x20, - IWL_MLD_EMLSR_BLOCKED_TPT = 0x40, + IWL_MLD_EMLSR_BLOCKED_ROC = 0x4, + IWL_MLD_EMLSR_BLOCKED_NON_BSS = 0x8, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10, + IWL_MLD_EMLSR_BLOCKED_TPT = 0x20, }; /** @@ -62,6 +60,7 @@ enum iwl_mld_emlsr_blocked { * @IWL_MLD_EMLSR_EXIT_CHAN_LOAD: Exit EMLSR because the primary channel is not * loaded enough to justify EMLSR. * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI + * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it */ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_BLOCK = 0x1, @@ -75,6 +74,7 @@ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_BT_COEX = 0x100, IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x200, IWL_MLD_EMLSR_EXIT_RFI = 0x400, + IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x800, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 91e201fde72a..f6623988fff6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1189,8 +1189,13 @@ iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld, iwl_mld_omi_ap_changed_bw(mld, link_conf, bw); } - if (changes & BSS_CHANGED_BANDWIDTH) - iwl_mld_emlsr_check_equal_bw(mld, vif, link_conf); + if (changes & BSS_CHANGED_BANDWIDTH) { + if (iwl_mld_emlsr_active(vif)) + iwl_mld_emlsr_check_equal_bw(mld, vif, link_conf); + else + /* Channel load threshold may have changed */ + iwl_mld_retry_emlsr(mld, vif); + } } static int iwl_mld_update_mu_groups(struct iwl_mld *mld, @@ -1712,10 +1717,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, IWL_MLD_EMLSR_BLOCKED_TPT, 0); - /* Wait for the FW to send a recommendation */ - iwl_mld_block_emlsr(mld, vif, - IWL_MLD_EMLSR_BLOCKED_FW, 0); - /* clear COEX_HIGH_PRIORITY_ENABLE */ ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index a5b1d373922a..9556ff3a75a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -9,7 +9,6 @@ #define HANDLE_EMLSR_BLOCKED_REASONS(HOW) \ HOW(PREVENTION) \ HOW(WOWLAN) \ - HOW(FW) \ HOW(ROC) \ HOW(NON_BSS) \ HOW(TMP_NON_BSS) \ @@ -53,7 +52,8 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) HOW(LINK_USAGE) \ HOW(BT_COEX) \ HOW(CHAN_LOAD) \ - HOW(RFI) + HOW(RFI) \ + HOW(FW_REQUEST) static const char * iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit) @@ -332,18 +332,14 @@ iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac, return; switch (le32_to_cpu(notif->action)) { - case ESR_RECOMMEND_ENTER: - iwl_mld_unblock_emlsr(mld_vif->mld, vif, - IWL_MLD_EMLSR_BLOCKED_FW); - break; case ESR_RECOMMEND_LEAVE: - iwl_mld_block_emlsr(mld_vif->mld, vif, - IWL_MLD_EMLSR_BLOCKED_FW, - iwl_mld_get_primary_link(vif)); + iwl_mld_exit_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_EXIT_FW_REQUEST, + iwl_mld_get_primary_link(vif)); break; + case ESR_RECOMMEND_ENTER: case ESR_FORCE_LEAVE: default: - /* ESR_FORCE_LEAVE should not happen at this point */ IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n", le32_to_cpu(notif->action)); } @@ -724,6 +720,53 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld, return n_data; } +static u32 +iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx) +{ + const struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(chanctx); + + switch (phy->chandef.width) { + case NL80211_CHAN_WIDTH_320: + case NL80211_CHAN_WIDTH_160: + return 5; + case NL80211_CHAN_WIDTH_80: + return 7; + default: + break; + } + return 10; +} + +static bool +iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_mld_link_sel_data *a, + const struct iwl_mld_link_sel_data *b) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link_a = + iwl_mld_link_dereference_check(mld_vif, a->link_id); + struct ieee80211_chanctx_conf *chanctx_a = NULL; + u32 primary_load_perc; + + if (!link_a || !link_a->active) { + IWL_DEBUG_EHT(mld, "Primary link is not active. Can't enter EMLSR\n"); + return false; + } + + chanctx_a = wiphy_dereference(mld->wiphy, link_a->chan_ctx); + + if (WARN_ON(!chanctx_a)) + return false; + + primary_load_perc = + iwl_mld_phy_from_mac80211(chanctx_a)->avg_channel_load_not_by_us; + + IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc); + + return primary_load_perc > iwl_mld_get_min_chan_load_thresh(chanctx_a); +} + static bool iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, struct iwl_mld_link_sel_data *a, @@ -746,6 +789,8 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, */ reason_mask |= IWL_MLD_EMLSR_EXIT_BANDWIDTH; } + if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b)) + reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD; if (reason_mask) { IWL_DEBUG_INFO(mld, @@ -904,7 +949,6 @@ void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, link_conf_dereference_check(vif, other_link_id); if (!ieee80211_vif_link_active(vif, link->link_id) || - !iwl_mld_emlsr_active(vif) || WARN_ON(link->link_id == other_link_id || !other_link)) return; @@ -952,39 +996,67 @@ void iwl_mld_emlsr_check_bt(struct iwl_mld *mld) NULL); } -static void iwl_mld_emlsr_check_chan_load_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) +struct iwl_mld_chan_load_data { + struct iwl_mld_phy *phy; + u32 prev_chan_load_not_by_us; +}; + +static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) { - struct iwl_mld *mld = (struct iwl_mld *)_data; + struct iwl_mld_chan_load_data *data = _data; + const struct iwl_mld_phy *phy = data->phy; + struct ieee80211_chanctx_conf *chanctx = + container_of((const void *)phy, struct ieee80211_chanctx_conf, + drv_priv); + struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; struct ieee80211_bss_conf *prim_link; unsigned int prim_link_id; - int chan_load; - - if (!iwl_mld_emlsr_active(vif)) - return; prim_link_id = iwl_mld_get_primary_link(vif); prim_link = link_conf_dereference_protected(vif, prim_link_id); + if (WARN_ON(!prim_link)) return; - chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link, true); - - if (chan_load < 0) + if (chanctx != rcu_access_pointer(prim_link->chanctx_conf)) return; - /* chan_load is in range [0,255] */ - if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_CHAN_LOAD_THRESH)) - iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CHAN_LOAD, - prim_link_id); + if (iwl_mld_emlsr_active(vif)) { + int chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link, + true); + + if (chan_load < 0) + return; + + /* chan_load is in range [0,255] */ + if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_EXIT_EMLSR_CHAN_LOAD)) + iwl_mld_exit_emlsr(mld, vif, + IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + prim_link_id); + } else { + u32 old_chan_load = data->prev_chan_load_not_by_us; + u32 new_chan_load = phy->avg_channel_load_not_by_us; + u32 thresh = iwl_mld_get_min_chan_load_thresh(chanctx); + + if (old_chan_load <= thresh && new_chan_load > thresh) + iwl_mld_retry_emlsr(mld, vif); + } } -void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld) +void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, + struct iwl_mld_phy *phy, + u32 prev_chan_load_not_by_us) { - ieee80211_iterate_active_interfaces_mtx(mld->hw, + struct iwl_mld_chan_load_data data = { + .phy = phy, + .prev_chan_load_not_by_us = prev_chan_load_not_by_us, + }; + + ieee80211_iterate_active_interfaces_mtx(hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_emlsr_check_chan_load_iter, - (void *)(uintptr_t)mld); + iwl_mld_chan_load_update_iter, + &data); } void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index 0f1b18f61c75..a5fbe1919c6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -11,6 +11,7 @@ #include "iwl-config.h" #include "iwl-trans.h" #include "iface.h" +#include "phy.h" struct iwl_mld; @@ -139,7 +140,9 @@ void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); -void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld); +void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, + struct iwl_mld_phy *phy, + u32 prev_chan_load_not_by_us); /** * iwl_mld_retry_emlsr - Retry entering EMLSR diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 75cb204c2419..0715bbc31031 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -457,7 +457,7 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, { struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); const struct iwl_stats_ntfy_per_phy *per_phy = data; - u32 new_load; + u32 new_load, old_load; if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL)) return; @@ -465,14 +465,16 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, phy->channel_load_by_us = le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us); + old_load = phy->avg_channel_load_not_by_us; new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); if (IWL_FW_CHECK(phy->mld, new_load > 100, "Invalid channel load %u\n", new_load)) return; /* give a weight of 0.5 for the old value */ - phy->avg_channel_load_not_by_us = - (new_load >> 1) + (phy->avg_channel_load_not_by_us >> 1); + phy->avg_channel_load_not_by_us = (new_load >> 1) + (old_load >> 1); + + iwl_mld_emlsr_check_chan_load(hw, phy, old_load); } static void @@ -483,8 +485,6 @@ iwl_mld_process_per_phy_stats(struct iwl_mld *mld, iwl_mld_fill_chanctx_stats, (void *)(uintptr_t)per_phy); - /* channel_load_by_us may have been updated, so recheck */ - iwl_mld_emlsr_check_chan_load(mld); } void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, From c8fcf89b9e8e5478105f34f16c45dedd4d6f2738 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 341/465] wifi: iwlwifi: mld: prevent toggling EMLSR due to FW requests JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 008c04d53ef5af32b99dfcd502174ed05aaf80c9 Author: Miri Korenblit Date: Thu Mar 13 00:22:29 2025 +0200 wifi: iwlwifi: mld: prevent toggling EMLSR due to FW requests We exit EMLSR mode if the FW requested to do so. To prevent repeated toggling of the EMLSR mode (frequent entry and exit), add this exit reason to the EMLSR prevention mechanism. This mechanism avoids re-entering EMLSR for a certain period of time after multiple exits caused by the same reason. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.f0e74a7f99af.I447c8788afba85a2a5040ae2c1213b6e05ec14f3@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 9556ff3a75a1..8f6da90bf82c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -116,8 +116,9 @@ void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, #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 | \ - IWL_MLD_EMLSR_EXIT_LINK_USAGE) +#define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \ + IWL_MLD_EMLSR_EXIT_LINK_USAGE | \ + IWL_MLD_EMLSR_EXIT_FW_REQUEST) #define IWL_MLD_PREVENT_EMLSR_TIMEOUT (HZ * 400) #define IWL_MLD_EMLSR_PREVENT_SHORT (HZ * 300) From 8957f154dfae0b9e7d261ca5aaa8beac11fc164e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 342/465] wifi: iwlwifi: mld: allow EMLSR for unequal bandwidth JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c008fadb90b13c9f2a9759a448bc7d823f897a9b Author: Miri Korenblit Date: Thu Mar 13 00:22:30 2025 +0200 wifi: iwlwifi: mld: allow EMLSR for unequal bandwidth Allow EMLSR if the bandwidths of the links are unequal if one of the following conditions is true: 1. in low latency mode 2. bandwidth of the secondary link is greater than the bandwidth of the primary 3. the primary link is active and is loaded enough to justify EMLSR Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.150c330711c4.Ifd72d2e076783991852a7f1756948b4f0efb9fea@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/iface.h | 15 ++--- .../wireless/intel/iwlwifi/mld/low_latency.c | 4 ++ .../net/wireless/intel/iwlwifi/mld/mac80211.c | 10 +-- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 63 +++++++++++-------- drivers/net/wireless/intel/iwlwifi/mld/mlo.h | 4 -- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 550ae3c9d766..d1d56b081bf6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -52,8 +52,6 @@ enum iwl_mld_emlsr_blocked { * @IWL_MLD_EMLSR_EXIT_FAIL_ENTRY: FW failed to enter EMLSR * @IWL_MLD_EMLSR_EXIT_CSA: EMLSR prevented due to channel switch on link * @IWL_MLD_EMLSR_EXIT_EQUAL_BAND: EMLSR prevented as both links share the band - * @IWL_MLD_EMLSR_EXIT_BANDWIDTH: Bandwidths of primary and secondary links are - * not equal * @IWL_MLD_EMLSR_EXIT_LOW_RSSI: Link RSSI is unsuitable for EMLSR * @IWL_MLD_EMLSR_EXIT_LINK_USAGE: Exit EMLSR due to low TPT on secondary link * @IWL_MLD_EMLSR_EXIT_BT_COEX: Exit EMLSR due to BT coexistence @@ -68,13 +66,12 @@ enum iwl_mld_emlsr_exit { IWL_MLD_EMLSR_EXIT_FAIL_ENTRY = 0x4, IWL_MLD_EMLSR_EXIT_CSA = 0x8, IWL_MLD_EMLSR_EXIT_EQUAL_BAND = 0x10, - IWL_MLD_EMLSR_EXIT_BANDWIDTH = 0x20, - IWL_MLD_EMLSR_EXIT_LOW_RSSI = 0x40, - IWL_MLD_EMLSR_EXIT_LINK_USAGE = 0x80, - IWL_MLD_EMLSR_EXIT_BT_COEX = 0x100, - IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x200, - IWL_MLD_EMLSR_EXIT_RFI = 0x400, - IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x800, + IWL_MLD_EMLSR_EXIT_LOW_RSSI = 0x20, + IWL_MLD_EMLSR_EXIT_LINK_USAGE = 0x40, + IWL_MLD_EMLSR_EXIT_BT_COEX = 0x80, + IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x100, + IWL_MLD_EMLSR_EXIT_RFI = 0x200, + IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x400, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c index e74e66735f52..a4a612afb3b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -7,6 +7,7 @@ #include "low_latency.h" #include "hcmd.h" #include "power.h" +#include "mlo.h" #define MLD_LL_WK_INTERVAL_MSEC 500 #define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000) @@ -230,6 +231,9 @@ void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, return; iwl_mld_update_mac_power(mld, vif, false); + + if (low_latency) + iwl_mld_retry_emlsr(mld, vif); } static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index f6623988fff6..938cf5900a29 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1187,15 +1187,11 @@ iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld, bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); iwl_mld_omi_ap_changed_bw(mld, link_conf, bw); + } - if (changes & BSS_CHANGED_BANDWIDTH) { - if (iwl_mld_emlsr_active(vif)) - iwl_mld_emlsr_check_equal_bw(mld, vif, link_conf); - else - /* Channel load threshold may have changed */ - iwl_mld_retry_emlsr(mld, vif); - } + if (changes & BSS_CHANGED_BANDWIDTH) + iwl_mld_retry_emlsr(mld, vif); } static int iwl_mld_update_mu_groups(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 8f6da90bf82c..9342f03c0908 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -47,7 +47,6 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) HOW(FAIL_ENTRY) \ HOW(CSA) \ HOW(EQUAL_BAND) \ - HOW(BANDWIDTH) \ HOW(LOW_RSSI) \ HOW(LINK_USAGE) \ HOW(BT_COEX) \ @@ -748,6 +747,7 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, struct iwl_mld_link *link_a = iwl_mld_link_dereference_check(mld_vif, a->link_id); struct ieee80211_chanctx_conf *chanctx_a = NULL; + u32 bw_a, bw_b, ratio; u32 primary_load_perc; if (!link_a || !link_a->active) { @@ -765,7 +765,34 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc); - return primary_load_perc > iwl_mld_get_min_chan_load_thresh(chanctx_a); + if (primary_load_perc < iwl_mld_get_min_chan_load_thresh(chanctx_a)) { + IWL_DEBUG_EHT(mld, "Channel load is below the minimum threshold\n"); + return false; + } + + if (iwl_mld_vif_low_latency(mld_vif)) { + IWL_DEBUG_EHT(mld, "Low latency vif, EMLSR is allowed\n"); + return true; + } + + if (a->chandef->width <= b->chandef->width) + return true; + + bw_a = nl80211_chan_width_to_mhz(a->chandef->width); + bw_b = nl80211_chan_width_to_mhz(b->chandef->width); + ratio = bw_a / bw_b; + + switch (ratio) { + case 2: + return primary_load_perc > 25; + case 4: + return primary_load_perc > 40; + case 8: + case 16: + return primary_load_perc > 50; + } + + return false; } static bool @@ -784,12 +811,6 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, if (a->chandef->chan->band == b->chandef->chan->band) reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; - if (a->chandef->width != b->chandef->width) { - /* TODO: task=EMLSR task=statistics - * replace BANDWIDTH exit reason with channel load criteria - */ - reason_mask |= IWL_MLD_EMLSR_EXIT_BANDWIDTH; - } if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b)) reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD; @@ -941,23 +962,6 @@ void iwl_mld_select_links(struct iwl_mld *mld) NULL); } -void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link) -{ - u8 other_link_id = iwl_mld_get_other_link(vif, link->link_id); - struct ieee80211_bss_conf *other_link = - link_conf_dereference_check(vif, other_link_id); - - if (!ieee80211_vif_link_active(vif, link->link_id) || - WARN_ON(link->link_id == other_link_id || !other_link)) - return; - - if (link->chanreq.oper.width != other_link->chanreq.oper.width) - iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BANDWIDTH, - iwl_mld_get_primary_link(vif)); -} - static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) { @@ -1038,10 +1042,15 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, } else { u32 old_chan_load = data->prev_chan_load_not_by_us; u32 new_chan_load = phy->avg_channel_load_not_by_us; - u32 thresh = iwl_mld_get_min_chan_load_thresh(chanctx); + u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx); - if (old_chan_load <= thresh && new_chan_load > thresh) +#define THRESHOLD_CROSSED(threshold) \ + (old_chan_load <= (threshold) && new_chan_load > (threshold)) + + if (THRESHOLD_CROSSED(min_thresh) || THRESHOLD_CROSSED(25) || + THRESHOLD_CROSSED(40) || THRESHOLD_CROSSED(50)) iwl_mld_retry_emlsr(mld, vif); +#undef THRESHOLD_CROSSED } } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index a5fbe1919c6d..6c652c17069f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -134,10 +134,6 @@ void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk); void iwl_mld_select_links(struct iwl_mld *mld); -void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link); - void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, From 4a1b445d1c78c3c43b82c702e9e40d978d42057a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 343/465] wifi: iwlwifi: mld: KUnit: introduce iwl_mld_kunit_link JIRA: https://issues.redhat.com/browse/RHEL-89168 commit de5ca699bc3f7fe9f90ba927d8a6e7783cd7311d Author: Miri Korenblit Date: Thu Mar 13 00:22:31 2025 +0200 wifi: iwlwifi: mld: KUnit: introduce iwl_mld_kunit_link To allow setting up association/EMLSR states with more flexibility, change the relevant functions to receive a new struct, iwl_mld_kunit_link, which will contain all the link parameters (for now just link id, band and bandwidth). Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.f336491ccc4e.I6b727765eb394a3dbb78cea71e356be1bdc4a17c@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../intel/iwlwifi/mld/tests/link-selection.c | 13 +++--- .../wireless/intel/iwlwifi/mld/tests/link.c | 15 +++++-- .../wireless/intel/iwlwifi/mld/tests/utils.c | 45 +++++++++---------- .../wireless/intel/iwlwifi/mld/tests/utils.h | 22 ++++++--- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index d835550c1a6b..34782569d67e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -126,23 +126,24 @@ static void test_link_grading(struct kunit *test) struct ieee80211_vif *vif; struct ieee80211_bss_conf *link; unsigned int actual_grade; - u8 assoc_link_id; /* Extract test case parameters */ u8 link_id = test_param->input.link.link_id; - enum nl80211_band band = test_param->input.link.chandef->chan->band; bool active = test_param->input.link.active; u16 valid_links; + struct iwl_mld_kunit_link assoc_link = { + .band = test_param->input.link.chandef->chan->band, + }; /* If the link is not active, use a different link as the assoc link */ if (active) { - assoc_link_id = link_id; + assoc_link.id = link_id; valid_links = BIT(link_id); } else { - assoc_link_id = BIT(ffz(BIT(link_id))); - valid_links = BIT(assoc_link_id) | BIT(link_id); + assoc_link.id = BIT(ffz(BIT(link_id))); + valid_links = BIT(assoc_link.id) | BIT(link_id); } - vif = iwlmld_kunit_setup_mlo_assoc(valid_links, assoc_link_id, band); + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link); wiphy_lock(mld->wiphy); link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 6e251dbc1dfe..4a4eaa134bd3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include @@ -61,6 +61,14 @@ static void test_missed_beacon(struct kunit *test) (const void *)(test->param_value); struct ieee80211_vif *vif; struct iwl_rx_packet *pkt; + struct iwl_mld_kunit_link link1 = { + .id = 0, + .band = NL80211_BAND_6GHZ, + }; + struct iwl_mld_kunit_link link2 = { + .id = 1, + .band = NL80211_BAND_5GHZ, + }; kunit_activate_static_stub(test, ieee80211_connection_loss, fake_ieee80211_connection_loss); @@ -68,12 +76,11 @@ static void test_missed_beacon(struct kunit *test) notif = (void *)pkt->data; if (test_param->input.emlsr) { - vif = iwlmld_kunit_assoc_emlsr(0x3, NL80211_BAND_5GHZ, - NL80211_BAND_6GHZ); + vif = iwlmld_kunit_assoc_emlsr(&link1, &link2); } else { struct iwl_mld_vif *mld_vif; - vif = iwlmld_kunit_setup_non_mlo_assoc(NL80211_BAND_6GHZ); + vif = iwlmld_kunit_setup_non_mlo_assoc(&link1); mld_vif = iwl_mld_vif_from_mac80211(vif); notif->link_id = cpu_to_le32(mld_vif->deflink.fw_id); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index 6331cd91cdf6..fa2710661bd5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -329,7 +329,7 @@ static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif) } static struct ieee80211_vif * -iwlmld_kunit_setup_assoc(bool mlo, u8 link_id, enum nl80211_band band) +iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) { struct kunit *test = kunit_get_current_test(); struct iwl_mld *mld = test->priv; @@ -337,32 +337,32 @@ iwlmld_kunit_setup_assoc(bool mlo, u8 link_id, enum nl80211_band band) struct ieee80211_bss_conf *link; struct ieee80211_chanctx_conf *chan_ctx; - KUNIT_ASSERT_TRUE(test, mlo || link_id == 0); + KUNIT_ASSERT_TRUE(test, mlo || assoc_link->id == 0); vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION); if (mlo) - link = iwlmld_kunit_add_link(vif, link_id); + link = iwlmld_kunit_add_link(vif, assoc_link->id); else link = &vif->bss_conf; - chan_ctx = iwlmld_kunit_add_chanctx(band); + chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band); wiphy_lock(mld->wiphy); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); wiphy_unlock(mld->wiphy); /* The AP sta will now be pointer to by mld_vif->ap_sta */ - iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, link_id); + iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, assoc_link->id); iwlmld_kunit_set_vif_associated(vif); return vif; } -struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, - u8 assoc_link_id, - enum nl80211_band band) +struct ieee80211_vif * +iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + struct iwl_mld_kunit_link *assoc_link) { struct kunit *test = kunit_get_current_test(); struct ieee80211_vif *vif; @@ -370,13 +370,13 @@ struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 1 || hweight16(valid_links) == 2); - KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link_id)); + KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link->id)); - vif = iwlmld_kunit_setup_assoc(true, assoc_link_id, band); + vif = iwlmld_kunit_setup_assoc(true, assoc_link); /* Add the other link, if applicable */ if (hweight16(valid_links) > 1) { - u8 other_link_id = ffs(valid_links & ~BIT(assoc_link_id)) - 1; + u8 other_link_id = ffs(valid_links & ~BIT(assoc_link->id)) - 1; iwlmld_kunit_add_link(vif, other_link_id); } @@ -384,9 +384,10 @@ struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, return vif; } -struct ieee80211_vif *iwlmld_kunit_setup_non_mlo_assoc(enum nl80211_band band) +struct ieee80211_vif * +iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link) { - return iwlmld_kunit_setup_assoc(false, 0, band); + return iwlmld_kunit_setup_assoc(false, assoc_link); } struct iwl_rx_packet * @@ -403,9 +404,8 @@ _iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz) return pkt; } -struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, - enum nl80211_band band1, - enum nl80211_band band2) +struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, + struct iwl_mld_kunit_link *link2) { struct kunit *test = kunit_get_current_test(); struct iwl_mld *mld = test->priv; @@ -414,23 +414,20 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, struct ieee80211_chanctx_conf *chan_ctx; struct ieee80211_sta *sta; struct iwl_mld_vif *mld_vif; - u8 assoc_link_id, other_link_id; + u16 valid_links = BIT(link1->id) | BIT(link2->id); KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2); - assoc_link_id = ffs(valid_links) - 1; - other_link_id = ffs(valid_links & ~BIT(assoc_link_id)) - 1; - - vif = iwlmld_kunit_setup_mlo_assoc(valid_links, assoc_link_id, band1); + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, link1); mld_vif = iwl_mld_vif_from_mac80211(vif); /* Activate second link */ wiphy_lock(mld->wiphy); - link = wiphy_dereference(mld->wiphy, vif->link_conf[other_link_id]); + link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); KUNIT_EXPECT_NOT_NULL(test, link); - chan_ctx = iwlmld_kunit_add_chanctx(band2); + chan_ctx = iwlmld_kunit_add_chanctx(link2->band); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); wiphy_unlock(mld->wiphy); @@ -439,7 +436,7 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, sta = mld_vif->ap_sta; KUNIT_EXPECT_NOT_NULL(test, sta); - iwlmld_kunit_alloc_link_sta(sta, other_link_id); + iwlmld_kunit_alloc_link_sta(sta, link2->id); return vif; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index 9b1f2ada997f..bb757a7dd8e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -13,6 +13,12 @@ struct iwl_mld; int iwlmld_kunit_test_init(struct kunit *test); +struct iwl_mld_kunit_link { + u8 id; + enum nl80211_band band; + enum nl80211_chan_width bandwidth; +}; + enum nl80211_iftype; struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type); @@ -88,10 +94,12 @@ struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, enum ieee80211_sta_state state, int link_id); -struct ieee80211_vif *iwlmld_kunit_setup_mlo_assoc(u16 valid_links, - u8 assoc_link_id, - enum nl80211_band band); -struct ieee80211_vif *iwlmld_kunit_setup_non_mlo_assoc(enum nl80211_band band); +struct ieee80211_vif * +iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + struct iwl_mld_kunit_link *assoc_link); + +struct ieee80211_vif * +iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link); struct iwl_rx_packet * _iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz); @@ -99,9 +107,9 @@ _iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz); #define iwl_mld_kunit_create_pkt(_notif) \ _iwl_mld_kunit_create_pkt(&(_notif), sizeof(_notif)) -struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(u16 valid_links, - enum nl80211_band band1, - enum nl80211_band band2); +struct ieee80211_vif * +iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, + struct iwl_mld_kunit_link *link2); struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len); From 273a4dc75bd270b7a1cabfdd82b2b85275eb7821 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 344/465] wifi: iwlwifi: mld: KUnit: create chanctx with a custom width JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2907c039ff396d43d13ef19b0dbd9b86791f2520 Author: Miri Korenblit Date: Thu Mar 13 00:22:32 2025 +0200 wifi: iwlwifi: mld: KUnit: create chanctx with a custom width Currently iwlmld_kunit_add_chanctx receives a band, picks a predefined static chandef, and creates the chanctx from it. Change it to receive a bandwidth as well. Otherwise, the bandwidth in the chanctx/phy will be different than what test specified in the iwl_mld_kunit_link. Signed-off-by: Miri Korenblit Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.85a1285d34cd.Ia71cdcd4241fe73501bc93e3cb2c6bb3f631b9ec@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/tests/utils.c | 5 +++-- .../net/wireless/intel/iwlwifi/mld/tests/utils.h | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index fa2710661bd5..9712ee696509 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -346,7 +346,8 @@ iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) else link = &vif->bss_conf; - chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band); + chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band, + assoc_link->bandwidth); wiphy_lock(mld->wiphy); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); @@ -427,7 +428,7 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); KUNIT_EXPECT_NOT_NULL(test, link); - chan_ctx = iwlmld_kunit_add_chanctx(link2->band); + chan_ctx = iwlmld_kunit_add_chanctx(link2->band, link2->bandwidth); iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); wiphy_unlock(mld->wiphy); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index bb757a7dd8e7..d3723653cf1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -65,24 +65,26 @@ struct ieee80211_chanctx_conf * iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def); static inline struct ieee80211_chanctx_conf * -iwlmld_kunit_add_chanctx(enum nl80211_band band) +iwlmld_kunit_add_chanctx(enum nl80211_band band, enum nl80211_chan_width width) { - struct cfg80211_chan_def *chandef; + struct cfg80211_chan_def chandef; switch (band) { case NL80211_BAND_2GHZ: - chandef = &chandef_2ghz; + chandef = chandef_2ghz; break; case NL80211_BAND_5GHZ: - chandef = &chandef_5ghz; + chandef = chandef_5ghz; break; default: case NL80211_BAND_6GHZ: - chandef = &chandef_6ghz; + chandef = chandef_6ghz; break; } - return iwlmld_kunit_add_chanctx_from_def(chandef); + chandef.width = width; + + return iwlmld_kunit_add_chanctx_from_def(&chandef); } void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, From b69768ff4da2abbfed33ab5eb3b2467fc76af9fd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 345/465] wifi: iwlwifi: mld: KUnit: test iwl_mld_channel_load_allows_emlsr JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cf6efe8902350d2bdd968784a2522b672d77bb81 Author: Miri Korenblit Date: Thu Mar 13 00:22:33 2025 +0200 wifi: iwlwifi: mld: KUnit: test iwl_mld_channel_load_allows_emlsr Add tests to check that iwl_mld_channel_load_allows_emlsr decides correctly whether EMLSR is allowed or not. Signed-off-by: Miri Korenblit Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20250313002008.06fdf416c62f.If6e8f0e017287e79364eac9366f93c9ab964a673@changeid [fix kunit visibility macro] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 9 +- drivers/net/wireless/intel/iwlwifi/mld/mlo.h | 14 ++ .../intel/iwlwifi/mld/tests/link-selection.c | 131 ++++++++++++++++++ 3 files changed, 147 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 9342f03c0908..4bd3b2b63769 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -602,12 +602,6 @@ void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk) /* * Link selection */ -struct iwl_mld_link_sel_data { - u8 link_id; - const struct cfg80211_chan_def *chandef; - s32 signal; - u16 grade; -}; s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, const struct cfg80211_chan_def *chandef, @@ -737,7 +731,7 @@ iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx) return 10; } -static bool +VISIBLE_IF_IWLWIFI_KUNIT bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, const struct iwl_mld_link_sel_data *a, @@ -794,6 +788,7 @@ iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, return false; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_channel_load_allows_emlsr); static bool iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h index 6c652c17069f..4fb1fdbe3df9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -150,4 +150,18 @@ void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, */ void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif); +struct iwl_mld_link_sel_data { + u8 link_id; + const struct cfg80211_chan_def *chandef; + s32 signal; + u16 grade; +}; + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_mld_link_sel_data *a, + const struct iwl_mld_link_sel_data *b); +#endif + #endif /* __iwl_mld_mlo_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 34782569d67e..295dcfd3f85d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -11,6 +11,7 @@ #include "link.h" #include "iface.h" #include "phy.h" +#include "mlo.h" static const struct link_grading_test_case { const char *desc; @@ -170,3 +171,133 @@ static struct kunit_suite link_selection = { }; kunit_test_suite(link_selection); + +static const struct channel_load_case { + const char *desc; + bool low_latency_vif; + u32 chan_load_not_by_us; + enum nl80211_chan_width bw_a; + enum nl80211_chan_width bw_b; + bool primary_link_active; + bool expected_result; +} channel_load_cases[] = { + { + .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed", + .low_latency_vif = false, + .primary_link_active = false, + .bw_a = NL80211_CHAN_WIDTH_40, + .bw_b = NL80211_CHAN_WIDTH_20, + .expected_result = false, + }, + { + .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 11, + .bw_a = NL80211_CHAN_WIDTH_40, + .bw_b = NL80211_CHAN_WIDTH_40, + .expected_result = true, + }, + { + .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 6, + .bw_a = NL80211_CHAN_WIDTH_80, + .bw_b = NL80211_CHAN_WIDTH_80, + .expected_result = false, + }, + { + .desc = "Low latency VIF, sufficient channel load, EMLSR allowed", + .low_latency_vif = true, + .primary_link_active = true, + .chan_load_not_by_us = 6, + .bw_a = NL80211_CHAN_WIDTH_160, + .bw_b = NL80211_CHAN_WIDTH_160, + .expected_result = true, + }, + { + .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .bw_a = NL80211_CHAN_WIDTH_40, + .bw_b = NL80211_CHAN_WIDTH_20, + .expected_result = true, + }, + { + .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 45, + .bw_a = NL80211_CHAN_WIDTH_80, + .bw_b = NL80211_CHAN_WIDTH_20, + .expected_result = true, + }, + { + .desc = "Different bandwidths (16x ratio), primary link load insufficient", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 45, + .bw_a = NL80211_CHAN_WIDTH_320, + .bw_b = NL80211_CHAN_WIDTH_20, + .expected_result = false, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(channel_load, channel_load_cases, desc); + +static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) +{ + const struct channel_load_case *params = test->param_value; + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct cfg80211_chan_def chandef_a, chandef_b; + struct iwl_mld_link_sel_data a = {.chandef = &chandef_a, + .link_id = 4}; + struct iwl_mld_link_sel_data b = {.chandef = &chandef_b, + .link_id = 5}; + struct iwl_mld_kunit_link assoc_link = { + .id = params->primary_link_active ? a.link_id : b.link_id, + .bandwidth = params->primary_link_active ? params->bw_a : params->bw_b, + }; + bool result; + + vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id), + &assoc_link); + + chandef_a.width = params->bw_a; + chandef_b.width = params->bw_b; + + if (params->low_latency_vif) + iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1; + + wiphy_lock(mld->wiphy); + + /* Simulate channel load */ + if (params->primary_link_active) { + struct iwl_mld_phy *phy = + iwlmld_kunit_get_phy_of_link(vif, a.link_id); + + phy->avg_channel_load_not_by_us = params->chan_load_not_by_us; + } + + result = iwl_mld_channel_load_allows_emlsr(mld, vif, &a, &b); + + wiphy_unlock(mld->wiphy); + + KUNIT_EXPECT_EQ(test, result, params->expected_result); +} + +static struct kunit_case channel_load_criteria_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_channel_load_allows_emlsr, channel_load_gen_params), + {} +}; + +static struct kunit_suite channel_load_criteria_tests = { + .name = "iwlmld_channel_load_allows_emlsr", + .test_cases = channel_load_criteria_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(channel_load_criteria_tests); From ed7b781692a032721070a3afbc9b1f7baf0e7c9e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 346/465] wifi: iwlwifi: mld: make iwl_mld_run_fw_init_sequence static JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 892998c759065c9abb93ff6acbee0fae0c9cce13 Author: Miri Korenblit Date: Thu Mar 13 00:22:34 2025 +0200 wifi: iwlwifi: mld: make iwl_mld_run_fw_init_sequence static It is not used outside of fw.c. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.2d30c0b66734.I98cd21aeaf6e787af3ee3ed60d0ad8656ed8ec52@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/fw.c | 2 +- drivers/net/wireless/intel/iwlwifi/mld/mld.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 9c1dce0d5979..62da137e1024 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -257,7 +257,7 @@ alive_failure: return ret; } -int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) +static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) { struct iwl_notification_wait init_wait; struct iwl_init_extended_cfg_cmd init_cfg = {}; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 38f1d1bc5a24..5eceaaf7696d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -336,7 +336,6 @@ iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) {} #endif -int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld); int iwl_mld_load_fw(struct iwl_mld *mld); void iwl_mld_stop_fw(struct iwl_mld *mld); int iwl_mld_start_fw(struct iwl_mld *mld); From 01dd7349d7d4630ed990c9348252312fa310d9f3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 347/465] wifi: iwlwifi: mld: fix copy/paste error JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bb307028a0c8a39b1b2bc4960794ea00f774637e Author: Miri Korenblit Date: Thu Mar 13 00:22:35 2025 +0200 wifi: iwlwifi: mld: fix copy/paste error iwl_mld_emlsr_tmp_non_bss_done_wk used the wrong work name (prevent_done_wk) to extract the mld_vif pointer, so the pointer was a wrong one, leading to a page fault. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.aabb2232f9dd.I7cb24458a747e8363df2bf1ff848db6a9d472f60@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 4bd3b2b63769..a870e169e265 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -99,7 +99,7 @@ void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, struct wiphy_work *wk) { struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, - emlsr.prevent_done_wk.work); + emlsr.tmp_non_bss_done_wk.work); struct ieee80211_vif *vif = container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); From 14af00c14eee44895e420a939ace781e640d6873 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 348/465] wifi: iwlwifi: mld: iwl_mld_remove_link can't fail JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5789f7913ee37e0f54c073287ab4b5942feb4d12 Author: Miri Korenblit Date: Thu Mar 13 00:22:36 2025 +0200 wifi: iwlwifi: mld: iwl_mld_remove_link can't fail iwl_mld_remove_link removes the link from both the FW and from the driver. If removing it from the FW failed, we assume that the FW is dead anyway and remove it from the driver as well. On the other hand, we still return an error value, indicating the caller (i.e. mac80211) that the link couldn't be removed - while it was actually removed. Later, mac80211 might tell the driver again to remove that link, and then the driver will warn that it doesn't exist. Fix this by making iwl_mld_remove_link a void function. Signed-off-by: Miri Korenblit Reviewed-by: Benjamin Berg Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250313002008.16fe6ebae412.If5371ff7e096b7078ff9e98ff0e72010cd1f076d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 19 ++++++++----------- drivers/net/wireless/intel/iwlwifi/mld/link.h | 4 ++-- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 9 ++------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 6db8b5305349..82a4979a3af3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -455,7 +455,7 @@ void iwl_mld_deactivate_link(struct iwl_mld *mld, mld_link->fw_id); } -static int +static void iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) { struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); @@ -464,13 +464,13 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) lockdep_assert_wiphy(mld->wiphy); if (WARN_ON(!mld_link)) - return -EINVAL; + return; cmd.link_id = cpu_to_le32(mld_link->fw_id); cmd.spec_link_id = link->link_id; cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); - return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); + iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); } static void iwl_mld_omi_bw_update(struct iwl_mld *mld, @@ -832,18 +832,17 @@ free: } /* Remove link from fw, unmap the bss_conf, and destroy the link structure */ -int iwl_mld_remove_link(struct iwl_mld *mld, - struct ieee80211_bss_conf *bss_conf) +void iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); bool is_deflink = link == &mld_vif->deflink; - int ret; if (WARN_ON(!link || link->active)) - return -EINVAL; + return; - ret = iwl_mld_rm_link_from_fw(mld, bss_conf); + iwl_mld_rm_link_from_fw(mld, bss_conf); /* Continue cleanup on failure */ if (!is_deflink) @@ -854,11 +853,9 @@ int iwl_mld_remove_link(struct iwl_mld *mld, wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work); if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) - return -EINVAL; + return; RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); - - return ret; } void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index 3a4f3ac990d7..42b7bdcbd741 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -114,8 +114,8 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) int iwl_mld_add_link(struct iwl_mld *mld, struct ieee80211_bss_conf *bss_conf); -int iwl_mld_remove_link(struct iwl_mld *mld, - struct ieee80211_bss_conf *bss_conf); +void iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf); int iwl_mld_activate_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link); void iwl_mld_deactivate_link(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 938cf5900a29..6851064b82da 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -2456,13 +2456,8 @@ iwl_mld_change_vif_links(struct ieee80211_hw *hw, added |= BIT(0); for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - if (removed & BIT(i)) { - link_conf = old[i]; - - err = iwl_mld_remove_link(mld, link_conf); - if (err) - return err; - } + if (removed & BIT(i)) + iwl_mld_remove_link(mld, old[i]); } for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { From 488249b836cc6e457658de493aff6001f02aeb86 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:00:59 +0200 Subject: [PATCH 349/465] wifi: iwlwifi: mld: we support v6 of compressed_ba_notif JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 630b6c095e1910a295d8f4494684ef3502c00cf3 Author: Emmanuel Grumbach Date: Thu Mar 13 00:22:37 2025 +0200 wifi: iwlwifi: mld: we support v6 of compressed_ba_notif It just added a field that we don't use. Claim support for this new version. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250313002008.f3b668f41de7.Ib8d984c7158bf286adfb6d343955ff5f95b51d52@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/notif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index 06caedf9b0ba..fc18cba8aaa8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -347,7 +347,8 @@ CMD_VERSIONS(missed_beacon_notif, CMD_VERSIONS(tx_resp_notif, CMD_VER_ENTRY(8, iwl_tx_resp)) CMD_VERSIONS(compressed_ba_notif, - CMD_VER_ENTRY(5, iwl_compressed_ba_notif)) + CMD_VER_ENTRY(5, iwl_compressed_ba_notif) + CMD_VER_ENTRY(6, iwl_compressed_ba_notif)) CMD_VERSIONS(tlc_notif, CMD_VER_ENTRY(3, iwl_tlc_update_notif)) CMD_VERSIONS(mu_mimo_grp_notif, From b49528a2197c07614cac52bad582ceb2eea82b08 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 350/465] wifi: iwlwifi: mld: add debugfs to control MLO scan JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e4f4a4accf27185ed68493d1aeac1b79096e5a44 Author: Miri Korenblit Date: Thu Mar 13 00:22:38 2025 +0200 wifi: iwlwifi: mld: add debugfs to control MLO scan Add a debugfs entry to start/stop an MLO scan. This is required for testing. Signed-off-by: Miri Korenblit Reviewed-by: Benjamin Berg Link: https://patch.msgid.link/20250313002008.1a1c2d285336.I5163ceb97ac797e3cf00badf79b9aa9355d7327d@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/mld/debugfs.c | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 14330daa6d13..453ce2ba39d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -14,6 +14,7 @@ #include "notif.h" #include "ap.h" #include "iwl-utils.h" +#include "scan.h" #ifdef CONFIG_THERMAL #include "thermal.h" #endif @@ -901,6 +902,33 @@ iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count, VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256); +static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + u32 action; + int ret; + + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) + return -EINVAL; + + if (kstrtou32(buf, 0, &action)) + return -EINVAL; + + if (action == 0) { + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false); + } else if (action == 1) { + iwl_mld_int_mlo_scan(mld, vif); + ret = 0; + } else { + ret = -EINVAL; + } + + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32); + void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -939,8 +967,8 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600); VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200); VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200); } - #define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) From d7b9434d5ee03ed592849c0c93511360babbc773 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 351/465] wifi: mwifiex: Fix HT40 bandwidth issue. JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4fcfcbe457349267fe048524078e8970807c1a5b Author: Jeff Chen Date: Fri Mar 14 17:42:38 2025 +0800 wifi: mwifiex: Fix HT40 bandwidth issue. This patch addresses an issue where, despite the AP supporting 40MHz bandwidth, the connection was limited to 20MHz. Without this fix, even if the access point supports 40MHz, the bandwidth after connection remains at 20MHz. This issue is not a regression. Signed-off-by: Jeff Chen Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20250314094238.2097341-1-jeff.chen_1@nxp.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/11n.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 66f0f5377ac1..738bafc3749b 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -403,12 +403,14 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) { + chan_list->chan_scan_param[0].radio_type |= + CHAN_BW_40MHZ << 2; SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. radio_type, (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - + } *buffer += struct_size(chan_list, chan_scan_param, 1); ret_len += struct_size(chan_list, chan_scan_param, 1); } From f070dd104e532cb61e9f2464916628430d853b83 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 352/465] wifi: mac80211: fix indentation in ieee80211_set_monitor_channel() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 892726f0099efdbec0be8275b58af0dc70e02ff2 Author: Johannes Berg Date: Tue Mar 18 09:54:46 2025 +0100 wifi: mac80211: fix indentation in ieee80211_set_monitor_channel() There's some confusing indentation, I thought even that braces were missing. Fix indentation here. Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 229b4e2bd06f..1456eb481f0e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -919,7 +919,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) { if (cfg80211_chandef_identical(&local->monitor_chanreq.oper, - &chanreq.oper)) + &chanreq.oper)) return 0; sdata = wiphy_dereference(wiphy, local->monitor_sdata); @@ -928,7 +928,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, } if (rcu_access_pointer(sdata->deflink.conf->chanctx_conf) && - cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper, + cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper, &chanreq.oper)) return 0; From 4584a21b071d8afe34aa1cb35c5c16dfb9839ccc Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 353/465] wifi: mac80211: check basic rates validity in sta_link_apply_parameters JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 16ee3ea8faef8ff042acc15867a6c458c573de61 Author: Mikhail Lobanov Date: Mon Mar 17 13:31:37 2025 +0300 wifi: mac80211: check basic rates validity in sta_link_apply_parameters When userspace sets supported rates for a new station via NL80211_CMD_NEW_STATION, it might send a list that's empty or contains only invalid values. Currently, we process these values in sta_link_apply_parameters() without checking the result of ieee80211_parse_bitrates(), which can lead to an empty rates bitmap. A similar issue was addressed for NL80211_CMD_SET_BSS in commit ce04abc3fcc6 ("wifi: mac80211: check basic rates validity"). This patch applies the same approach in sta_link_apply_parameters() for NL80211_CMD_NEW_STATION, ensuring there is at least one valid rate by inspecting the result of ieee80211_parse_bitrates(). Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: b95eb7f0eee4 ("wifi: cfg80211/mac80211: separate link params from station params") Signed-off-by: Mikhail Lobanov Link: https://patch.msgid.link/20250317103139.17625-1-m.lobanov@rosa.ru Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/cfg.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1456eb481f0e..bec31f595f27 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1907,12 +1907,12 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, } if (params->supported_rates && - params->supported_rates_len) { - ieee80211_parse_bitrates(link->conf->chanreq.oper.width, - sband, params->supported_rates, - params->supported_rates_len, - &link_sta->pub->supp_rates[sband->band]); - } + params->supported_rates_len && + !ieee80211_parse_bitrates(link->conf->chanreq.oper.width, + sband, params->supported_rates, + params->supported_rates_len, + &link_sta->pub->supp_rates[sband->band])) + return -EINVAL; if (params->ht_capa) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, From d9a24bd0a58339419e0b68ca792b015bce80b2b0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 354/465] wifi: cfg80211: init wiphy_work before allocating rfkill fails JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-22119 commit fc88dee89d7b63eeb17699393eb659aadf9d9b7c Author: Edward Adam Davis Date: Tue Mar 18 11:13:45 2025 +0800 wifi: cfg80211: init wiphy_work before allocating rfkill fails syzbort reported a uninitialize wiphy_work_lock in cfg80211_dev_free. [1] After rfkill allocation fails, the wiphy release process will be performed, which will cause cfg80211_dev_free to access the uninitialized wiphy_work related data. Move the initialization of wiphy_work to before rfkill initialization to avoid this issue. [1] INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator. CPU: 0 UID: 0 PID: 5935 Comm: syz-executor550 Not tainted 6.14.0-rc6-syzkaller-00103-g4003c9e78778 #0 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 assign_lock_key kernel/locking/lockdep.c:983 [inline] register_lock_class+0xc39/0x1240 kernel/locking/lockdep.c:1297 __lock_acquire+0x135/0x3c40 kernel/locking/lockdep.c:5103 lock_acquire.part.0+0x11b/0x380 kernel/locking/lockdep.c:5851 __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:110 [inline] _raw_spin_lock_irqsave+0x3a/0x60 kernel/locking/spinlock.c:162 cfg80211_dev_free+0x30/0x3d0 net/wireless/core.c:1196 device_release+0xa1/0x240 drivers/base/core.c:2568 kobject_cleanup lib/kobject.c:689 [inline] kobject_release lib/kobject.c:720 [inline] kref_put include/linux/kref.h:65 [inline] kobject_put+0x1e4/0x5a0 lib/kobject.c:737 put_device+0x1f/0x30 drivers/base/core.c:3774 wiphy_free net/wireless/core.c:1224 [inline] wiphy_new_nm+0x1c1f/0x2160 net/wireless/core.c:562 ieee80211_alloc_hw_nm+0x1b7a/0x2260 net/mac80211/main.c:835 mac80211_hwsim_new_radio+0x1d6/0x54e0 drivers/net/wireless/virtual/mac80211_hwsim.c:5185 hwsim_new_radio_nl+0xb42/0x12b0 drivers/net/wireless/virtual/mac80211_hwsim.c:6242 genl_family_rcv_msg_doit+0x202/0x2f0 net/netlink/genetlink.c:1115 genl_family_rcv_msg net/netlink/genetlink.c:1195 [inline] genl_rcv_msg+0x565/0x800 net/netlink/genetlink.c:1210 netlink_rcv_skb+0x16b/0x440 net/netlink/af_netlink.c:2533 genl_rcv+0x28/0x40 net/netlink/genetlink.c:1219 netlink_unicast_kernel net/netlink/af_netlink.c:1312 [inline] netlink_unicast+0x53c/0x7f0 net/netlink/af_netlink.c:1338 netlink_sendmsg+0x8b8/0xd70 net/netlink/af_netlink.c:1882 sock_sendmsg_nosec net/socket.c:718 [inline] __sock_sendmsg net/socket.c:733 [inline] ____sys_sendmsg+0xaaf/0xc90 net/socket.c:2573 ___sys_sendmsg+0x135/0x1e0 net/socket.c:2627 __sys_sendmsg+0x16e/0x220 net/socket.c:2659 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 Fixes: 72d520476a2f ("wifi: cfg80211: cancel wiphy_work before freeing wiphy") Reported-by: syzbot+aaf0488c83d1d5f4f029@syzkaller.appspotmail.com Close: https://syzkaller.appspot.com/bug?extid=aaf0488c83d1d5f4f029 Tested-by: syzbot+aaf0488c83d1d5f4f029@syzkaller.appspotmail.com Signed-off-by: Edward Adam Davis Link: https://patch.msgid.link/tencent_258DD9121DDDB9DD9A1939CFAA0D8625B107@qq.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 29c37e2293df..a94613d1359c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -549,6 +549,9 @@ use_default_name: INIT_WORK(&rdev->mgmt_registrations_update_wk, cfg80211_mgmt_registrations_update_wk); spin_lock_init(&rdev->mgmt_registrations_lock); + INIT_WORK(&rdev->wiphy_work, cfg80211_wiphy_work); + INIT_LIST_HEAD(&rdev->wiphy_work_list); + spin_lock_init(&rdev->wiphy_work_lock); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -566,9 +569,6 @@ use_default_name: return NULL; } - INIT_WORK(&rdev->wiphy_work, cfg80211_wiphy_work); - INIT_LIST_HEAD(&rdev->wiphy_work_list); - spin_lock_init(&rdev->wiphy_work_lock); INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); INIT_WORK(&rdev->conn_work, cfg80211_conn_work); INIT_WORK(&rdev->event_work, cfg80211_event_work); From 19e7d4748d11f5b64977702daf69eb2ac9a75b1c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 355/465] wifi: mwifiex: Fix premature release of RF calibration data. JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 69ae7e1f73abae20f26e3536ca4a980b90eafeb7 Author: Jeff Chen Date: Tue Mar 18 13:07:38 2025 +0800 wifi: mwifiex: Fix premature release of RF calibration data. This patch resolves an issue where RF calibration data was being released before the download process. Without this fix, the external calibration data file would not be downloaded at all. Fixes: d39fbc88956e ("mwifiex: remove cfg_data construction") Signed-off-by: Jeff Chen Link: https://patch.msgid.link/20250318050739.2239376-2-jeff.chen_1@nxp.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/main.c | 4 ---- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 45eecb5f643b..b07cb302a00c 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -691,10 +691,6 @@ err_dnld_fw: init_failed = true; done: - if (adapter->cal_data) { - release_firmware(adapter->cal_data); - adapter->cal_data = NULL; - } if (adapter->firmware) { release_firmware(adapter->firmware); adapter->firmware = NULL; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index e2800a831c8e..c0e6ce1a82fe 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2293,9 +2293,13 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) "marvell,caldata"); } - if (adapter->cal_data) + if (adapter->cal_data) { mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, HostCmd_ACT_GEN_SET, 0, NULL, true); + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } + /* Read MAC address from HW */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, From 6e18da97b54ca5fc6ca201d3e3749b80fafa0b85 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 356/465] wifi: mwifiex: Fix RF calibration data download from file JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9868c4ce9481043d6c11f7421fe2b9637aa0feee Author: Jeff Chen Date: Tue Mar 18 13:07:39 2025 +0800 wifi: mwifiex: Fix RF calibration data download from file This patch resolves an issue where RF calibration data from a file could not be downloaded to the firmware. The feature to download calibration data from a file was broken by the commit: d39fbc88956e. The issue arose because the function `mwifiex_cmd_cfg_data()` was modified in a way that prevented proper handling of file-based calibration data. While this patch restores the ability to download RF calibration data from a file, it may inadvertently break the feature to download calibration data from the device tree. This is because the function `mwifiex_dnld_dt_cfgdata()`, which also relies on `mwifiex_cmd_cfg_data()`, is still used for device tree-based calibration data downloads. Fixes: d39fbc88956e ("mwifiex: remove cfg_data construction") Signed-off-by: Jeff Chen Link: https://patch.msgid.link/20250318050739.2239376-3-jeff.chen_1@nxp.com [add newline for shorter lines] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/fw.h | 14 ++++++++++++++ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 12 ++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 4a96281792cc..91458f3bd14a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -454,6 +454,11 @@ enum mwifiex_channel_flags { #define HostCmd_RET_BIT 0x8000 #define HostCmd_ACT_GEN_GET 0x0000 #define HostCmd_ACT_GEN_SET 0x0001 +#define HOST_CMD_ACT_GEN_SET 0x0001 +/* Add this non-CamelCase-style macro to comply with checkpatch requirements. + * This macro will eventually replace all existing CamelCase-style macros in + * the future for consistency. + */ #define HostCmd_ACT_GEN_REMOVE 0x0004 #define HostCmd_ACT_BITWISE_SET 0x0002 #define HostCmd_ACT_BITWISE_CLR 0x0003 @@ -2352,6 +2357,14 @@ struct host_cmd_ds_add_station { u8 tlv[]; } __packed; +#define MWIFIEX_CFG_TYPE_CAL 0x2 + +struct host_cmd_ds_802_11_cfg_data { + __le16 action; + __le16 type; + __le16 data_len; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -2431,6 +2444,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl; struct host_cmd_ds_sta_configure sta_cfg; struct host_cmd_ds_add_station sta_info; + struct host_cmd_ds_802_11_cfg_data cfg_data; } params; } __packed; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index c0e6ce1a82fe..c4689f5a1acc 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1507,6 +1507,7 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, u32 len; u8 *data = (u8 *)cmd + S_DS_GEN; int ret; + struct host_cmd_ds_802_11_cfg_data *pcfg_data; if (prop) { len = prop->length; @@ -1514,12 +1515,20 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, data, len); if (ret) return ret; + + cmd->size = cpu_to_le16(S_DS_GEN + len); mwifiex_dbg(adapter, INFO, "download cfg_data from device tree: %s\n", prop->name); } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, data); + adapter->cal_data->size, + data + sizeof(*pcfg_data)); + pcfg_data = &cmd->params.cfg_data; + pcfg_data->action = cpu_to_le16(HOST_CMD_ACT_GEN_SET); + pcfg_data->type = cpu_to_le16(MWIFIEX_CFG_TYPE_CAL); + pcfg_data->data_len = cpu_to_le16(len); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*pcfg_data) + len); mwifiex_dbg(adapter, INFO, "download cfg_data from config file\n"); } else { @@ -1527,7 +1536,6 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, } cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + len); return 0; } From 813589ac75d6686f0d3260a8560e73ecac139753 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 357/465] wifi: iwlwifi: remove a buggy else statement in op_mode selection JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e6f98260d76c9204cf799b281211f4a62f932678 Author: Emmanuel Grumbach Date: Tue Mar 18 10:29:43 2025 +0100 wifi: iwlwifi: remove a buggy else statement in op_mode selection This led to weird behavior. The next debug print was not printed. Fixes: d1e879ec600f9 ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250318103019.bf54d0474909.Icfb129d4cf13b42b13e2ac4aa1bd171ef46bf561@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index dcf07ac042f7..15d2211e565c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1732,7 +1732,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) #if IS_ENABLED(CONFIG_IWLMLD) if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) op = &iwlwifi_opmode_table[MLD_OP_MODE]; - else #else if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) { IWL_ERR(drv, From 61addd2e2367f88b80eaaac541408f0d90bcdd21 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:00 +0200 Subject: [PATCH 358/465] wifi: iwlwifi: do not use iwlmld for non-wifi7 devices JIRA: https://issues.redhat.com/browse/RHEL-89168 commit db9b4b8311c19410ea7b064902b16c371f0fff83 Author: Emmanuel Grumbach Date: Tue Mar 18 10:29:44 2025 +0100 wifi: iwlwifi: do not use iwlmld for non-wifi7 devices Roll-back to use iwlmvm for those devices. iwlmld will support wifi7 capable devices only. The firmware for the non-wifi7 capable will soon be frozen and we don't want iwlmld to have to support devices that will require the old APIs. Fixes: d1e879ec600f9 ("wifi: iwlwifi: add iwlmld sub-driver") Reviewed-by: Miriam Rachel Korenblit Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20250318103019.df6df96df826.I0020ca9f6c6c928caa78721666df131a692c6186@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 15d2211e565c..d36837501e08 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -174,6 +174,11 @@ static inline char iwl_drv_get_step(int step) return 'a' + step; } +static bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans) +{ + return CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM; +} + const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) { char mac_step, rf_step; @@ -1730,10 +1735,12 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) } #if IS_ENABLED(CONFIG_IWLMLD) - if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION && + iwl_drv_is_wifi7_supported(drv->trans)) op = &iwlwifi_opmode_table[MLD_OP_MODE]; #else - if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION) { + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION && + iwl_drv_is_wifi7_supported(drv->trans)) { IWL_ERR(drv, "IWLMLD needs to be compiled to support this firmware\n"); mutex_unlock(&iwlwifi_opmode_table_mtx); From d4fdde099f34660660e875af4df477e5ec64514a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 359/465] wifi: iwlwifi: dvm: Avoid -Wflex-array-member-not-at-end warnings JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e74c0a7875cfec27c25f582f001cc4625a0f8c07 Author: Gustavo A. R. Silva Date: Thu Aug 15 13:00:23 2024 -0600 wifi: iwlwifi: dvm: Avoid -Wflex-array-member-not-at-end warnings -Wflex-array-member-not-at-end was introduced in GCC-14, and we are getting ready to enable it, globally. So, in order to avoid ending up with a flexible-array member in the middle of multiple other structs, we use the `__struct_group()` helper to create a new tagged `struct iwl_tx_cmd_hdr`. This structure groups together all the members of the flexible `struct iwl_tx_cmd` except the flexible array. As a result, the array is effectively separated from the rest of the members without modifying the memory layout of the flexible structure. We then change the type of the middle struct members currently causing trouble from `struct iwl_tx_cmd` to `struct iwl_tx_cmd_hdr`. We also want to ensure that when new members need to be added to the flexible structure, they are always included within the newly created tagged struct. For this, we use `static_assert()`. This ensures that the memory layout for both the flexible structure and the new tagged struct is the same after any changes. This approach avoids having to implement `struct iwl_tx_cmd_hdr` as a completely separate structure, thus preventing having to maintain two independent but basically identical structures, closing the door to potential bugs in the future. So, with these changes, fix the following warnings: drivers/net/wireless/intel/iwlwifi/dvm/commands.h:2315:27: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] drivers/net/wireless/intel/iwlwifi/dvm/commands.h:2426:27: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] Signed-off-by: Gustavo A. R. Silva Acked-by: Miri Korenblit Link: https://patch.msgid.link/Zr5QR03+wyw571zd@elsanto Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/dvm/commands.h | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index 3f49c0bccb28..96ea6c8dfc89 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -1180,85 +1180,87 @@ struct iwl_dram_scratch { } __packed; struct iwl_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; + /* New members MUST be added within the __struct_group() macro below. */ + __struct_group(iwl_tx_cmd_hdr, __hdr, __packed, + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; - __le32 tx_flags; /* TX_CMD_FLG_* */ + __le32 tx_flags; /* TX_CMD_FLG_* */ - /* uCode may modify this field of the Tx command (in host DRAM!). - * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ - struct iwl_dram_scratch scratch; + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl_dram_scratch scratch; - /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ - __le32 rate_n_flags; /* RATE_MCS_* */ + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ - /* Index of destination station in uCode's station table */ - u8 sta_id; + /* Index of destination station in uCode's station table */ + u8 sta_id; - /* Type of security encryption: CCM or TKIP */ - u8 sec_ctl; /* TX_CMD_SEC_* */ + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ - /* - * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial - * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for - * data frames, this field may be used to selectively reduce initial - * rate (via non-0 value) for special frames (e.g. management), while - * still supporting rate scaling for all frames. - */ - u8 initial_rate_index; - u8 reserved; - u8 key[16]; - __le16 next_frame_flags; - __le16 reserved2; - union { - __le32 life_time; - __le32 attempt; - } stop_time; + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; - /* Host DRAM physical address pointer to "scratch" in this command. - * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - u8 tid_tspec; - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + ); /* * MAC header goes here, followed by 2 bytes padding if MAC header * length is 26 or 30 bytes, followed by payload data */ - union { - DECLARE_FLEX_ARRAY(u8, payload); - DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr); - }; + struct ieee80211_hdr hdr[]; } __packed; +static_assert(offsetof(struct iwl_tx_cmd, hdr) == sizeof(struct iwl_tx_cmd_hdr), + "struct member likely outside of __struct_group()"); /* * TX command response is sent after *agn* transmission attempts. @@ -2312,7 +2314,7 @@ struct iwl_scan_cmd { /* For active scans (set to all-0s for passive scans). * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct iwl_tx_cmd tx_cmd; + struct iwl_tx_cmd_hdr tx_cmd; /* For directed active scans (set to all-0s otherwise) */ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; @@ -2423,7 +2425,7 @@ struct iwlagn_beacon_notif { */ struct iwl_tx_beacon_cmd { - struct iwl_tx_cmd tx; + struct iwl_tx_cmd_hdr tx; __le16 tim_idx; u8 tim_size; u8 reserved1; From 47455352dc028d6ca70c75ccb50e47f8dcd5f795 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 360/465] wifi: nl80211: re-enable multi-link reconfiguration JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cd0b9da682cfe3b595b1d8195e6bfe8acc76e2c3 Author: Johannes Berg Date: Tue Mar 18 13:50:08 2025 +0100 wifi: nl80211: re-enable multi-link reconfiguration With the recent fixes, we can re-enable multi-link reconfiguration. Also add a CMD() entry to allow userspace discovery for it. Link: https://patch.msgid.link/20250318135009.a95c43837a0f.Ic6ed3d184e5be8ba47c6affa7271daaf824fd823@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/nl80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 97285d2fb914..91b04f5779a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2776,6 +2776,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, CMD(update_ft_ies, UPDATE_FT_IES); if (rdev->wiphy.sar_capa) CMD(set_sar_specs, SET_SAR_SPECS); + CMD(assoc_ml_reconf, ASSOC_MLO_RECONF); } #undef CMD @@ -16575,7 +16576,7 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) req.ext_mld_capa_ops = nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); - err = -EOPNOTSUPP; + err = cfg80211_assoc_ml_reconf(rdev, dev, &req); out: for (link_id = 0; link_id < ARRAY_SIZE(req.add_links); link_id++) From 418af651979e3dbdc5e6843c9a4cbcad04db1aa8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 361/465] wifi: mt76: mt7925: introduce MLO capability control JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5fd0ff3b96494e6a2eaf876ace3152a01dca9c1f Author: Ming Yen Hsieh Date: Mon Feb 17 16:17:29 2025 +0800 wifi: mt76: mt7925: introduce MLO capability control This patch introduces MLO capability control for the MT792x Wi-Fi driver. It removes the unused `MT792x_CHIP_CAP_MLO_EVT_EN` flag and introduces new capability flags `MT792x_CHIP_CAP_MLO_EN` and `MT792x_CHIP_CAP_MLO_EML_EN` to enable MLO and EML features based on firmware capabilities. Signed-off-by: Ming Yen Hsieh Reviewed-by: Sabeeh Khan Link: https://patch.msgid.link/20250217081729.1840930-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 10 ++++++++-- drivers/net/wireless/mediatek/mt76/mt792x.h | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 98daf80ac131..ad47a4b153da 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -251,7 +251,7 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy) }, }; - if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EVT_EN)) + if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EN)) return 0; ext_capab[0].eml_capabilities = phy->eml_cap; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 15815ad84713..5f40c3c1ffa1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2559,6 +2559,7 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif = link_conf->vif; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_phy *phy = mvif->phy; struct bss_mld_tlv *mld; struct tlv *tlv; bool is_mld; @@ -2574,8 +2575,13 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, mld->group_mld_id = is_mld ? mvif->bss_conf.mt76.idx : 0xff; mld->own_mld_id = mconf->mt76.idx + 32; mld->remap_idx = 0xff; - mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & - IEEE80211_EML_CAP_EMLSR_SUPP); + + if (phy->chip_cap & MT792x_CHIP_CAP_MLO_EML_EN) { + mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & + IEEE80211_EML_CAP_EMLSR_SUPP); + } else { + mld->eml_enable = 0; + } memcpy(mld->mac_addr, vif->addr, ETH_ALEN); } diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 32ed01a96bf7..454c6f523cc2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -27,8 +27,9 @@ #define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0) #define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1) -#define MT792x_CHIP_CAP_MLO_EVT_EN BIT(2) #define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3) +#define MT792x_CHIP_CAP_MLO_EN BIT(8) +#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9) /* NOTE: used to map mt76_rates. idx may change if firmware expands table */ #define MT792x_BASIC_RATES_TBL 11 From 05d418519979f79a680f3c92c56b45714dd0afb0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 362/465] wifi: mt76: mt7925: fix fails to enter low power mode in suspend state JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2d5630b0c9466ac6549495828aa7dce7424a272a Author: Quan Zhou Date: Tue Jan 14 13:06:22 2025 +0800 wifi: mt76: mt7925: fix fails to enter low power mode in suspend state The mt7925 sometimes fails to enter low power mode during suspend. This is caused by the chip firmware sending an additional ACK event to the host after processing the suspend command. Due to timing issues, this event may not reach the host, causing the chip to get stuck. To resolve this, the ACK flag in the suspend command is removed, as it is not needed in the MT7925 architecture. This prevents the firmware from sending the additional ACK event, ensuring the device can reliably enter low power mode during suspend. Signed-off-by: Quan Zhou Link: https://patch.msgid.link/d056938144a3a0336c3a4e3cec6f271899f32bf7.1736775666.git.quan.zhou@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 5f40c3c1ffa1..4bbbcfd5cdd4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3272,6 +3272,9 @@ int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, else uni_txd->option = MCU_CMD_UNI_EXT_ACK; + if (cmd == MCU_UNI_CMD(HIF_CTRL)) + uni_txd->option &= ~MCU_CMD_ACK; + goto exit; } From 9545994ded495d118e5773dbe615c6e73fb3cbd0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 363/465] wifi: mt76: mt7915: fix possible integer overflows in mt7915_muru_stats_show() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 77b749520cac06d000d9923f79ffa632cdea6113 Author: Nikita Zhandarovich Date: Tue Jan 14 07:44:41 2025 -0800 wifi: mt76: mt7915: fix possible integer overflows in mt7915_muru_stats_show() Assuming sums of values stored in variables such as sub_total_cnt and total_ppdu_cnt are big enough to warrant their u64 type, it makes sense to ensure that their calculation takes into account possible integer overflow issues. Play it safe and fix the problem by casting right hand expressions to u64 as well. Also, slightly adjust tabulation. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 1966a5078f2d ("mt76: mt7915: add mu-mimo and ofdma debugfs knobs") Signed-off-by: Nikita Zhandarovich Link: https://patch.msgid.link/20250114154441.16920-1-n.zhandarovich@fintech.ru Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 578013884e43..4fec7d000a63 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -303,9 +303,9 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.dl_vht_3mu_cnt, phy->mib.dl_vht_4mu_cnt); - sub_total_cnt = phy->mib.dl_vht_2mu_cnt + - phy->mib.dl_vht_3mu_cnt + - phy->mib.dl_vht_4mu_cnt; + sub_total_cnt = (u64)phy->mib.dl_vht_2mu_cnt + + phy->mib.dl_vht_3mu_cnt + + phy->mib.dl_vht_4mu_cnt; seq_printf(file, "\nTotal non-HE MU-MIMO DL PPDU count: %lld", sub_total_cnt); @@ -353,26 +353,27 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.dl_he_9to16ru_cnt, phy->mib.dl_he_gtr16ru_cnt); - sub_total_cnt = phy->mib.dl_he_2mu_cnt + - phy->mib.dl_he_3mu_cnt + - phy->mib.dl_he_4mu_cnt; + sub_total_cnt = (u64)phy->mib.dl_he_2mu_cnt + + phy->mib.dl_he_3mu_cnt + + phy->mib.dl_he_4mu_cnt; total_ppdu_cnt = sub_total_cnt; seq_printf(file, "\nTotal HE MU-MIMO DL PPDU count: %lld", sub_total_cnt); - sub_total_cnt = phy->mib.dl_he_2ru_cnt + - phy->mib.dl_he_3ru_cnt + - phy->mib.dl_he_4ru_cnt + - phy->mib.dl_he_5to8ru_cnt + - phy->mib.dl_he_9to16ru_cnt + - phy->mib.dl_he_gtr16ru_cnt; + sub_total_cnt = (u64)phy->mib.dl_he_2ru_cnt + + phy->mib.dl_he_3ru_cnt + + phy->mib.dl_he_4ru_cnt + + phy->mib.dl_he_5to8ru_cnt + + phy->mib.dl_he_9to16ru_cnt + + phy->mib.dl_he_gtr16ru_cnt; total_ppdu_cnt += sub_total_cnt; seq_printf(file, "\nTotal HE OFDMA DL PPDU count: %lld", sub_total_cnt); - total_ppdu_cnt += phy->mib.dl_he_su_cnt + phy->mib.dl_he_ext_su_cnt; + total_ppdu_cnt += (u64)phy->mib.dl_he_su_cnt + + phy->mib.dl_he_ext_su_cnt; seq_printf(file, "\nAll HE DL PPDU count: %lld", total_ppdu_cnt); @@ -404,20 +405,20 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.ul_hetrig_9to16ru_cnt, phy->mib.ul_hetrig_gtr16ru_cnt); - sub_total_cnt = phy->mib.ul_hetrig_2mu_cnt + - phy->mib.ul_hetrig_3mu_cnt + - phy->mib.ul_hetrig_4mu_cnt; + sub_total_cnt = (u64)phy->mib.ul_hetrig_2mu_cnt + + phy->mib.ul_hetrig_3mu_cnt + + phy->mib.ul_hetrig_4mu_cnt; total_ppdu_cnt = sub_total_cnt; seq_printf(file, "\nTotal HE MU-MIMO UL TB PPDU count: %lld", sub_total_cnt); - sub_total_cnt = phy->mib.ul_hetrig_2ru_cnt + - phy->mib.ul_hetrig_3ru_cnt + - phy->mib.ul_hetrig_4ru_cnt + - phy->mib.ul_hetrig_5to8ru_cnt + - phy->mib.ul_hetrig_9to16ru_cnt + - phy->mib.ul_hetrig_gtr16ru_cnt; + sub_total_cnt = (u64)phy->mib.ul_hetrig_2ru_cnt + + phy->mib.ul_hetrig_3ru_cnt + + phy->mib.ul_hetrig_4ru_cnt + + phy->mib.ul_hetrig_5to8ru_cnt + + phy->mib.ul_hetrig_9to16ru_cnt + + phy->mib.ul_hetrig_gtr16ru_cnt; total_ppdu_cnt += sub_total_cnt; seq_printf(file, "\nTotal HE OFDMA UL TB PPDU count: %lld", From 126649c9d342e7a2d00a33053b44adb8b251862c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 364/465] wifi: mt76: mt7925: ensure wow pattern command align fw format JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8ae45b1f699bbc27ea8647093f794f671e77410b Author: Ming Yen Hsieh Date: Thu Jan 16 13:59:25 2025 +0800 wifi: mt76: mt7925: ensure wow pattern command align fw format Align the format of "struct mt7925_wow_pattern_tlv" with firmware to ensure proper functionality. Cc: stable@vger.kernel.org Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250116055925.3856856-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 1e47d2c61b54..6bf1623e542e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -566,8 +566,8 @@ struct mt7925_wow_pattern_tlv { u8 offset; u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN]; u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN]; - u8 rsv[7]; -} __packed; + u8 rsv[4]; +}; struct roc_acquire_tlv { __le16 tag; From 7a13ebcad9fbf2fe9d2cfe7a66bc4ae649c8564f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 365/465] wifi: mt76: mt7925: fix country count limitation for CLC JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6458d760a0c0afd2fda11e83ed3e1125a252432f Author: Ming Yen Hsieh Date: Thu Jan 16 14:21:31 2025 +0800 wifi: mt76: mt7925: fix country count limitation for CLC Due to the increase in the number of power tables for 6Ghz on CLC, the variable nr_country is no longer sufficient to represent the total quantity. Therefore, we have switched to calculating the length of clc buf to obtain the correct power table. Cc: stable@vger.kernel.org Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250116062131.3860198-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 4bbbcfd5cdd4..10272af24dd1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3164,13 +3164,14 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, .acpi_conf = mt792x_acpi_get_flags(&dev->phy), }; int ret, valid_cnt = 0; - u8 i, *pos; + u8 *pos, *last_pos; if (!clc) return 0; pos = clc->data + sizeof(*seg) * clc->nr_seg; - for (i = 0; i < clc->nr_country; i++) { + last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); + while (pos < last_pos) { struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos; pos += sizeof(*rule); From 0bd9afe8f471e2d44f6b7ff7f98344cab8c71971 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 366/465] wifi: mt76: Add check for devm_kstrdup() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4bc1da524b502999da28d287de4286c986a1af57 Author: Haoxiang Li Date: Wed Feb 19 11:36:45 2025 +0800 wifi: mt76: Add check for devm_kstrdup() Add check for the return value of devm_kstrdup() in mt76_get_of_data_from_mtd() to catch potential exception. Fixes: e7a6a044f9b9 ("mt76: testmode: move mtd part to mt76_dev") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20250219033645.2594753-1-haoxiang_li2024@163.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/eeprom.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index 0bc66cc19acd..443517d06c9f 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -95,6 +95,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l #ifdef CONFIG_NL80211_TESTMODE dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL); + if (!dev->test_mtd.name) { + ret = -ENOMEM; + goto out_put_node; + } dev->test_mtd.offset = offset; #endif From 1671ff29fda0a60e029f1bd144be73d79eec013f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 367/465] wifi: mt76: mt7925: Remove unnecessary if-check JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ffb9d7bcd3e3e2d937bcfd7fc064b15f3b7ca3e3 Author: Thorsten Blum Date: Mon Feb 24 00:21:24 2025 +0100 wifi: mt76: mt7925: Remove unnecessary if-check The if and else branches implement the same logic. Remove the unnecessary if-check and simplify the code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250223232127.99357-2-thorsten.blum@linux.dev Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 10272af24dd1..eee65cf48203 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1983,11 +1983,7 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev, mlink = mt792x_sta_to_link(msta, link_sta->link_id); } info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid; - - if (link_sta) - info.newly = state != MT76_STA_INFO_STATE_ASSOC; - else - info.newly = state == MT76_STA_INFO_STATE_ASSOC ? false : true; + info.newly = state != MT76_STA_INFO_STATE_ASSOC; if (ieee80211_vif_is_mld(vif)) err = mt7925_mcu_mlo_sta_cmd(&dev->mphy, &info); From b45b38eeedc1822413c1efbee4dd2f457ff6daa0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:01 +0200 Subject: [PATCH 368/465] wifi: mt76: mt7925: Simplify HIF suspend handling to avoid suspend fail JIRA: https://issues.redhat.com/browse/RHEL-89168 commit bf39813599b0375a3eebbbc6837f728554b3883a Author: Quan Zhou Date: Mon Feb 24 21:05:14 2025 +0800 wifi: mt76: mt7925: Simplify HIF suspend handling to avoid suspend fail System suspend failures may occur due to inappropriate handling of traffic not idle event by the WiFi driver. The WiFi firmware's traffic not idle indication does not need to be tied to suspend. Fix the flow to ensuring the system can suspend properly. Signed-off-by: Quan Zhou Link: https://patch.msgid.link/34208c7280325f57a651363d339399eb1744d3b7.1740400998.git.quan.zhou@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index eee65cf48203..7885d71b7a77 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -348,14 +348,10 @@ mt7925_mcu_handle_hif_ctrl_basic(struct mt792x_dev *dev, struct tlv *tlv) basic = (struct mt7925_mcu_hif_ctrl_basic_tlv *)tlv; if (basic->hifsuspend) { - if (basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && - basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE) - /* success */ - dev->hif_idle = true; - else - /* busy */ - /* invalid */ - dev->hif_idle = false; + dev->hif_idle = true; + if (!(basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && + basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE)) + dev_info(dev->mt76.dev, "Hif traffic not idle.\n"); } else { dev->hif_resumed = true; } From 53cf507c0eebef9594d7ec9ca721b615fb35b2fc Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 369/465] wifi: mt76: mt7921: fix kernel panic due to null pointer dereference JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-22032 commit adc3fd2a2277b7cc0b61692463771bf9bd298036 Author: Ming Yen Hsieh Date: Tue Feb 18 11:33:42 2025 +0800 wifi: mt76: mt7921: fix kernel panic due to null pointer dereference Address a kernel panic caused by a null pointer dereference in the `mt792x_rx_get_wcid` function. The issue arises because the `deflink` structure is not properly initialized with the `sta` context. This patch ensures that the `deflink` structure is correctly linked to the `sta` context, preventing the null pointer dereference. BUG: kernel NULL pointer dereference, address: 0000000000000400 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 [#1] PREEMPT SMP NOPTI CPU: 0 UID: 0 PID: 470 Comm: mt76-usb-rx phy Not tainted 6.12.13-gentoo-dist #1 Hardware name: /AMD HUDSON-M1, BIOS 4.6.4 11/15/2011 RIP: 0010:mt792x_rx_get_wcid+0x48/0x140 [mt792x_lib] RSP: 0018:ffffa147c055fd98 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff8e9ecb652000 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8e9ecb652000 RBP: 0000000000000685 R08: ffff8e9ec6570000 R09: 0000000000000000 R10: ffff8e9ecd2ca000 R11: ffff8e9f22a217c0 R12: 0000000038010119 R13: 0000000080843801 R14: ffff8e9ec6570000 R15: ffff8e9ecb652000 FS: 0000000000000000(0000) GS:ffff8e9f22a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000400 CR3: 000000000d2ea000 CR4: 00000000000006f0 Call Trace: ? __die_body.cold+0x19/0x27 ? page_fault_oops+0x15a/0x2f0 ? search_module_extables+0x19/0x60 ? search_bpf_extables+0x5f/0x80 ? exc_page_fault+0x7e/0x180 ? asm_exc_page_fault+0x26/0x30 ? mt792x_rx_get_wcid+0x48/0x140 [mt792x_lib] mt7921_queue_rx_skb+0x1c6/0xaa0 [mt7921_common] mt76u_alloc_queues+0x784/0x810 [mt76_usb] ? __pfx___mt76_worker_fn+0x10/0x10 [mt76] __mt76_worker_fn+0x4f/0x80 [mt76] kthread+0xd2/0x100 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x34/0x50 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 ---[ end trace 0000000000000000 ]--- Reported-by: Nick Morrow Closes: https://github.com/morrownr/USB-WiFi/issues/577 Cc: stable@vger.kernel.org Fixes: 90c10286b176 ("wifi: mt76: mt7925: Update mt792x_rx_get_wcid for per-link STA") Signed-off-by: Ming Yen Hsieh Tested-by: Salah Coronya Link: https://patch.msgid.link/20250218033343.1999648-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 13e58c328aff..78b77a54d195 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -811,6 +811,7 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->deflink.wcid.phy_idx = mvif->bss_conf.mt76.band_idx; msta->deflink.wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->deflink.last_txs = jiffies; + msta->deflink.sta = msta; ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); if (ret) From 125c00a38477137d958a53dd94bbcd0ac7f1a4f2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 370/465] Revert "wifi: mt76: mt7925: Update mt7925_mcu_uni_[tx,rx]_ba for MLO" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 766ea2cf5a398c7eed519b12c6c6cf1631143ea2 Author: Sean Wang Date: Tue Mar 4 16:08:46 2025 -0800 Revert "wifi: mt76: mt7925: Update mt7925_mcu_uni_[tx,rx]_ba for MLO" For MLO, mac80211 will send the BA action for each link to the driver, so the driver does not need to handle it itself. Therefore, revert this patch. Fixes: eb2a9a12c609 ("wifi: mt76: mt7925: Update mt7925_mcu_uni_[tx,rx]_ba for MLO") Cc: stable@vger.kernel.org Signed-off-by: Ming Yen Hsieh Tested-by: Caleb Jorden Signed-off-by: Sean Wang Link: https://patch.msgid.link/20250305000851.493671-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/main.c | 10 ++-- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 50 ++++--------------- .../wireless/mediatek/mt76/mt7925/mt7925.h | 2 - 3 files changed, 14 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index ad47a4b153da..47a6040381a0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1289,22 +1289,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, params->buf_size); - mt7925_mcu_uni_rx_ba(dev, vif, params, true); + mt7925_mcu_uni_rx_ba(dev, params, true); break; case IEEE80211_AMPDU_RX_STOP: mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); - mt7925_mcu_uni_rx_ba(dev, vif, params, false); + mt7925_mcu_uni_rx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; - mt7925_mcu_uni_tx_ba(dev, vif, params, true); + mt7925_mcu_uni_tx_ba(dev, params, true); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, vif, params, false); + mt7925_mcu_uni_tx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_START: set_bit(tid, &msta->deflink.wcid.ampdu_state); @@ -1313,7 +1313,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, vif, params, false); + mt7925_mcu_uni_tx_ba(dev, params, false); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 7885d71b7a77..ebe7cc30aaf9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -572,10 +572,10 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) static int mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, - struct mt76_wcid *wcid, struct ieee80211_ampdu_params *params, bool enable, bool tx) { + struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; @@ -603,60 +603,28 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, /** starec & wtbl **/ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - struct mt792x_link_sta *mlink; - struct mt792x_bss_conf *mconf; - unsigned long usable_links = ieee80211_vif_usable_links(vif); - struct mt76_wcid *wcid; - u8 link_id, ret; + struct mt792x_vif *mvif = msta->vif; - for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { - mconf = mt792x_vif_to_link(mvif, link_id); - mlink = mt792x_sta_to_link(msta, link_id); - wcid = &mlink->wcid; + if (enable && !params->amsdu) + msta->deflink.wcid.amsdu = false; - if (enable && !params->amsdu) - mlink->wcid.amsdu = false; - - ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params, - enable, true); - if (ret < 0) - break; - } - - return ret; + return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + enable, true); } int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - struct mt792x_link_sta *mlink; - struct mt792x_bss_conf *mconf; - unsigned long usable_links = ieee80211_vif_usable_links(vif); - struct mt76_wcid *wcid; - u8 link_id, ret; + struct mt792x_vif *mvif = msta->vif; - for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { - mconf = mt792x_vif_to_link(mvif, link_id); - mlink = mt792x_sta_to_link(msta, link_id); - wcid = &mlink->wcid; - - ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params, - enable, false); - if (ret < 0) - break; - } - - return ret; + return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + enable, false); } static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 8707b5d04743..fd5f9d4ea4a7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -263,11 +263,9 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable); int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable); int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable); void mt7925_scan_work(struct work_struct *work); From 611be4c97c381b71db3a1257c72caf3d5070ee45 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 371/465] wifi: mt76: mt7925: fix the wrong link_idx when a p2p_device is present JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4bada9b0a29c185d45cc9512509edd6069fbfa79 Author: Ming Yen Hsieh Date: Tue Mar 4 16:08:47 2025 -0800 wifi: mt76: mt7925: fix the wrong link_idx when a p2p_device is present When the p2p device and MLO station are running concurrently, the p2p device will occupy the wrong link_idx when the MLO secondary link is added. Fixes: 9e4c3a007f01 ("wifi: mt76: connac: Extend mt76_connac_mcu_uni_add_dev for MLO") Cc: stable@vger.kernel.org Co-developed-by: Sean Wang Signed-off-by: Sean Wang Tested-by: Caleb Jorden Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250305000851.493671-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt76_connac_mcu.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 14 ++++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 132148f7b107..05651efb549e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -769,6 +769,7 @@ struct mt76_testmode_data { struct mt76_vif_link { u8 idx; + u8 link_idx; u8 omac_idx; u8 band_idx; u8 wmm_idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index f30cf9e71610..d0e49d68c5db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1168,7 +1168,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, - .link_idx = mvif->idx, + .link_idx = mvif->link_idx, }, }; struct { @@ -1191,7 +1191,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, .bmc_tx_wlan_idx = cpu_to_le16(wcid->idx), .sta_idx = cpu_to_le16(wcid->idx), .conn_state = 1, - .link_idx = mvif->idx, + .link_idx = mvif->link_idx, }, }; int err, idx, cmd, len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 47a6040381a0..676882f3928e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -360,10 +360,15 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, struct mt76_txq *mtxq; int idx, ret = 0; - mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask); - if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) { - ret = -ENOSPC; - goto out; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mconf->mt76.idx = MT792x_MAX_INTERFACES; + } else { + mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask); + + if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } } mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ? @@ -371,6 +376,7 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, mconf->mt76.band_idx = 0xff; mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; + mconf->mt76.link_idx = hweight16(mvif->valid_links); if (mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4; From bc3c01e187c4a80b24b3c398668624cceec37f0b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 372/465] wifi: mt76: mt7925: fix the wrong simultaneous cap for MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7dcea6fe33ee3d7cbb65baee0dd7adc76d1c9ddc Author: Ming Yen Hsieh Date: Tue Mar 4 16:08:48 2025 -0800 wifi: mt76: mt7925: fix the wrong simultaneous cap for MLO The mt7925 chip is only support a single radio, so the maximum number of simultaneous should be 0. Fixes: 86c051f2c418 ("wifi: mt76: mt7925: enabling MLO when the firmware supports it") Cc: stable@vger.kernel.org Co-developed-by: Sean Wang Signed-off-by: Sean Wang Tested-by: Caleb Jorden Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250305000851.493671-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 676882f3928e..dd886b39f550 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -256,7 +256,7 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy) ext_capab[0].eml_capabilities = phy->eml_cap; ext_capab[0].mld_capa_and_ops = - u16_encode_bits(1, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS); + u16_encode_bits(0, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS); wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; wiphy->iftype_ext_capab = ext_capab; From 312a5d4d5bf0ebbfd07dfa25b56c7ddb42b1f6da Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 373/465] wifi: mt76: mt7925: adjust rm BSS flow to prevent next connection failure JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0ebb60da8416c1d8e84c7e511a5687ce76a9467a Author: Ming Yen Hsieh Date: Tue Mar 4 16:08:49 2025 -0800 wifi: mt76: mt7925: adjust rm BSS flow to prevent next connection failure Removing BSS without removing STAREC first will cause firmware abnormal and next connection fail. Fixes: 816161051a03 ("wifi: mt76: mt7925: Cleanup MLO settings post-disconnection") Cc: stable@vger.kernel.org Co-developed-by: Sean Wang Signed-off-by: Sean Wang Tested-by: Caleb Jorden Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250305000851.493671-4-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/main.c | 66 +++++++++---------- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 56 ++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/mcu.h | 2 + 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index dd886b39f550..2f6c687d0a05 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1155,7 +1155,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, struct mt792x_bss_conf *mconf; mconf = mt792x_link_conf_to_mconf(link_conf); - mt792x_mac_link_bss_remove(dev, mconf, mlink); + + if (ieee80211_vif_is_mld(vif)) + mt792x_mac_link_bss_remove(dev, mconf, mlink); + else + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); } spin_lock_bh(&mdev->sta_poll_lock); @@ -1175,6 +1180,31 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, struct mt76_wcid *wcid; unsigned int link_id; + /* clean up bss before starec */ + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; + + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); + if (!link_sta) + continue; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + continue; + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + if (!link_conf) + continue; + + mconf = mt792x_link_conf_to_mconf(link_conf); + + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); + } + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_link_sta *link_sta; struct mt792x_link_sta *mlink; @@ -1212,44 +1242,14 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; - struct { - struct { - u8 omac_idx; - u8 band_idx; - __le16 pad; - } __packed hdr; - struct req_tlv { - __le16 tag; - __le16 len; - u8 active; - u8 link_idx; /* hw link idx */ - u8 omac_addr[ETH_ALEN]; - } __packed tlv; - } dev_req = { - .hdr = { - .omac_idx = 0, - .band_idx = 0, - }, - .tlv = { - .tag = cpu_to_le16(DEV_INFO_ACTIVE), - .len = cpu_to_le16(sizeof(struct req_tlv)), - .active = true, - }, - }; unsigned long rem; rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0); mt7925_mac_sta_remove_links(dev, vif, sta, rem); - if (ieee80211_vif_is_mld(vif)) { - mt7925_mcu_set_dbdc(&dev->mphy, false); - - /* recovery omac address for the legacy interface */ - memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); - mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), - &dev_req, sizeof(dev_req), true); - } + if (ieee80211_vif_is_mld(vif)) + mt7925_mcu_del_dev(mdev, vif); if (vif->type == NL80211_IFTYPE_STATION) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index ebe7cc30aaf9..d970243d64ff 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2634,6 +2634,62 @@ int mt7925_mcu_set_timing(struct mt792x_phy *phy, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 omac_idx; + u8 band_idx; + __le16 pad; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 link_idx; /* hw link idx */ + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } dev_req = { + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = true, + }, + }; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + } basic_req = { + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .active = true, + .conn_state = 1, + }, + }; + + dev_req.hdr.omac_idx = mvif->omac_idx; + dev_req.hdr.band_idx = mvif->band_idx; + + basic_req.hdr.bss_idx = mvif->idx; + basic_req.basic.omac_idx = mvif->omac_idx; + basic_req.basic.band_idx = mvif->band_idx; + basic_req.basic.link_idx = mvif->link_idx; + + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), + &basic_req, sizeof(basic_req), true); + + /* recovery omac address for the legacy interface */ + memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), + &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, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 6bf1623e542e..8ac43feb26d6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -627,6 +627,8 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, struct ieee80211_vif *vif, bool enable); +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif); int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf, From 0edc8b93fac5f6d13773dcb23949a0fce9b5bf12 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 374/465] wifi: mt76: mt7925: integrate *mlo_sta_cmd and *sta_cmd JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cb1353ef34735ec1e5d9efa1fe966f05ff1dc1e1 Author: Ming Yen Hsieh Date: Tue Mar 4 16:08:50 2025 -0800 wifi: mt76: mt7925: integrate *mlo_sta_cmd and *sta_cmd Integrate *mlo_sta_cmd and *sta_cmd for the MLO firmware. Fixes: 86c051f2c418 ("wifi: mt76: mt7925: enabling MLO when the firmware supports it") Cc: stable@vger.kernel.org Co-developed-by: Sean Wang Signed-off-by: Sean Wang Tested-by: Caleb Jorden Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250305000851.493671-5-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 59 ++----------------- 1 file changed, 4 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index d970243d64ff..17baa653dab1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1814,49 +1814,6 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb, } } -static int -mt7925_mcu_sta_cmd(struct mt76_phy *phy, - struct mt76_sta_cmd_info *info) -{ - struct mt76_vif_link *mvif = (struct mt76_vif_link *)info->vif->drv_priv; - struct mt76_dev *dev = phy->dev; - struct sk_buff *skb; - int conn_state; - - skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, info->wcid, - MT7925_STA_UPDATE_MAX_SIZE); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - conn_state = info->enable ? CONN_STATE_PORT_SECURE : - CONN_STATE_DISCONNECT; - if (info->link_sta) - mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, - info->link_sta, - conn_state, info->newly); - if (info->link_sta && info->enable) { - mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); - 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_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); - mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif, - info->link_sta); - mt7925_mcu_sta_state_v2_tlv(phy, skb, info->link_sta, - info->vif, info->rcpi, - info->state); - mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); - } - - if (info->enable) - mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); - - return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); -} - static void mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) { @@ -1869,8 +1826,8 @@ mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) } static int -mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy, - struct mt76_sta_cmd_info *info) +mt7925_mcu_sta_cmd(struct mt76_phy *phy, + struct mt76_sta_cmd_info *info) { struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv; struct mt76_dev *dev = phy->dev; @@ -1884,12 +1841,10 @@ mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy, if (IS_ERR(skb)) return PTR_ERR(skb); - if (info->enable) + if (info->enable && info->link_sta) { mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, info->link_sta, info->enable, info->newly); - - if (info->enable && info->link_sta) { mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); mt7925_mcu_sta_ht_tlv(skb, info->link_sta); mt7925_mcu_sta_vht_tlv(skb, info->link_sta); @@ -1940,7 +1895,6 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev, }; struct mt792x_sta *msta; struct mt792x_link_sta *mlink; - int err; if (link_sta) { msta = (struct mt792x_sta *)link_sta->sta->drv_priv; @@ -1949,12 +1903,7 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev, info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid; info.newly = state != MT76_STA_INFO_STATE_ASSOC; - if (ieee80211_vif_is_mld(vif)) - err = mt7925_mcu_mlo_sta_cmd(&dev->mphy, &info); - else - err = mt7925_mcu_sta_cmd(&dev->mphy, &info); - - return err; + return mt7925_mcu_sta_cmd(&dev->mphy, &info); } int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, From 512fbdb9ae951a92cdae65a7df0e00b2887466cd Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 375/465] wifi: mt76: mt7925: update the power-saving flow JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 276a568832577c81ec90b62dc506bbdc3781ca46 Author: Ming Yen Hsieh Date: Tue Mar 4 16:08:51 2025 -0800 wifi: mt76: mt7925: update the power-saving flow After joining MLO, ensure that all links are setup before enabling power-saving. Fixes: 86c051f2c418 ("wifi: mt76: mt7925: enabling MLO when the firmware supports it") Cc: stable@vger.kernel.org Co-developed-by: Sean Wang Signed-off-by: Sean Wang Tested-by: Caleb Jorden Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250305000851.493671-6-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/init.c | 1 + .../net/wireless/mediatek/mt76/mt7925/main.c | 68 ++++++++++++++++--- .../wireless/mediatek/mt76/mt7925/mt7925.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x.h | 9 +++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index f41ca4248497..a2bb36dab231 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -244,6 +244,7 @@ int mt7925_register_device(struct mt792x_dev *dev) dev->mt76.tx_worker.fn = mt792x_tx_worker; INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work); + INIT_DELAYED_WORK(&dev->mlo_pm_work, mt7925_mlo_pm_work); INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work); spin_lock_init(&dev->pm.wake.lock); mutex_init(&dev->pm.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2f6c687d0a05..0b390b2691e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -427,6 +427,7 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mvif->bss_conf.vif = mvif; mvif->sta.vif = mvif; mvif->deflink_id = IEEE80211_LINK_UNSPECIFIED; + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; ret = mt7925_mac_link_bss_add(dev, &vif->bss_conf, &mvif->sta.deflink); if (ret < 0) @@ -1242,6 +1243,7 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; unsigned long rem; rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0); @@ -1252,11 +1254,11 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, mt7925_mcu_del_dev(mdev, vif); if (vif->type == NL80211_IFTYPE_STATION) { - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - mvif->wep_sta = NULL; ewma_rssi_init(&mvif->bss_conf.rssi); } + + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; } EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove); @@ -1328,6 +1330,38 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return ret; } +static void +mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct ieee80211_bss_conf *bss_conf; + int i; + + if (mvif->mlo_pm_state != MT792x_MLO_CHANGED_PS) + return; + + mt792x_mutex_acquire(dev); + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + mt792x_mutex_release(dev); +} + +void mt7925_mlo_pm_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + mlo_pm_work.work); + struct ieee80211_hw *hw = mt76_hw(dev); + + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mlo_pm_iter, dev); +} + static bool is_valid_alpha2(const char *alpha2) { if (!alpha2) @@ -1877,6 +1911,9 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, mt7925_mcu_sta_update(dev, NULL, vif, true, MT76_STA_INFO_STATE_ASSOC); mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc); + + if (ieee80211_vif_is_mld(vif)) + mvif->mlo_pm_state = MT792x_MLO_LINK_ASSOC; } if (changed & BSS_CHANGED_ARP_FILTER) { @@ -1887,9 +1924,19 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_PS) { - for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { - bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (hweight16(mvif->valid_links) < 2) { + /* legacy */ + bss_conf = &vif->bss_conf; mt7925_mcu_uni_bss_ps(dev, bss_conf); + } else { + if (mvif->mlo_pm_state == MT792x_MLO_LINK_ASSOC) { + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS_PENDING; + } else if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS) { + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + } } } @@ -1940,11 +1987,12 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw, if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) mt7925_mcu_set_tx(dev, info); - if (changed & BSS_CHANGED_BSSID) { - if (ieee80211_vif_is_mld(vif) && - hweight16(mvif->valid_links) == 2) - /* Indicate the secondary setup done */ - mt7925_mcu_uni_bss_bcnft(dev, info, true); + if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS_PENDING) { + /* Indicate the secondary setup done */ + mt7925_mcu_uni_bss_bcnft(dev, info, true); + + ieee80211_queue_delayed_work(hw, &dev->mlo_pm_work, 5 * HZ); + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS; } mt792x_mutex_release(dev); @@ -2028,8 +2076,6 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto free; if (mconf != &mvif->bss_conf) { - mt7925_mcu_set_bss_pm(dev, link_conf, true); - err = mt7925_set_mlo_roc(phy, &mvif->bss_conf, vif->active_links); if (err < 0) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index fd5f9d4ea4a7..cb7b1a49fbd1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -268,6 +268,7 @@ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, bool enable); +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); int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 454c6f523cc2..048f9ab16126 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -82,6 +82,13 @@ enum mt792x_reg_power_type { MT_AP_VLP, }; +enum mt792x_mlo_pm_state { + MT792x_MLO_LINK_DISASSOC, + MT792x_MLO_LINK_ASSOC, + MT792x_MLO_CHANGED_PS_PENDING, + MT792x_MLO_CHANGED_PS, +}; + DECLARE_EWMA(avg_signal, 10, 8) struct mt792x_link_sta { @@ -135,6 +142,7 @@ struct mt792x_vif { struct mt792x_phy *phy; u16 valid_links; u8 deflink_id; + enum mt792x_mlo_pm_state mlo_pm_state; struct work_struct csa_work; struct timer_list csa_timer; @@ -240,6 +248,7 @@ struct mt792x_dev { const struct mt792x_irq_map *irq_map; struct work_struct ipv6_ns_work; + struct delayed_work mlo_pm_work; /* IPv6 addresses for WoWLAN */ struct sk_buff_head ipv6_ns_list; From b62ee06d2017e747c513fd8ac8759dd25afb2d07 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 376/465] wifi: mt76: mt7925: load the appropriate CLC data based on hardware type JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f2027ef3f733d3f0bb7f27fa3343784058f946ab Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:44 2025 +0800 wifi: mt76: mt7925: load the appropriate CLC data based on hardware type Read the EEPROM to determine the hardware type and uses this to load the correct CLC data. Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 61 ++++++++++++++++++- .../wireless/mediatek/mt76/mt7925/mt7925.h | 3 + 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 17baa653dab1..0f58a5afb77a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -627,6 +627,54 @@ int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, enable, false); } +static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) +{ + struct { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + __le32 addr; + __le32 valid; + u8 data[MT7925_EEPROM_BLOCK_SIZE]; + } __packed req = { + .tag = cpu_to_le16(1), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = cpu_to_le32(round_down(offset, + MT7925_EEPROM_BLOCK_SIZE)), + }; + struct evt { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ver; + __le32 addr; + __le32 valid; + __le32 size; + __le32 magic_num; + __le32 type; + __le32 rsv1[4]; + u8 data[32]; + } __packed *res; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct evt *)skb->data; + *val = res->data[offset % MT7925_EEPROM_BLOCK_SIZE]; + + dev_kfree_skb(skb); + + return 0; +} + static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) { const struct mt76_connac2_fw_trailer *hdr; @@ -635,13 +683,20 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; const struct firmware *fw; + u8 *clc_base = NULL, hw_encap = 0; int ret, i, len, offset = 0; - u8 *clc_base = NULL; if (mt7925_disable_clc || mt76_is_usb(&dev->mt76)) return 0; + if (mt76_is_mmio(&dev->mt76)) { + ret = mt7925_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); + if (ret) + return ret; + hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); + } + ret = request_firmware(&fw, fw_name, mdev->dev); if (ret) return ret; @@ -686,6 +741,10 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) if (phy->clc[clc->idx]) continue; + /* header content sanity */ + if (u8_get_bits(clc->type, MT_EE_HW_TYPE_ENCAP) != hw_encap) + continue; + phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, le32_to_cpu(clc->len), GFP_KERNEL); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index cb7b1a49fbd1..073e433069e0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -167,9 +167,12 @@ enum mt7925_eeprom_field { MT_EE_CHIP_ID = 0x000, MT_EE_VERSION = 0x002, MT_EE_MAC_ADDR = 0x004, + MT_EE_HW_TYPE = 0xa71, __MT_EE_MAX = 0x9ff }; +#define MT_EE_HW_TYPE_ENCAP GENMASK(1, 0) + enum { TXPWR_USER, TXPWR_EEPROM, From 6399fbc1f054ae5af96c03082b2367c539548b22 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:02 +0200 Subject: [PATCH 377/465] wifi: mt76: mt7925: add EHT control support based on the CLC data JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f0317215b367e22e1cde5dcdb4c0687f0a85b913 Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:45 2025 +0800 wifi: mt76: mt7925: add EHT control support based on the CLC data Some countries do not support EHT modulation for now. To prevent violating regulations, the MT7925 chipset should control the EHT capabilities. Therefore, when a regulatory domain change is detected during scanning, the `mt7925_regd_be_ctrl` will process the CLC data to update the EHT capabilities. Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-2-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/init.c | 38 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/main.c | 2 + .../net/wireless/mediatek/mt76/mt7925/mcu.c | 10 +++-- .../wireless/mediatek/mt76/mt7925/mt7925.h | 27 +++++++++++-- drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + 5 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index a2bb36dab231..46b12a2e81ee 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -58,6 +58,44 @@ static int mt7925_thermal_init(struct mt792x_phy *phy) return PTR_ERR_OR_ZERO(hwmon); } +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2) +{ + struct mt792x_phy *phy = &dev->phy; + struct mt7925_clc_rule_v2 *rule; + struct mt7925_clc *clc; + bool old = dev->has_eht, new = true; + u8 *pos; + + if (!phy->clc[MT792x_CLC_BE_CTRL]) + goto out; + + clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL]; + pos = clc->data; + + while (1) { + rule = (struct mt7925_clc_rule_v2 *)pos; + + if (rule->alpha2[0] == alpha2[0] && + rule->alpha2[1] == alpha2[1]) { + new = false; + break; + } + + /* Check the last one */ + if (rule->flag && BIT(0)) + break; + + pos += sizeof(*rule); + } + +out: + if (old == new) + return; + + dev->has_eht = new; + mt7925_set_stream_he_eht_caps(phy); +} + void mt7925_regd_update(struct mt792x_dev *dev) { struct mt76_dev *mdev = &dev->mt76; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 0b390b2691e3..e79364ac129e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1418,6 +1418,8 @@ void mt7925_scan_work(struct work_struct *work) if (!is_valid_alpha2(evt->alpha2)) break; + mt7925_regd_be_ctrl(phy->dev, evt->alpha2); + if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 0f58a5afb77a..575b51931d7d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -742,7 +742,9 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) continue; /* header content sanity */ - if (u8_get_bits(clc->type, MT_EE_HW_TYPE_ENCAP) != hw_encap) + if ((clc->idx == MT792x_CLC_BE_CTRL && + u8_get_bits(clc->t2.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) || + u8_get_bits(clc->t0.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) continue; phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, @@ -851,7 +853,6 @@ mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data) mdev->phy.chainmask = mdev->phy.antenna_mask; mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); - dev->has_eht = cap->eht; } static void @@ -3193,7 +3194,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, if (!clc) return 0; - pos = clc->data + sizeof(*seg) * clc->nr_seg; + pos = clc->data + sizeof(*seg) * clc->t0.nr_seg; last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); while (pos < last_pos) { struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos; @@ -3239,6 +3240,9 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { + if (i == MT792x_CLC_BE_CTRL) + continue; + ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap, phy->clc[i], i); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 073e433069e0..5cd3073dfc50 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -137,6 +137,12 @@ enum { MT7925_CLC_MAX_NUM, }; +struct mt7925_clc_rule_v2 { + u32 flag; + u8 alpha2[2]; + u8 rsv[10]; +} __packed; + struct mt7925_clc_rule { u8 alpha2[2]; u8 type[2]; @@ -152,14 +158,26 @@ struct mt7925_clc_segment { u8 rsv2[4]; } __packed; -struct mt7925_clc { - __le32 len; - u8 idx; - u8 ver; +struct mt7925_clc_type0 { u8 nr_country; u8 type; u8 nr_seg; u8 rsv[7]; +} __packed; + +struct mt7925_clc_type2 { + u8 type; + u8 rsv[9]; +} __packed; + +struct mt7925_clc { + __le32 len; + u8 idx; + u8 ver; + union { + struct mt7925_clc_type0 t0; + struct mt7925_clc_type2 t2; + }; u8 data[]; } __packed; @@ -238,6 +256,7 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd); int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, u8 bit_op, u32 bit_map); +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2); void mt7925_regd_update(struct mt792x_dev *dev); int mt7925_mac_init(struct mt792x_dev *dev); int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 048f9ab16126..97e9de88a63d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -71,6 +71,7 @@ struct mt792x_fw_features { enum { MT792x_CLC_POWER, MT792x_CLC_POWER_EXT, + MT792x_CLC_BE_CTRL, MT792x_CLC_MAX_NUM, }; From 0a77be32e010c70019e33cb96b12c66b3b3d7322 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 378/465] wifi: mt76: mt7925: update the channel usage when the regd domain changed JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 91b3790240b5114a07a5d14a33e48d6c99350496 Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:46 2025 +0800 wifi: mt76: mt7925: update the channel usage when the regd domain changed The 5.9/6GHz channel license of a certain platform device has been regulated in various countries. That may be difference with standard Liunx regulatory domain settings. In this case, when .reg_notifier() called for regulatory change, mt7925 chipset should update the channel usage based on CLC data. Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-3-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/init.c | 40 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/mcu.c | 7 +++- .../wireless/mediatek/mt76/mt7925/mt7925.h | 3 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 46b12a2e81ee..8732b05355db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -96,15 +96,55 @@ out: mt7925_set_stream_he_eht_caps(phy); } +static void +mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_channel *ch; + int i; + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) || + IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) || + IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) || + IS_UNII_INVALID(4, 6875, 7125, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } +} + void mt7925_regd_update(struct mt792x_dev *dev) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; + struct wiphy *wiphy = hw->wiphy; if (!dev->regd_change) return; mt7925_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); + mt7925_regd_channel_update(wiphy, dev); mt7925_mcu_set_channel_domain(hw->priv); mt7925_set_tx_sar_pwr(hw, NULL); dev->regd_change = false; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 575b51931d7d..30f88a9dcded 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -686,6 +686,7 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) u8 *clc_base = NULL, hw_encap = 0; int ret, i, len, offset = 0; + dev->phy.clc_chan_conf = 0xff; if (mt7925_disable_clc || mt76_is_usb(&dev->mt76)) return 0; @@ -3194,6 +3195,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, if (!clc) return 0; + req.ver = clc->ver; pos = clc->data + sizeof(*seg) * clc->t0.nr_seg; last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); while (pos < last_pos) { @@ -3211,6 +3213,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, memcpy(req.type, rule->type, 2); req.size = cpu_to_le16(seg->len); + 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), sizeof(req), GFP_KERNEL); @@ -3226,8 +3229,10 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, valid_cnt++; } - if (!valid_cnt) + if (!valid_cnt) { + dev->phy.clc_chan_conf = 0xff; return -ENOENT; + } return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 5cd3073dfc50..4e50f2597ccd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -147,7 +147,8 @@ struct mt7925_clc_rule { u8 alpha2[2]; u8 type[2]; u8 seg_idx; - u8 rsv[3]; + u8 flag; /* UNII4~8 ctrl flag */ + u8 rsv[2]; } __packed; struct mt7925_clc_segment { From ade04a457b646f301fa6f7376ab38065d5edf7cb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 379/465] wifi: mt76: mt7925: remove unused acpi function for clc JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b4ea6fdfc08375aae59c7e7059653b9877171fe4 Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:47 2025 +0800 wifi: mt76: mt7925: remove unused acpi function for clc The code for handling ACPI configuration in CLC was copied from the mt7921 driver but is not utilized in the mt7925 implementation. So removes the unused functionality to clean up the codebase. Cc: stable@vger.kernel.org Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-4-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 30f88a9dcded..e61da76b2097 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3187,7 +3187,6 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, .idx = idx, .env = env_cap, - .acpi_conf = mt792x_acpi_get_flags(&dev->phy), }; int ret, valid_cnt = 0; u8 *pos, *last_pos; From b95021eab4bab19a43dac40660e22ebd391b7591 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 380/465] wifi: mt76: mt792x: extend MTCL of APCI to version3 for EHT control JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7cebc2300de2c6948792c5b9bee8937f219b58f7 Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:48 2025 +0800 wifi: mt76: mt792x: extend MTCL of APCI to version3 for EHT control This patch introduces version 3 of the MTCL table, which provides regulatory information for WiFi 7. It also configured by the platform vender like the version 1 and 2. Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-5-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt792x.h | 6 +- .../wireless/mediatek/mt76/mt792x_acpi_sar.c | 123 ++++++++++++++---- .../wireless/mediatek/mt76/mt792x_acpi_sar.h | 18 ++- 3 files changed, 120 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 97e9de88a63d..e0359d431eca 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -508,7 +508,7 @@ int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev); int mt792x_init_acpi_sar(struct mt792x_dev *dev); int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default); u8 mt792x_acpi_get_flags(struct mt792x_phy *phy); -u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2); +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2); #else static inline int mt792x_init_acpi_sar(struct mt792x_dev *dev) { @@ -526,9 +526,9 @@ static inline u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) return 0; } -static inline u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +static inline u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) { - return 0xf; + return MT792X_ACPI_MTCL_INVALID; } #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c index 9317f8ff2070..d1aebadd50aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c @@ -4,6 +4,28 @@ #include #include "mt792x.h" +static const char * const cc_list_all[] = { + "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR", + "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME", + "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR", + "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY", +}; + +static const char * const cc_list_eu[] = { + "AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK", + "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE", + "IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL", + "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE", + "CH", +}; + +static const char * const cc_list_be[] = { + "AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU", + "RW", "VN", "KR", "UA", "", "", "", "", + "EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK", + "US", +}; + static int mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len) { @@ -66,13 +88,22 @@ free: } /* MTCL : Country List Table for 6G band */ +/* MTCL : Country List Table for 6G band and 11BE */ static int mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version) { - int ret; + int len, ret; - *version = ((ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, NULL)) < 0) - ? 1 : 2; + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len); + if (ret) + return ret; + + if (len == sizeof(struct mt792x_asar_cl)) + *version = ((struct mt792x_asar_cl *)*table)->version; + else if (len == sizeof(struct mt792x_asar_cl_v3)) + *version = ((struct mt792x_asar_cl_v3 *)*table)->version; + else + return -EINVAL; return ret; } @@ -351,10 +382,24 @@ u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) } EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags); -static u8 +static u32 +mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl) +{ + u32 config = 0; + u8 mode_be = 0; + + mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be; + + if (cl->version > 2 && cl->clbe[row] & BIT(column)) + config |= (mode_be & 0x3) << 4; + + return config; +} + +static u32 mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl) { - u8 config = 0; + u32 config = 0; u8 mode_6g, mode_5g9; mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g; @@ -368,30 +413,44 @@ mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl) return config; } -u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +static u32 +mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2) { - static const char * const cc_list_all[] = { - "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR", - "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME", - "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR", - "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY", - }; - static const char * const cc_list_eu[] = { - "AT", "BE", "BG", "CY", "CZ", "HR", "DK", "EE", - "FI", "FR", "DE", "GR", "HU", "IS", "IE", "IT", - "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL", - "PT", "RO", "SK", "SI", "ES", "SE", "CH", - }; struct mt792x_acpi_sar *sar = phy->acpisar; - struct mt792x_asar_cl *cl; + struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3; int col, row, i; - if (!sar) - return 0xf; + if (sar->ver != 3) + goto out; - cl = sar->countrylist; if (!cl) - return 0xc; + return MT792X_ACPI_MTCL_INVALID; + + for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) { + col = 7 - i % 8; + row = i / 8; + if (!memcmp(cc_list_be[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(row, col, cl); + } + for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) { + if (!memcmp(cc_list_eu[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(3, 7, cl); + } + +out: + /* Depends on driver */ + return 0x20; +} + +static u32 +mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + struct mt792x_asar_cl *cl = sar->countrylist; + int col, row, i; + + if (!cl) + return MT792X_ACPI_MTCL_INVALID; for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) { col = 7 - i % 8; @@ -406,4 +465,22 @@ u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) return mt792x_acpi_get_mtcl_map(0, 7, cl); } + +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + u32 config = 0; + + if (!sar) + return MT792X_ACPI_MTCL_INVALID; + + config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2); + + if (config == MT792X_ACPI_MTCL_INVALID) + return MT792X_ACPI_MTCL_INVALID; + + config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2); + + return config; +} EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h index 2298983b6342..e45dcd7fbdb1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h @@ -15,6 +15,8 @@ #define MT792x_ACPI_MTGS "MTGS" #define MT792x_ACPI_MTFG "MTFG" +#define MT792X_ACPI_MTCL_INVALID 0xffffffff + struct mt792x_asar_dyn_limit { u8 idx; u8 frp[5]; @@ -72,6 +74,17 @@ struct mt792x_asar_geo_v2 { DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit_v2, tbl); } __packed; +struct mt792x_asar_cl_v3 { + u8 names[4]; + u8 version; + u8 mode_6g; + u8 cl6g[6]; + u8 mode_5g9; + u8 cl5g9[6]; + u8 mode_be; + u8 clbe[6]; +} __packed; + struct mt792x_asar_cl { u8 names[4]; u8 version; @@ -100,7 +113,10 @@ struct mt792x_acpi_sar { struct mt792x_asar_geo *geo; struct mt792x_asar_geo_v2 *geo_v2; }; - struct mt792x_asar_cl *countrylist; + union { + struct mt792x_asar_cl *countrylist; + struct mt792x_asar_cl_v3 *countrylist_v3; + }; struct mt792x_asar_fg *fg; }; From ef7e01fdfe4e0ebf1f98df794ecdbf66377fa5b7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 381/465] wifi: mt76: mt7925: add MTCL support to enhance the regulatory compliance JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 51ac8dbb706a6a88dbe289205aeb22e4013e5885 Author: Ming Yen Hsieh Date: Tue Mar 4 19:36:49 2025 +0800 wifi: mt76: mt7925: add MTCL support to enhance the regulatory compliance Apply the MTCL configuration to improving channel availability and regulatory compliance if MTCL table is supported. Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250304113649.867387-6-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7925/init.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 8732b05355db..63cb08f4d87c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -64,8 +64,15 @@ void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2) struct mt7925_clc_rule_v2 *rule; struct mt7925_clc *clc; bool old = dev->has_eht, new = true; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2); u8 *pos; + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID && + (((mtcl_conf >> 4) & 0x3) == 0)) { + new = false; + goto out; + } + if (!phy->clc[MT792x_CLC_BE_CTRL]) goto out; @@ -101,11 +108,21 @@ mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) { #define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \ (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) +#define MT7925_UNII_59G_IS_VALID 0x1 +#define MT7925_UNII_6G_IS_VALID 0x1e struct ieee80211_supported_band *sband; struct mt76_dev *mdev = &dev->mt76; struct ieee80211_channel *ch; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2); int i; + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) { + if ((mtcl_conf & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID; + if (((mtcl_conf >> 2) & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID; + } + sband = wiphy->bands[NL80211_BAND_5GHZ]; if (!sband) return; From 81c9bd4aaea8e20f4a66ff9fba2c3f2c1073514a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 382/465] wifi: mt76: add mt76_get_power_bound helper function JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 764bf16699ac9ed3fa1aa02db99a3a40a0078322 Author: Razvan Grigore Date: Tue Feb 11 08:12:43 2025 +0000 wifi: mt76: add mt76_get_power_bound helper function This will replace mt7915_get_power_bound function from b/mt7915/mcu.h, since we will need it also for mt7921 and mt7925 Signed-off-by: Razvan Grigore Link: https://patch.msgid.link/20250211081247.5892-2-razvan.grigore@vampirebyte.ro Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mac80211.c | 11 +++++++++++ drivers/net/wireless/mediatek/mt76/mt76.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 508b472408c2..2c98cc42cd7e 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1697,6 +1697,17 @@ void mt76_wcid_add_poll(struct mt76_dev *dev, struct mt76_wcid *wcid) } EXPORT_SYMBOL_GPL(mt76_wcid_add_poll); +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower) +{ + int n_chains = hweight16(phy->chainmask); + + txpower = mt76_get_sar_power(phy, phy->chandef.chan, txpower * 2); + txpower -= mt76_tx_power_nss_delta(n_chains); + + return txpower; +} +EXPORT_SYMBOL_GPL(mt76_get_power_bound); + int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, int *dbm) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 05651efb549e..7596191252dc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1483,6 +1483,8 @@ void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx); +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower); + int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, int *dbm); int mt76_init_sar_power(struct ieee80211_hw *hw, From 1b48aee980095d33b828b26b71e28746686363b6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 383/465] wifi: mt76: mt7915: cleanup mt7915_get_power_bound JIRA: https://issues.redhat.com/browse/RHEL-89168 commit b4446a000bee8a73d027cd0e882015030d84166f Author: Razvan Grigore Date: Tue Feb 11 08:12:46 2025 +0000 wifi: mt76: mt7915: cleanup mt7915_get_power_bound Refactor for making use of mt76_get_power_bound instead of the specific mt7915_get_power_bound, since we need this for other chipsets as well when calculating txpower Signed-off-by: Razvan Grigore Link: https://patch.msgid.link/20250211081247.5892-5-razvan.grigore@vampirebyte.ro Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c | 8 ++++---- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 12 ------------ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 4fec7d000a63..192e8eff970b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -1085,13 +1085,13 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, return -EINVAL; if (pwr160) - pwr160 = mt7915_get_power_bound(phy, pwr160); + pwr160 = mt76_get_power_bound(mphy, pwr160); if (pwr80) - pwr80 = mt7915_get_power_bound(phy, pwr80); + pwr80 = mt76_get_power_bound(mphy, pwr80); if (pwr40) - pwr40 = mt7915_get_power_bound(phy, pwr40); + pwr40 = mt76_get_power_bound(mphy, pwr40); if (pwr20) - pwr20 = mt7915_get_power_bound(phy, pwr20); + pwr20 = mt76_get_power_bound(mphy, pwr20); if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0) return -EINVAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 9d790f234e82..3643c72bb68d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3323,7 +3323,7 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, if (ret) return ret; - txpower = mt7915_get_power_bound(phy, txpower); + txpower = mt76_get_power_bound(mphy, txpower); if (txpower > mphy->txpower_cur || txpower < 0) return -EINVAL; @@ -3373,7 +3373,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) int i, idx; int tx_power; - tx_power = mt7915_get_power_bound(phy, hw->conf.power_level); + tx_power = mt76_get_power_bound(mphy, hw->conf.power_level); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits_array, tx_power); mphy->txpower_cur = tx_power; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 49476a4182fd..092ed504a8f2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -515,16 +515,4 @@ enum { sizeof(struct bss_info_bmc_rate) +\ sizeof(struct bss_info_ext_bss)) -static inline s8 -mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower) -{ - struct mt76_phy *mphy = phy->mt76; - int n_chains = hweight16(mphy->chainmask); - - txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); - txpower -= mt76_tx_power_nss_delta(n_chains); - - return txpower; -} - #endif From 8e06cd6ee3b3a38143c4e4b3790d74ab2eac4a00 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 384/465] wifi: mt76: mt7996: cleanup mt7996_get_power_bound JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6fc82f65870daa4bb351abb91411e139c4cdeb6b Author: Razvan Grigore Date: Tue Feb 11 08:12:47 2025 +0000 wifi: mt76: mt7996: cleanup mt7996_get_power_bound Refactor for making use of mt76_get_power_bound instead of the specific mt7996_get_power_bound, since we need this for other chipsets as well when calculating txpower Signed-off-by: Razvan Grigore Link: https://patch.msgid.link/20250211081247.5892-6-razvan.grigore@vampirebyte.ro Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index e4569d032221..76f489e2f602 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4579,7 +4579,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy) struct sk_buff *skb; int i, tx_power; - tx_power = mt7996_get_power_bound(phy, phy->txpower); + tx_power = mt76_get_power_bound(mphy, phy->txpower); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, tx_power); mphy->txpower_cur = tx_power; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index 43468bcaffc6..bd1ba00e1bc6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -809,18 +809,6 @@ enum { #define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \ MT7996_BEACON_UPDATE_SIZE) -static inline s8 -mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower) -{ - struct mt76_phy *mphy = phy->mt76; - int n_chains = hweight16(mphy->chainmask); - - txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); - txpower -= mt76_tx_power_nss_delta(n_chains); - - return txpower; -} - enum { UNI_BAND_CONFIG_RADIO_ENABLE, UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, From 368fe69544f303748a51d06c8bf81ee1c1fd6b0c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 385/465] wifi: mt76: mt7996: revise TXS size JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 593c829b4326f7b3b15a69e97c9044ecbad3c319 Author: Benjamin Lin Date: Tue Mar 11 11:36:38 2025 +0100 wifi: mt76: mt7996: revise TXS size Size of MPDU/PPDU TXS is 12 DWs. In mt7996/mt7992, last 4 DWs are reserved, so TXS size was mistakenly considered to be 8 DWs. However, in mt7990, 9th DW of TXS starts to be used. Signed-off-by: Benjamin Lin Link: https://patch.msgid.link/20250311103646.43346-1-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h | 3 +++ drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h index db0c29e65185..487ad716f872 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h @@ -314,6 +314,9 @@ enum tx_frag_idx { #define MT_TXFREE_INFO_COUNT GENMASK(27, 24) #define MT_TXFREE_INFO_STAT GENMASK(29, 28) +#define MT_TXS_HDR_SIZE 4 /* Unit: DW */ +#define MT_TXS_SIZE 12 /* Unit: DW */ + #define MT_TXS0_BW GENMASK(31, 29) #define MT_TXS0_TID GENMASK(28, 26) #define MT_TXS0_AMPDU BIT(25) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index bc8cba4dca47..e3e6626b5179 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1399,7 +1399,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len) mt7996_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: - for (rxd += 4; rxd + 8 <= end; rxd += 8) + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) mt7996_mac_add_txs(dev, rxd); return false; case PKT_TYPE_RX_FW_MONITOR: @@ -1442,7 +1442,7 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, mt7996_mcu_rx_event(dev, skb); break; case PKT_TYPE_TXS: - for (rxd += 4; rxd + 8 <= end; rxd += 8) + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) mt7996_mac_add_txs(dev, rxd); dev_kfree_skb(skb); break; From d85a58afa819c0b51db8fb2082fcd354d7c94aa2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 386/465] wifi: mt76: mt7996: fix SER reset trigger on WED reset JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 8d38abdf6c182225c5c0a81451fa51b7b36a635d Author: Rex Lu Date: Tue Mar 11 11:36:39 2025 +0100 wifi: mt76: mt7996: fix SER reset trigger on WED reset The firmware needs a specific trigger when WED is being reset due to an ethernet reset condition. This helps prevent further L1 SER failure. Signed-off-by: Rex Lu Link: https://patch.msgid.link/20250311103646.43346-2-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 3 ++- drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index bd1ba00e1bc6..5fdc47dad28c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -896,7 +896,8 @@ enum { UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE, UNI_CMD_SER_SET_RECOVER_L3_BF, UNI_CMD_SER_SET_RECOVER_L4_MDP, - UNI_CMD_SER_SET_RECOVER_FULL, + UNI_CMD_SER_SET_RECOVER_FROM_ETH, + UNI_CMD_SER_SET_RECOVER_FULL = 8, UNI_CMD_SER_SET_SYSTEM_ASSERT, /* action */ UNI_CMD_SER_ENABLE = 1, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 7a8ee6c75cf2..9d37f8238746 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -281,7 +281,7 @@ static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed) if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) return -EBUSY; - ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_L1, + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_FROM_ETH, mphy->band_idx); if (ret) goto out; From bad2b888b35fc112d19ac2068c76bcd95e5e50d4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:03 +0200 Subject: [PATCH 387/465] wifi: mt76: mt7996: remove unnecessary key->cipher check for BIP frames JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0337355cc217215ba7c4e5a858c8f73c1a78bab4 Author: Michael-CY Lee Date: Tue Mar 11 11:36:40 2025 +0100 wifi: mt76: mt7996: remove unnecessary key->cipher check for BIP frames The cipher type check is redundant, and there is no need to dereference the key struct here. Signed-off-by: Michael-CY Lee Link: https://patch.msgid.link/20250311103646.43346-3-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e3e6626b5179..ea202213f8b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -773,8 +773,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, info->flags & IEEE80211_TX_CTL_USE_MINRATE) val |= MT_TXD1_FIXED_RATE; - if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && - key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb)) { val |= MT_TXD1_BIP; txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); } From a2d63a25b1b5e10f2d2025dbbe96292d1581960e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 388/465] wifi: mt76: scan: set vif offchannel link for scanning/roc JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3ba20af886d1f604dceeb4d4c8ff872e2c4e885e Author: Felix Fietkau Date: Tue Mar 11 11:36:41 2025 +0100 wifi: mt76: scan: set vif offchannel link for scanning/roc The driver needs to know what vif link to use Link: https://patch.msgid.link/20250311103646.43346-4-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/channel.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt76.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 6a35c6ebd823..e7b839e74290 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -293,6 +293,7 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy, kfree(mlink); return ERR_PTR(ret); } + rcu_assign_pointer(mvif->offchannel_link, mlink); return mlink; } @@ -301,10 +302,12 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt76_dev *dev = phy->dev; + struct mt76_vif_data *mvif = mlink->mvif; if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel) return; + rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); kfree(mlink); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 7596191252dc..c16a6743ad64 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -351,6 +351,7 @@ struct mt76_wcid { u8 hw_key_idx; u8 hw_key_idx2; + u8 offchannel:1; u8 sta:1; u8 sta_disabled:1; u8 amsdu:1; @@ -787,6 +788,7 @@ struct mt76_vif_link { struct mt76_vif_data { struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct mt76_vif_link __rcu *offchannel_link; struct mt76_phy *roc_phy; u16 valid_links; From c9f5bc9eaf239cd9619444c090789daa79475015 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 389/465] wifi: mt76: mt7996: use the correct vif link for scanning/roc JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 13b4c81083cc4b59fb639a511c0a9a7c38efde7e Author: Felix Fietkau Date: Tue Mar 11 11:36:42 2025 +0100 wifi: mt76: mt7996: use the correct vif link for scanning/roc Use the newly added offchannel_link pointer in vif data Link: https://patch.msgid.link/20250311103646.43346-5-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 40 +++++++++++++------ .../net/wireless/mediatek/mt76/mt7996/main.c | 1 + 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index ea202213f8b0..44864d1c3a88 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -831,7 +831,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; - struct mt76_vif_link *mvif; + struct mt76_vif_link *mlink = NULL; + struct mt7996_vif *mvif; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -839,11 +840,18 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - mvif = vif ? (struct mt76_vif_link *)vif->drv_priv : NULL; - if (mvif) { - omac_idx = mvif->omac_idx; - wmm_idx = mvif->wmm_idx; - band_idx = mvif->band_idx; + if (vif) { + mvif = (struct mt7996_vif *)vif->drv_priv; + if (wcid->offchannel) + mlink = rcu_dereference(mvif->mt76.offchannel_link); + if (!mlink) + mlink = &mvif->deflink.mt76; + } + + if (mlink) { + omac_idx = mlink->omac_idx; + wmm_idx = mlink->wmm_idx; + band_idx = mlink->band_idx; } if (inband_disc) { @@ -909,13 +917,13 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, is_multicast_ether_addr(hdr->addr1); u8 idx = MT7996_BASIC_RATES_TBL; - if (mvif) { - if (mcast && mvif->mcast_rates_idx) - idx = mvif->mcast_rates_idx; - else if (beacon && mvif->beacon_rates_idx) - idx = mvif->beacon_rates_idx; + if (mlink) { + if (mcast && mlink->mcast_rates_idx) + idx = mlink->mcast_rates_idx; + else if (beacon && mlink->beacon_rates_idx) + idx = mlink->beacon_rates_idx; else - idx = mvif->basic_rates_idx; + idx = mlink->basic_rates_idx; } val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW; @@ -983,8 +991,14 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (vif) { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_vif_link *mlink = NULL; - txp->fw.bss_idx = mvif->deflink.mt76.idx; + if (wcid->offchannel) + mlink = rcu_dereference(mvif->mt76.offchannel_link); + if (!mlink) + mlink = &mvif->deflink.mt76; + + txp->fw.bss_idx = mlink->idx; } txp->fw.token = cpu_to_le16(id); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 69dd565d8319..886b6ef3462b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -249,6 +249,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mlink->band_idx = band_idx; mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3; mlink->wcid = &link->sta.wcid; + mlink->wcid->offchannel = mlink->offchannel; ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true); if (ret) From e7627ac0cd0a180fb7e12343166d6bf726e3ae40 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 390/465] wifi: mt76: only mark tx-status-failed frames as ACKed on mt76x0/2 JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0c5a89ceddc1728a40cb3313948401dd70e3c649 Author: Felix Fietkau Date: Tue Mar 11 11:36:43 2025 +0100 wifi: mt76: only mark tx-status-failed frames as ACKed on mt76x0/2 The interrupt status polling is unreliable, which can cause status events to get lost. On all newer chips, txs-timeout is an indication that the packet was either never sent, or never acked. Fixes issues with inactivity polling. Link: https://patch.msgid.link/20250311103646.43346-6-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/mt76x0/pci.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt76x0/usb.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt76x2/pci.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 3 ++- drivers/net/wireless/mediatek/mt76/tx.c | 3 ++- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index c16a6743ad64..b461273c3306 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -492,6 +492,7 @@ struct mt76_hw_cap { #define MT_DRV_RX_DMA_HDR BIT(3) #define MT_DRV_HW_MGMT_TXQ BIT(4) #define MT_DRV_AMSDU_OFFLOAD BIT(5) +#define MT_DRV_IGNORE_TXS_FAILED BIT(6) struct mt76_driver_ops { u32 drv_flags; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index 1eb955f3ca13..911e162a4598 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -156,7 +156,8 @@ mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id) static const struct mt76_driver_ops drv_ops = { .txwi_size = sizeof(struct mt76x02_txwi), .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | - MT_DRV_SW_RX_AIRTIME, + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x0_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index b031c500b741..90e5666c0857 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -214,7 +214,8 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { static const struct mt76_driver_ops drv_ops = { - .drv_flags = MT_DRV_SW_RX_AIRTIME, + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x0_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c index 67c9d1caa0bd..55f076231bdc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c @@ -22,7 +22,8 @@ mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id) static const struct mt76_driver_ops drv_ops = { .txwi_size = sizeof(struct mt76x02_txwi), .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | - MT_DRV_SW_RX_AIRTIME, + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x2e_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index e832ad53e239..3747f9ed31e6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -29,7 +29,8 @@ static int mt76x2u_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const struct mt76_driver_ops drv_ops = { - .drv_flags = MT_DRV_SW_RX_AIRTIME, + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x2u_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index af0c50c983ec..513916469ca2 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -100,7 +100,8 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags, return; /* Tx status can be unreliable. if it fails, mark the frame as ACKed */ - if (flags & MT_TX_CB_TXS_FAILED) { + if (flags & MT_TX_CB_TXS_FAILED && + (dev->drv->drv_flags & MT_DRV_IGNORE_TXS_FAILED)) { info->status.rates[0].count = 0; info->status.rates[0].idx = -1; info->flags |= IEEE80211_TX_STAT_ACK; From a691ab2e9d38ea1a328b68c1a8c092da1f3b8d1d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 391/465] wifi: mt76: mt7996: implement driver specific get_txpower function JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 86db2c5d4ed390b97a5b455a97e2cd9c4f3eff2b Author: Felix Fietkau Date: Tue Mar 11 11:36:44 2025 +0100 wifi: mt76: mt7996: implement driver specific get_txpower function Fixes reporting tx power for vifs that don't have a channel context assigned. Report the tx power of a phy that is covered by the vif's radio mask. Link: https://patch.msgid.link/20250311103646.43346-7-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 886b6ef3462b..b01cc7ef4799 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -602,6 +602,33 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } +static int +mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct wireless_dev *wdev; + int n_chains, delta, i; + + if (!phy) { + wdev = ieee80211_vif_to_wdev(vif); + for (i = 0; i < hw->wiphy->n_radio; i++) + if (wdev->radio_mask & BIT(i)) + phy = dev->radio_phy[i]; + + if (!phy) + return -EINVAL; + } + + n_chains = hweight16(phy->mt76->chainmask); + delta = mt76_tx_power_nss_delta(n_chains); + *dbm = DIV_ROUND_UP(phy->mt76->txpower_cur + delta, 2); + + return 0; +} + static u8 mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf, bool beacon, bool mcast) @@ -1651,7 +1678,7 @@ const struct ieee80211_ops mt7996_ops = { .remain_on_channel = mt76_remain_on_channel, .cancel_remain_on_channel = mt76_cancel_remain_on_channel, .release_buffered_frames = mt76_release_buffered_frames, - .get_txpower = mt76_get_txpower, + .get_txpower = mt7996_get_txpower, .channel_switch_beacon = mt7996_channel_switch_beacon, .get_stats = mt7996_get_stats, .get_et_sset_count = mt7996_get_et_sset_count, From 2c889bc208730807cb2d97b8a9ed97260a9d581d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 392/465] wifi: mt76: scan: fix setting tx_info fields JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5b5f1ca9ce73ab6c35e5cd3348f8432ba190d7f4 Author: Felix Fietkau Date: Tue Mar 11 11:36:45 2025 +0100 wifi: mt76: scan: fix setting tx_info fields ieee80211_tx_prepare_skb initializes the skb cb, so fields need to be set afterwards. Link: https://patch.msgid.link/20250311103646.43346-8-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/scan.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 1c4f9deaaada..9b20ccbeb8cf 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -52,11 +52,6 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) ether_addr_copy(hdr->addr3, req->bssid); } - info = IEEE80211_SKB_CB(skb); - if (req->no_cck) - info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; - if (req->ie_len) skb_put_data(skb, req->ie, req->ie_len); @@ -64,10 +59,20 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) skb_set_queue_mapping(skb, IEEE80211_AC_VO); rcu_read_lock(); - if (ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) - mt76_tx(phy, NULL, mvif->wcid, skb); - else + + if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) { ieee80211_free_txskb(phy->hw, skb); + goto out; + } + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; + + mt76_tx(phy, NULL, mvif->wcid, skb); + +out: rcu_read_unlock(); } From c646473eb1c2b20abb07a3a365bc7b1e838995a0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 393/465] wifi: mt76: mt7996: Add change_vif_links stub JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 142f82d45ed4fbe2d08cf6f19a6fa90970de044b Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:00 2025 +0100 wifi: mt76: mt7996: Add change_vif_links stub change_vif_links callback is required by mac80211. This is a preliminary patch to introduce MLO support for MT7996 driver. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-1-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index b01cc7ef4799..c0e7ab9bcae5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1650,6 +1650,14 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, #endif +static int +mt7996_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]) +{ + return 0; +} + const struct ieee80211_ops mt7996_ops = { .add_chanctx = mt76_add_chanctx, .remove_chanctx = mt76_remove_chanctx, @@ -1705,4 +1713,5 @@ const struct ieee80211_ops mt7996_ops = { .net_fill_forward_path = mt7996_net_fill_forward_path, .net_setup_tc = mt76_wed_net_setup_tc, #endif + .change_vif_links = mt7996_change_vif_links, }; From 3de1fa792c4d4f58920ffa729eb774c81e324fa9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 394/465] wifi: mt76: mt7996: Introduce mt7996_sta_link container JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f32915eb6dd4b66c0b434afc7c30c2ff55a4e6e8 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:01 2025 +0100 wifi: mt76: mt7996: Introduce mt7996_sta_link container Similar to mt7996_vif_link, add mt7996_sta_link structure as driver representation for sta links. This is a preliminary patch to introduce MLO support for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Shayne Chen Signed-off-by: Shayne Chen Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-2-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/mediatek/mt76/mt7996/debugfs.c | 12 +- .../net/wireless/mediatek/mt76/mt7996/mac.c | 117 +++++++++------ .../net/wireless/mediatek/mt76/mt7996/main.c | 136 ++++++++++-------- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 33 +++-- .../wireless/mediatek/mt76/mt7996/mt7996.h | 10 +- 5 files changed, 181 insertions(+), 127 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 7b2bb72b407d..d453c2fc97e4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -617,13 +617,14 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_sta_link *msta_link = &msta->deflink; struct seq_file *s = data; u8 ac; for (ac = 0; ac < 4; ac++) { u32 qlen, ctrl, val; - u32 idx = msta->wcid.idx >> 5; - u8 offs = msta->wcid.idx & GENMASK(4, 0); + u32 idx = msta_link->wcid.idx >> 5; + u8 offs = msta_link->wcid.idx & GENMASK(4, 0); ctrl = BIT(31) | BIT(11) | (ac << 24); val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); @@ -631,11 +632,11 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) if (val & BIT(offs)) continue; - mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx); qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, GENMASK(11, 0)); seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", - sta->addr, msta->wcid.idx, + sta->addr, msta_link->wcid.idx, msta->vif->deflink.mt76.wmm_idx, ac, qlen); } } @@ -930,6 +931,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file, struct ieee80211_sta *sta = file->private_data; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_sta_link *msta_link = &msta->deflink; struct ra_rate phy = {}; char buf[100]; int ret; @@ -964,7 +966,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file, goto out; } - phy.wlan_idx = cpu_to_le16(msta->wcid.idx); + phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx); phy.gi = cpu_to_le16(gi); phy.ltf = cpu_to_le16(ltf); phy.ldpc = phy.ldpc ? 7 : 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 44864d1c3a88..c95f5f776e78 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -55,7 +55,8 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = { static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, u16 idx, bool unicast) { - struct mt7996_sta *sta; + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; struct mt76_wcid *wcid; if (idx >= ARRAY_SIZE(dev->mt76.wcid)) @@ -68,11 +69,13 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, if (!wcid->sta) return NULL; - sta = container_of(wcid, struct mt7996_sta, wcid); - if (!sta->vif) + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + msta = msta_link->sta; + + if (!msta->vif) return NULL; - return &sta->vif->deflink.sta.wcid; + return &msta->vif->deflink.sta.deflink.wcid; } bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) @@ -100,6 +103,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) [IEEE80211_AC_VI] = 4, [IEEE80211_AC_VO] = 6 }; + struct mt7996_sta_link *msta_link; struct ieee80211_sta *sta; struct mt7996_sta *msta; u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; @@ -123,25 +127,27 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) spin_unlock_bh(&dev->mt76.sta_poll_lock); break; } - msta = list_first_entry(&sta_poll_list, - struct mt7996_sta, wcid.poll_list); - list_del_init(&msta->wcid.poll_list); + msta_link = list_first_entry(&sta_poll_list, + struct mt7996_sta_link, + wcid.poll_list); + msta = msta_link->sta; + list_del_init(&msta_link->wcid.poll_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); - idx = msta->wcid.idx; + idx = msta_link->wcid.idx; /* refresh peer's airtime reporting */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u32 tx_last = msta->airtime_ac[i]; - u32 rx_last = msta->airtime_ac[i + 4]; + u32 tx_last = msta_link->airtime_ac[i]; + u32 rx_last = msta_link->airtime_ac[i + 4]; - msta->airtime_ac[i] = mt76_rr(dev, addr); - msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + msta_link->airtime_ac[i] = mt76_rr(dev, addr); + msta_link->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); - tx_time[i] = msta->airtime_ac[i] - tx_last; - rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + tx_time[i] = msta_link->airtime_ac[i] - tx_last; + rx_time[i] = msta_link->airtime_ac[i + 4] - rx_last; if ((tx_last | rx_last) & BIT(30)) clear = true; @@ -152,10 +158,11 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) if (clear) { mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + memset(msta_link->airtime_ac, 0, + sizeof(msta_link->airtime_ac)); } - if (!msta->wcid.sta) + if (!msta_link->wcid.sta) continue; sta = container_of((void *)msta, struct ieee80211_sta, @@ -181,10 +188,11 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rssi[2] = to_rssi(GENMASK(23, 16), val); rssi[3] = to_rssi(GENMASK(31, 14), val); - msta->ack_signal = + msta_link->ack_signal = mt76_rx_signal(msta->vif->deflink.phy->mt76->antenna_mask, rssi); - ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); + ewma_avg_signal_add(&msta_link->avg_ack_signal, + -msta_link->ack_signal); } rcu_read_unlock(); @@ -194,9 +202,10 @@ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, struct ieee80211_vif *vif, bool enable) { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &mvif->deflink.sta.deflink; u32 addr; - addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->deflink.sta.wcid.idx, 5); + addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 5); if (enable) mt76_set(dev, addr, BIT(5)); else @@ -477,8 +486,12 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, status->wcid = mt7996_rx_get_wcid(dev, idx, unicast); if (status->wcid) { - msta = container_of(status->wcid, struct mt7996_sta, wcid); - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + struct mt7996_sta_link *msta_link; + + msta_link = container_of(status->wcid, struct mt7996_sta_link, + wcid); + msta = msta_link->sta; + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); } status->freq = mphy->chandef.chan->center_freq; @@ -1040,9 +1053,10 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) static void mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb) { - struct mt7996_sta *msta; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; u16 fc, tid; if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) @@ -1071,7 +1085,9 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb) return; msta = (struct mt7996_sta *)sta->drv_priv; - if (!test_and_set_bit(tid, &msta->wcid.ampdu_state)) + msta_link = &msta->deflink; + + if (!test_and_set_bit(tid, &msta_link->wcid.ampdu_state)) ieee80211_start_tx_ba_session(sta, tid, 0); } @@ -1149,7 +1165,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) */ info = le32_to_cpu(*cur_info); if (info & MT_TXFREE_INFO_PAIR) { - struct mt7996_sta *msta; + struct mt7996_sta_link *msta_link; u16 idx; idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); @@ -1158,8 +1174,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) if (!sta) continue; - msta = container_of(wcid, struct mt7996_sta, wcid); - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + msta_link = container_of(wcid, struct mt7996_sta_link, + wcid); + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); continue; } else if (info & MT_TXFREE_INFO_HEADER) { u32 tx_retries = 0, tx_failed = 0; @@ -1357,7 +1374,7 @@ out: static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) { - struct mt7996_sta *msta = NULL; + struct mt7996_sta_link *msta_link; struct mt76_wcid *wcid; __le32 *txs_data = data; u16 wcidx; @@ -1378,14 +1395,13 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) if (!wcid) goto out; - msta = container_of(wcid, struct mt7996_sta, wcid); - mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data); if (!wcid->sta) goto out; - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); out: rcu_read_unlock(); @@ -2252,6 +2268,7 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy) void mt7996_mac_sta_rc_work(struct work_struct *work) { struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + struct mt7996_sta_link *msta_link; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct mt7996_sta *msta; @@ -2262,12 +2279,15 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) list_splice_init(&dev->sta_rc_list, &list); while (!list_empty(&list)) { - msta = list_first_entry(&list, struct mt7996_sta, rc_list); - list_del_init(&msta->rc_list); - changed = msta->changed; - msta->changed = 0; + msta_link = list_first_entry(&list, struct mt7996_sta_link, + rc_list); + list_del_init(&msta_link->rc_list); + + changed = msta_link->changed; + msta_link->changed = 0; spin_unlock_bh(&dev->mt76.sta_poll_lock); + msta = msta_link->sta; sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); @@ -2551,7 +2571,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt) } static bool -mt7996_mac_twt_param_equal(struct mt7996_sta *msta, +mt7996_mac_twt_param_equal(struct mt7996_sta_link *msta_link, struct ieee80211_twt_params *twt_agrt) { u16 type = le16_to_cpu(twt_agrt->req_type); @@ -2562,10 +2582,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta, for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) { struct mt7996_twt_flow *f; - if (!(msta->twt.flowid_mask & BIT(i))) + if (!(msta_link->twt.flowid_mask & BIT(i))) continue; - f = &msta->twt.flow[i]; + f = &msta_link->twt.flow[i]; if (f->duration == twt_agrt->min_twt_dur && f->mantissa == twt_agrt->mantissa && f->exp == exp && @@ -2585,6 +2605,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + struct mt7996_sta_link *msta_link = &msta->deflink; u16 req_type = le16_to_cpu(twt_agrt->req_type); enum ieee80211_twt_setup_cmd sta_setup_cmd; struct mt7996_dev *dev = mt7996_hw_dev(hw); @@ -2599,7 +2620,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT) goto unlock; - if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow)) + if (hweight8(msta_link->twt.flowid_mask) == + ARRAY_SIZE(msta_link->twt.flow)) goto unlock; if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) { @@ -2608,10 +2630,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, goto unlock; } - if (mt7996_mac_twt_param_equal(msta, twt_agrt)) + if (mt7996_mac_twt_param_equal(msta_link, twt_agrt)) goto unlock; - flowid = ffs(~msta->twt.flowid_mask) - 1; + flowid = ffs(~msta_link->twt.flowid_mask) - 1; twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID); twt_agrt->req_type |= le16_encode_bits(flowid, IEEE80211_TWT_REQTYPE_FLOWID); @@ -2620,10 +2642,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); - flow = &msta->twt.flow[flowid]; + flow = &msta_link->twt.flow[flowid]; memset(flow, 0, sizeof(*flow)); INIT_LIST_HEAD(&flow->list); - flow->wcid = msta->wcid.idx; + flow->wcid = msta_link->wcid.idx; flow->table_id = table_id; flow->id = flowid; flow->duration = twt_agrt->min_twt_dur; @@ -2655,7 +2677,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, setup_cmd = TWT_SETUP_CMD_ACCEPT; dev->twt.table_mask |= BIT(table_id); - msta->twt.flowid_mask |= BIT(flowid); + msta_link->twt.flowid_mask |= BIT(flowid); dev->twt.n_agrt++; unlock: @@ -2671,23 +2693,24 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, struct mt7996_sta *msta, u8 flowid) { + struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_twt_flow *flow; lockdep_assert_held(&dev->mt76.mutex); - if (flowid >= ARRAY_SIZE(msta->twt.flow)) + if (flowid >= ARRAY_SIZE(msta_link->twt.flow)) return; - if (!(msta->twt.flowid_mask & BIT(flowid))) + if (!(msta_link->twt.flowid_mask & BIT(flowid))) return; - flow = &msta->twt.flow[flowid]; + flow = &msta_link->twt.flow[flowid]; if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_DELETE)) return; list_del_init(&flow->list); - msta->twt.flowid_mask &= ~BIT(flowid); + msta_link->twt.flowid_mask &= ~BIT(flowid); dev->twt.table_mask &= ~BIT(flow->table_id); dev->twt.n_agrt--; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index c0e7ab9bcae5..0cd011619fd3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -163,7 +163,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mlink->sta; - struct mt76_wcid *wcid = &msta->wcid; + struct mt76_wcid *wcid = &msta->deflink.wcid; u8 *wcid_keyidx = &wcid->hw_key_idx; struct mt7996_phy *phy; int idx = key->keyidx; @@ -208,8 +208,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return mt7996_mcu_bcn_prot_enable(dev, vif, key); return mt7996_mcu_add_key(&dev->mt76, vif, key, - MCU_WMWA_UNI_CMD(STA_REC_UPDATE), - &msta->wcid, cmd); + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), wcid, cmd); } static void @@ -230,6 +229,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_sta_link *msta_link = &link->sta.deflink; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; u8 band_idx = phy->mt76->band_idx; @@ -248,7 +248,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mlink->omac_idx = idx; mlink->band_idx = band_idx; mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3; - mlink->wcid = &link->sta.wcid; + mlink->wcid = &msta_link->wcid; mlink->wcid->offchannel = mlink->offchannel; ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true); @@ -260,10 +260,11 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, idx = MT7996_WTBL_RESERVED - mlink->idx; - INIT_LIST_HEAD(&link->sta.rc_list); - link->sta.wcid.idx = idx; - link->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; - mt76_wcid_init(&link->sta.wcid, band_idx); + INIT_LIST_HEAD(&msta_link->rc_list); + msta_link->sta = &link->sta; + msta_link->wcid.idx = idx; + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&msta_link->wcid, band_idx); mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -290,7 +291,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, */ if (vif->type != NL80211_IFTYPE_STATION) mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_PORT_SECURE, true); - rcu_assign_pointer(dev->mt76.wcid[idx], &link->sta.wcid); + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, link); @@ -304,11 +305,10 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; - struct mt7996_sta *msta; - int idx; + struct mt7996_sta *msta = &link->sta; + struct mt7996_sta_link *msta_link = &msta->deflink; + int idx = msta_link->wcid.idx; - msta = &link->sta; - idx = msta->wcid.idx; mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_DISCONNECT, false); mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, false); @@ -320,11 +320,11 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); spin_lock_bh(&dev->mt76.sta_poll_lock); - if (!list_empty(&msta->wcid.poll_list)) - list_del_init(&msta->wcid.poll_list); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); - mt76_wcid_cleanup(&dev->mt76, &msta->wcid); + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); } static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy) @@ -788,6 +788,7 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif_link *link = &mvif->deflink; u8 band_idx = link->phy->mt76->band_idx; int idx; @@ -796,14 +797,15 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (idx < 0) return -ENOSPC; - INIT_LIST_HEAD(&msta->rc_list); - INIT_LIST_HEAD(&msta->wcid.poll_list); msta->vif = mvif; - msta->wcid.sta = 1; - msta->wcid.idx = idx; - msta->wcid.phy_idx = band_idx; + INIT_LIST_HEAD(&msta_link->rc_list); + INIT_LIST_HEAD(&msta_link->wcid.poll_list); + msta_link->sta = msta; + msta_link->wcid.sta = 1; + msta_link->wcid.idx = idx; + msta_link->wcid.phy_idx = band_idx; - ewma_avg_signal_init(&msta->avg_ack_signal); + ewma_avg_signal_init(&msta_link->avg_ack_signal); mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -818,6 +820,7 @@ int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_vif_link *link = &mvif->deflink; int i, ret; @@ -833,8 +836,8 @@ int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (ret) return ret; - msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; - msta->wcid.sta = 1; + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta_link->wcid.sta = 1; return 0; @@ -843,13 +846,13 @@ int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, CONN_STATE_PORT_SECURE, false); case MT76_STA_EVENT_DISASSOC: - for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) + for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) mt7996_mac_twt_teardown_flow(dev, msta, i); mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, false); - msta->wcid.sta_disabled = 1; - msta->wcid.sta = 0; + msta_link->wcid.sta_disabled = 1; + msta_link->wcid.sta = 0; return 0; } @@ -862,15 +865,16 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; - mt7996_mac_wtbl_update(dev, msta->wcid.idx, + mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); spin_lock_bh(&mdev->sta_poll_lock); - if (!list_empty(&msta->wcid.poll_list)) - list_del_init(&msta->wcid.poll_list); - if (!list_empty(&msta->rc_list)) - list_del_init(&msta->rc_list); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + if (!list_empty(&msta_link->rc_list)) + list_del_init(&msta_link->rc_list); spin_unlock_bh(&mdev->sta_poll_lock); } @@ -888,7 +892,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, struct mt7996_vif *mvif; mvif = (struct mt7996_vif *)vif->drv_priv; - wcid = &mvif->deflink.sta.wcid; + wcid = &mvif->deflink.sta.deflink.wcid; if (mvif->mt76.roc_phy && (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { @@ -901,10 +905,10 @@ static void mt7996_tx(struct ieee80211_hw *hw, } if (control->sta) { - struct mt7996_sta *sta; + struct mt7996_sta_link *msta_link; - sta = (struct mt7996_sta *)control->sta->drv_priv; - wcid = &sta->wcid; + msta_link = (struct mt7996_sta_link *)control->sta->drv_priv; + wcid = &msta_link->wcid; } if (!mphy) { @@ -944,6 +948,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta = params->sta; struct ieee80211_txq *txq = sta->txq[params->tid]; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; u16 tid = params->tid; u16 ssn = params->ssn; struct mt76_txq *mtxq; @@ -957,12 +962,12 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_lock(&dev->mt76.mutex); switch (action) { case IEEE80211_AMPDU_RX_START: - mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid, ssn, params->buf_size); ret = mt7996_mcu_add_rx_ba(dev, params, true); break; case IEEE80211_AMPDU_RX_STOP: - mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, tid); ret = mt7996_mcu_add_rx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_OPERATIONAL: @@ -973,16 +978,16 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; - clear_bit(tid, &msta->wcid.ampdu_state); + clear_bit(tid, &msta_link->wcid.ampdu_state); ret = mt7996_mcu_add_tx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_START: - set_bit(tid, &msta->wcid.ampdu_state); + set_bit(tid, &msta_link->wcid.ampdu_state); ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; break; case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; - clear_bit(tid, &msta->wcid.ampdu_state); + clear_bit(tid, &msta_link->wcid.ampdu_state); ret = mt7996_mcu_add_tx_ba(dev, params, false); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; @@ -1173,7 +1178,8 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct rate_info *txrate = &msta->wcid.rate; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct rate_info *txrate = &msta_link->wcid.rate; if (txrate->legacy || txrate->flags) { if (txrate->legacy) { @@ -1193,29 +1199,30 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, sinfo->txrate.flags = txrate->flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); - sinfo->tx_failed = msta->wcid.stats.tx_failed; + sinfo->tx_failed = msta_link->wcid.stats.tx_failed; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); - sinfo->tx_retries = msta->wcid.stats.tx_retries; + sinfo->tx_retries = msta_link->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); - sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->ack_signal = (s8)msta_link->ack_signal; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); - sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->avg_ack_signal = + -(s8)ewma_avg_signal_read(&msta_link->avg_ack_signal); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { - sinfo->tx_bytes = msta->wcid.stats.tx_bytes; + sinfo->tx_bytes = msta_link->wcid.stats.tx_bytes; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); - sinfo->rx_bytes = msta->wcid.stats.rx_bytes; + sinfo->rx_bytes = msta_link->wcid.stats.rx_bytes; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); - sinfo->tx_packets = msta->wcid.stats.tx_packets; + sinfo->tx_packets = msta_link->wcid.stats.tx_packets; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); - sinfo->rx_packets = msta->wcid.stats.rx_packets; + sinfo->rx_packets = msta_link->wcid.stats.rx_packets; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); } } @@ -1224,12 +1231,13 @@ static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_sta_link *msta_link = &msta->deflink; u32 *changed = data; spin_lock_bh(&dev->mt76.sta_poll_lock); - msta->changed |= *changed; - if (list_empty(&msta->rc_list)) - list_add_tail(&msta->rc_list, &dev->sta_rc_list); + msta_link->changed |= *changed; + if (list_empty(&msta_link->rc_list)) + list_add_tail(&msta_link->rc_list, &dev->sta_rc_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); } @@ -1276,13 +1284,14 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; if (enabled) - set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); else - clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); - if (!msta->wcid.sta) + if (!msta_link->wcid.sta) return; mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); @@ -1295,13 +1304,14 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; if (enabled) - set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta_link->wcid.flags); else - clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta_link->wcid.flags); - if (!msta->wcid.sta) + if (!msta_link->wcid.sta) return; mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); @@ -1436,11 +1446,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) { struct mt76_ethtool_worker_info *wi = wi_data; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; if (msta->vif->deflink.mt76.idx != wi->idx) return; - mt76_ethtool_worker(wi, &msta->wcid.stats, true); + mt76_ethtool_worker(wi, &msta_link->wcid.stats, true); } static @@ -1617,6 +1628,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif_link *mlink = &mvif->deflink; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mtk_wed_device *wed = &dev->mt76.mmio.wed; @@ -1632,7 +1644,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, if (!mtk_wed_device_active(wed)) return -ENODEV; - if (!msta->wcid.sta || msta->wcid.idx > MT7996_WTBL_STA) + if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA) return -EIO; path->type = DEV_PATH_MTK_WDMA; @@ -1640,7 +1652,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, path->mtk_wdma.wdma_idx = wed->wdma_idx; path->mtk_wdma.bss = mvif->deflink.mt76.idx; path->mtk_wdma.queue = 0; - path->mtk_wdma.wcid = msta->wcid.idx; + path->mtk_wdma.wcid = msta_link->wcid.idx; path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed); ctx->dev = NULL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 76f489e2f602..b0ddc2af1516 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1162,10 +1162,11 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, bool enable) { struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif *mvif = msta->vif; if (enable && !params->amsdu) - msta->wcid.amsdu = false; + msta_link->wcid.amsdu = false; return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, true); } @@ -1322,6 +1323,7 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct sta_rec_amsdu *amsdu; struct tlv *tlv; @@ -1337,7 +1339,7 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; - msta->wcid.amsdu = true; + msta_link->wcid.amsdu = true; switch (sta->deflink.agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: @@ -1879,13 +1881,14 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct sta_phy_uni *phy = data; struct sta_rec_ra_fixed_uni *ra; struct sk_buff *skb; struct tlv *tlv; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1972,14 +1975,14 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed GI */ if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI || mask->control[band].he_gi != GENMASK(7, 0)) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = (void *)sta->drv_priv; u32 addr; /* firmware updates only TXCMD but doesn't take WTBL into * account, so driver should update here to reflect the * actual txrate hardware sends out. */ - addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7); + addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 7); if (sta->deflink.he_cap.has_he) mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); else @@ -2113,11 +2116,12 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; struct sk_buff *skb; int ret; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2148,6 +2152,7 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, { #define MT_STA_BSS_GROUP 1 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link; struct mt7996_sta *msta; struct { u8 __rsv1[4]; @@ -2167,7 +2172,8 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, }; msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; - req.wlan_idx = cpu_to_le16(msta->wcid.idx); + msta_link = &msta->deflink; + req.wlan_idx = cpu_to_le16(msta_link->wcid.idx); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, sizeof(req), true); @@ -2184,8 +2190,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, if (sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; - wcid = &msta->wcid; + wcid = &msta_link->wcid; link_sta = &sta->deflink; } @@ -2312,12 +2319,14 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, { #define TSC_TYPE_BIGTK_PN 2 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &mvif->deflink.sta.deflink; struct sta_rec_pn_info *pn_info; struct sk_buff *skb, *rskb; struct tlv *tlv; int ret; - skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, &mvif->deflink.sta.wcid); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, + &msta_link->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -4344,19 +4353,21 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_sta *sta) { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link; struct mt7996_sta *msta; struct sk_buff *skb; msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; + msta_link = &msta->deflink; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec hdr trans */ - mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta->wcid); + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta_link->wcid); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 29fabb9b04ae..d0a4240f12a4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -185,10 +185,10 @@ struct mt7996_twt_flow { DECLARE_EWMA(avg_signal, 10, 8) -struct mt7996_sta { +struct mt7996_sta_link { struct mt76_wcid wcid; /* must be first */ - struct mt7996_vif *vif; + struct mt7996_sta *sta; struct list_head rc_list; u32 airtime_ac[8]; @@ -206,6 +206,12 @@ struct mt7996_sta { } twt; }; +struct mt7996_sta { + struct mt7996_sta_link deflink; /* must be first */ + + struct mt7996_vif *vif; +}; + struct mt7996_vif_link { struct mt76_vif_link mt76; /* must be first */ From 63ffee8cc9e463583ae529bcfa12ee132152dee8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 395/465] wifi: mt76: mt7996: Add mt7996_sta_link struct in mt7996_vif_link JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 35997d7d39cce52c8ffa1e06acac6240e67bca33 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:02 2025 +0100 wifi: mt76: mt7996: Add mt7996_sta_link struct in mt7996_vif_link Introduce mt7996_sta_link field in mt7996_vif_link structure instead of mt7996_sta. This is a preliminary patch to support MLO in MT7996 driver. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-3-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++--- .../net/wireless/mediatek/mt76/mt7996/main.c | 24 ++++++++++--------- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 10 ++++---- .../wireless/mediatek/mt76/mt7996/mt7996.h | 2 +- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index c95f5f776e78..776a41ee6a08 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -72,10 +72,10 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, msta_link = container_of(wcid, struct mt7996_sta_link, wcid); msta = msta_link->sta; - if (!msta->vif) + if (!msta || !msta->vif) return NULL; - return &msta->vif->deflink.sta.deflink.wcid; + return &msta->vif->deflink.msta_link.wcid; } bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) @@ -202,7 +202,7 @@ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, struct ieee80211_vif *vif, bool enable) { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link = &mvif->deflink.sta.deflink; + struct mt7996_sta_link *msta_link = &mvif->deflink.msta_link; u32 addr; addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 5); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 0cd011619fd3..34f984387c00 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -161,19 +161,23 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct mt7996_vif_link *mlink, struct ieee80211_key_conf *key) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : - &mlink->sta; - struct mt76_wcid *wcid = &msta->deflink.wcid; - u8 *wcid_keyidx = &wcid->hw_key_idx; + struct mt76_wcid *wcid = &mlink->msta_link.wcid; struct mt7996_phy *phy; int idx = key->keyidx; + u8 *wcid_keyidx; phy = mt7996_vif_link_phy(mlink); if (!phy) return -EINVAL; - if (sta && !wcid->sta) - return -EOPNOTSUPP; + if (sta) { + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + wcid = &msta->deflink.wcid; + if (!wcid->sta) + return -EOPNOTSUPP; + } + wcid_keyidx = &wcid->hw_key_idx; switch (key->cipher) { case WLAN_CIPHER_SUITE_AES_CMAC: @@ -229,7 +233,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); - struct mt7996_sta_link *msta_link = &link->sta.deflink; + struct mt7996_sta_link *msta_link = &link->msta_link; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; u8 band_idx = phy->mt76->band_idx; @@ -261,7 +265,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, idx = MT7996_WTBL_RESERVED - mlink->idx; INIT_LIST_HEAD(&msta_link->rc_list); - msta_link->sta = &link->sta; msta_link->wcid.idx = idx; msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; mt76_wcid_init(&msta_link->wcid, band_idx); @@ -303,10 +306,9 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_sta_link *msta_link = &link->msta_link; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; - struct mt7996_sta *msta = &link->sta; - struct mt7996_sta_link *msta_link = &msta->deflink; int idx = msta_link->wcid.idx; mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_DISCONNECT, false); @@ -892,7 +894,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, struct mt7996_vif *mvif; mvif = (struct mt7996_vif *)vif->drv_priv; - wcid = &mvif->deflink.sta.deflink.wcid; + wcid = &mvif->deflink.msta_link.wcid; if (mvif->mt76.roc_phy && (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index b0ddc2af1516..9018ede6efed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2171,8 +2171,8 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, .val = cpu_to_le32(mvif->deflink.mt76.idx % 16), }; - msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; - msta_link = &msta->deflink; + msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; + msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link; req.wlan_idx = cpu_to_le16(msta_link->wcid.idx); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, @@ -2319,7 +2319,7 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, { #define TSC_TYPE_BIGTK_PN 2 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link = &mvif->deflink.sta.deflink; + struct mt7996_sta_link *msta_link = &mvif->deflink.msta_link; struct sta_rec_pn_info *pn_info; struct sk_buff *skb, *rskb; struct tlv *tlv; @@ -4357,8 +4357,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct mt7996_sta *msta; struct sk_buff *skb; - msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; - msta_link = &msta->deflink; + msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; + msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, &msta_link->wcid, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index d0a4240f12a4..ac5b94e1315e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -215,7 +215,7 @@ struct mt7996_sta { struct mt7996_vif_link { struct mt76_vif_link mt76; /* must be first */ - struct mt7996_sta sta; + struct mt7996_sta_link msta_link; struct mt7996_phy *phy; struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; From 52b3a63475d8a1f2ef0712f9e8549000a6ae636b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:04 +0200 Subject: [PATCH 396/465] wifi: mt76: mt7996: Add vif_cfg_changed callback JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2b967a3ad1b054b8a2e0eaa503009029f0e4ffe5 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:03 2025 +0100 wifi: mt76: mt7996: Add vif_cfg_changed callback Introduce vif_cfg_changed mac80211 callback as preliminary patch to enable MLO support in MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Shayne Chen Signed-off-by: Shayne Chen Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-4-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 34f984387c00..59a1e5e52450 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -679,6 +679,39 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]); } +static void +mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + + if ((changed & BSS_CHANGED_ASSOC) && vif->cfg.assoc) { + 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) + continue; + + if (!link->phy) + continue; + + mt7996_mcu_add_bss_info(link->phy, vif, link_conf, + &link->mt76, true); + mt7996_mcu_add_sta(dev, vif, &link->mt76, NULL, + CONN_STATE_PORT_SECURE, + !!(changed & BSS_CHANGED_BSSID)); + } + } + + mutex_unlock(&dev->mt76.mutex); +} + static void mt7996_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -705,7 +738,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, * and then peer references bss_info_rfch to set bandwidth cap. */ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) || - (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) || (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) { mt7996_mcu_add_bss_info(phy, vif, info, mvif, true); mt7996_mcu_add_sta(dev, vif, mvif, NULL, CONN_STATE_PORT_SECURE, @@ -1688,6 +1720,7 @@ const struct ieee80211_ops mt7996_ops = { .conf_tx = mt7996_conf_tx, .configure_filter = mt7996_configure_filter, .bss_info_changed = mt7996_bss_info_changed, + .vif_cfg_changed = mt7996_vif_cfg_changed, .sta_state = mt76_sta_state, .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, .link_sta_rc_update = mt7996_sta_rc_update, From bf08157c1849a6ffb501ced47bd95e8f3da4a322 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 397/465] wifi: mt76: mt7996: Add link_info_changed callback JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0a04597cd37bd258f3d361556999944aef1eba91 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:04 2025 +0100 wifi: mt76: mt7996: Add link_info_changed callback Convert bss_info_changed mac80211 callback in link_info_changed one. This is a preliminary patch to enable MLO support in MT7996 driver. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-5-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 59a1e5e52450..81a13662db24 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -712,10 +712,9 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_unlock(&dev->mt76.mutex); } -static void mt7996_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u64 changed) +static void +mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed) { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt76_vif_link *mvif; @@ -1719,8 +1718,8 @@ const struct ieee80211_ops mt7996_ops = { .config = mt7996_config, .conf_tx = mt7996_conf_tx, .configure_filter = mt7996_configure_filter, - .bss_info_changed = mt7996_bss_info_changed, .vif_cfg_changed = mt7996_vif_cfg_changed, + .link_info_changed = mt7996_link_info_changed, .sta_state = mt76_sta_state, .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, .link_sta_rc_update = mt7996_sta_rc_update, From 2346bb4b710daaae12180323d7182864af3cb73e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 398/465] wifi: mt76: mt7996: Add mt7996_sta_state routine JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e5d944b4af63285cc5133d9619cc2a4722cb588d Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:05 2025 +0100 wifi: mt76: mt7996: Add mt7996_sta_state routine Introduce mt7996_sta_state routine in order to initialize wcid structure in mt7996 codebase. This is a preliminary patch to enable MLO support in MT7996 driver. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-6-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mac80211.c | 5 +- drivers/net/wireless/mediatek/mt76/mt76.h | 2 + .../net/wireless/mediatek/mt76/mt7996/main.c | 100 +++++++++++++++--- .../net/wireless/mediatek/mt76/mt7996/mmio.c | 3 - .../wireless/mediatek/mt76/mt7996/mt7996.h | 6 -- 5 files changed, 91 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 2c98cc42cd7e..b88d7e10742e 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -816,8 +816,8 @@ void mt76_free_device(struct mt76_dev *dev) } EXPORT_SYMBOL_GPL(mt76_free_device); -static struct mt76_phy * -mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; struct mt76_chanctx *ctx; @@ -831,6 +831,7 @@ mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif) ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv; return ctx->phy; } +EXPORT_SYMBOL_GPL(mt76_vif_phy); static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index b461273c3306..d7cd467b812f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1228,6 +1228,8 @@ struct mt76_phy *mt76_alloc_phy(struct mt76_dev *dev, unsigned int size, u8 band_idx); int mt76_register_phy(struct mt76_phy *phy, bool vht, struct ieee80211_rate *rates, int n_rates); +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); struct dentry *mt76_register_debugfs_fops(struct mt76_phy *phy, const struct file_operations *ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 81a13662db24..d99a98d57142 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -815,20 +815,26 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } -int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static int +mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { + struct mt76_dev *mdev = mphy->dev; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif_link *link = &mvif->deflink; - u8 band_idx = link->phy->mt76->band_idx; - int idx; + u8 band_idx = mphy->band_idx; + int i, idx, ret = 0; - idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); - if (idx < 0) - return -ENOSPC; + mutex_lock(&mdev->mutex); + + idx = mt76_wcid_alloc(mdev->wcid_mask, MT7996_WTBL_STA); + if (idx < 0) { + ret = -ENOSPC; + goto unlock; + } msta->vif = mvif; INIT_LIST_HEAD(&msta_link->rc_list); @@ -838,20 +844,37 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta_link->wcid.idx = idx; msta_link->wcid.phy_idx = band_idx; + 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; + } + ewma_avg_signal_init(&msta_link->avg_ack_signal); + ewma_signal_init(&msta_link->wcid.rssi); mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, true); - return 0; + rcu_assign_pointer(mdev->wcid[idx], &msta_link->wcid); + mt76_wcid_init(&msta_link->wcid, band_idx); + mphy->num_sta++; +unlock: + mutex_unlock(&mdev->mutex); + + return ret; } -int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, enum mt76_sta_event ev) +static int +mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) { - struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; @@ -893,12 +916,20 @@ int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, return 0; } -void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static void +mt7996_mac_sta_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { + struct mt76_dev *mdev = mphy->dev; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link = &msta->deflink; + int i, idx = msta_link->wcid.idx; + + mutex_lock(&mdev->mutex); + + for (i = 0; i < ARRAY_SIZE(msta_link->wcid.aggr); i++) + mt76_rx_aggr_stop(mdev, &msta_link->wcid, i); mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -909,6 +940,47 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (!list_empty(&msta_link->rc_list)) list_del_init(&msta_link->rc_list); spin_unlock_bh(&mdev->sta_poll_lock); + + mt76_wcid_cleanup(mdev, &msta_link->wcid); + mt76_wcid_mask_clear(mdev->wcid_mask, idx); + mphy->num_sta--; + + mutex_unlock(&mdev->mutex); +} + +static int +mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct mt76_phy *mphy = mt76_vif_phy(hw, vif); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + enum mt76_sta_event ev; + + if (!mphy) + return -EINVAL; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + return mt7996_mac_sta_add(mphy, vif, sta); + + if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + mt7996_mac_sta_remove(mphy, vif, sta); + + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + ev = MT76_STA_EVENT_ASSOC; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) + ev = MT76_STA_EVENT_AUTHORIZE; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) + ev = MT76_STA_EVENT_DISASSOC; + else + return 0; + + return mt7996_mac_sta_event(dev, vif, sta, ev); } static void mt7996_tx(struct ieee80211_hw *hw, @@ -1720,7 +1792,7 @@ const struct ieee80211_ops mt7996_ops = { .configure_filter = mt7996_configure_filter, .vif_cfg_changed = mt7996_vif_cfg_changed, .link_info_changed = mt7996_link_info_changed, - .sta_state = mt76_sta_state, + .sta_state = mt7996_sta_state, .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, .link_sta_rc_update = mt7996_sta_rc_update, .set_key = mt7996_set_key, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 9d37f8238746..13b188e281bd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -618,9 +618,6 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, .rx_skb = mt7996_queue_rx_skb, .rx_check = mt7996_rx_check, .rx_poll_complete = mt7996_rx_poll_complete, - .sta_add = mt7996_mac_sta_add, - .sta_event = mt7996_mac_sta_event, - .sta_remove = mt7996_mac_sta_remove, .update_survey = mt7996_update_channel, .set_channel = mt7996_set_channel, .vif_link_add = mt7996_vif_link_add, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index ac5b94e1315e..117a9e6c4964 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -696,12 +696,6 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct ieee80211_key_conf *key, int pid, enum mt76_txq_id qid, u32 changed); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); -int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, enum mt76_sta_event ev); -void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); void mt7996_mac_work(struct work_struct *work); void mt7996_mac_reset_work(struct work_struct *work); void mt7996_mac_dump_work(struct work_struct *work); From 53981306ee87fa861f0aa36cc9f3681152011f65 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 399/465] wifi: mt76: mt7996: Rely on mt7996_sta_link in sta_add/sta_remove callbacks JIRA: https://issues.redhat.com/browse/RHEL-89168 commit dd82a9e02c054052b5899872c1f32805428f6131 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:06 2025 +0100 wifi: mt76: mt7996: Rely on mt7996_sta_link in sta_add/sta_remove callbacks Generalize mt7996_mac_sta_add() and mt7996_mac_sta_remove() routines to deal with mt7996_sta_link structure. This is a preliminary patch to introduce MLO support for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Shayne Chen Signed-off-by: Shayne Chen Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-7-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 203 +++++++++++++----- .../wireless/mediatek/mt76/mt7996/mt7996.h | 4 + 2 files changed, 150 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index d99a98d57142..1bca444d2d02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -815,6 +815,143 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } +static int +mt7996_mac_sta_init_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct mt7996_vif_link *link, + struct ieee80211_sta *sta, unsigned int link_id) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_phy *phy = link->phy; + struct mt7996_sta_link *msta_link; + int idx; + + 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; + + 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; + } + } else { + msta_link = kzalloc(sizeof(*msta_link), GFP_KERNEL); + if (!msta_link) + return -ENOMEM; + } + + INIT_LIST_HEAD(&msta_link->rc_list); + INIT_LIST_HEAD(&msta_link->wcid.poll_list); + msta_link->sta = msta; + msta_link->wcid.sta = 1; + msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_id; + + ewma_avg_signal_init(&msta_link->avg_ack_signal); + ewma_signal_init(&msta_link->wcid.rssi); + + rcu_assign_pointer(msta->link[link_id], msta_link); + + mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, + true); + + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); + mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx); + + return 0; +} + +static void +mt7996_mac_sta_deinit_link(struct mt7996_dev *dev, + struct mt7996_sta_link *msta_link) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msta_link->wcid.aggr); i++) + mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, i); + + mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + if (!list_empty(&msta_link->rc_list)) + list_del_init(&msta_link->rc_list); + 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_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; + + msta_link = rcu_replace_pointer(msta->link[link_id], msta_link, + lockdep_is_held(&mdev->mutex)); + if (!msta_link) + continue; + + mt7996_mac_sta_deinit_link(dev, msta_link); + if (msta->deflink_id == link_id) { + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + continue; + } + + kfree_rcu(msta_link, rcu_head); + } +} + +static int +mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long new_links) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned int link_id; + int err; + + for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + + if (rcu_access_pointer(msta->link[link_id])) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + goto error_unlink; + + err = mt7996_mac_sta_init_link(dev, vif, link, sta, link_id); + if (err) + goto error_unlink; + } + + return 0; + +error_unlink: + mt7996_mac_sta_remove_links(dev, sta, new_links); + + return err; +} + static int mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -823,52 +960,20 @@ mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - struct mt7996_vif_link *link = &mvif->deflink; - u8 band_idx = mphy->band_idx; - int i, idx, ret = 0; + unsigned long links = sta->mlo ? sta->valid_links : BIT(0); + int err; mutex_lock(&mdev->mutex); - idx = mt76_wcid_alloc(mdev->wcid_mask, MT7996_WTBL_STA); - if (idx < 0) { - ret = -ENOSPC; - goto unlock; - } - + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; msta->vif = mvif; - INIT_LIST_HEAD(&msta_link->rc_list); - INIT_LIST_HEAD(&msta_link->wcid.poll_list); - msta_link->sta = msta; - msta_link->wcid.sta = 1; - msta_link->wcid.idx = idx; - msta_link->wcid.phy_idx = band_idx; + err = mt7996_mac_sta_add_links(dev, vif, sta, links); + if (!err) + mphy->num_sta++; - 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; - } - - ewma_avg_signal_init(&msta_link->avg_ack_signal); - ewma_signal_init(&msta_link->wcid.rssi); - - mt7996_mac_wtbl_update(dev, idx, - MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, - true); - - rcu_assign_pointer(mdev->wcid[idx], &msta_link->wcid); - mt76_wcid_init(&msta_link->wcid, band_idx); - mphy->num_sta++; -unlock: mutex_unlock(&mdev->mutex); - return ret; + return err; } static int @@ -922,27 +1027,11 @@ mt7996_mac_sta_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, { struct mt76_dev *mdev = mphy->dev; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - int i, idx = msta_link->wcid.idx; + unsigned long links = sta->mlo ? sta->valid_links : BIT(0); mutex_lock(&mdev->mutex); - for (i = 0; i < ARRAY_SIZE(msta_link->wcid.aggr); i++) - mt76_rx_aggr_stop(mdev, &msta_link->wcid, i); - - mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, - MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - - spin_lock_bh(&mdev->sta_poll_lock); - if (!list_empty(&msta_link->wcid.poll_list)) - list_del_init(&msta_link->wcid.poll_list); - if (!list_empty(&msta_link->rc_list)) - list_del_init(&msta_link->rc_list); - spin_unlock_bh(&mdev->sta_poll_lock); - - mt76_wcid_cleanup(mdev, &msta_link->wcid); - mt76_wcid_mask_clear(mdev->wcid_mask, idx); + mt7996_mac_sta_remove_links(dev, sta, links); mphy->num_sta--; mutex_unlock(&mdev->mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 117a9e6c4964..cf37baa91a8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -204,10 +204,14 @@ struct mt7996_sta_link { u8 flowid_mask; struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT]; } twt; + + struct rcu_head rcu_head; }; struct mt7996_sta { struct mt7996_sta_link deflink; /* must be first */ + struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + u8 deflink_id; struct mt7996_vif *vif; }; From fae493d2025386c5e0e6e0cadb589cbb4a98706c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 400/465] wifi: mt76: mt7996: Add mt7996_mac_sta_change_links callback JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f520eceacd24ce8f70e222d31c3e694b21568bd7 Author: Shayne Chen Date: Tue Mar 11 18:45:07 2025 +0100 wifi: mt76: mt7996: Add mt7996_mac_sta_change_links callback Intrdouce mt7996_mac_sta_change_links routine to set change_sta_links required by mac80211. This is a preliminary patch to introduce MLO support for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-8-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 1bca444d2d02..307c68c6b0cd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -952,6 +952,26 @@ error_unlink: return err; } +static int +mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 old_links, + u16 new_links) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + unsigned long add = new_links & ~old_links; + unsigned long rem = old_links & ~new_links; + int ret; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_sta_remove_links(dev, sta, rem); + ret = mt7996_mac_sta_add_links(dev, vif, sta, add); + + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + static int mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -1921,4 +1941,5 @@ const struct ieee80211_ops mt7996_ops = { .net_setup_tc = mt76_wed_net_setup_tc, #endif .change_vif_links = mt7996_change_vif_links, + .change_sta_links = mt7996_mac_sta_change_links, }; From c7877eeeb00fafa7c25442e7c3c1c65b7ebe3eac Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 401/465] wifi: mt76: mt7996: Support MLO in mt7996_mac_sta_event() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ecd72f9695e7e250af4d555de22ba302e37c805a Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:08 2025 +0100 wifi: mt76: mt7996: Support MLO in mt7996_mac_sta_event() Similar to mt7996_mac_sta_add() adn mt7996_mac_sta_remove(), update mt7996_mac_sta_event routine to take into account MLO support. Please note mcu routines does not support MLO yet. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Shayne Chen Signed-off-by: Shayne Chen Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-9-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 307c68c6b0cd..6d323b5e4e80 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1001,41 +1001,52 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum mt76_sta_event ev) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_vif_link *link = &mvif->deflink; - int i, ret; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - switch (ev) { - case MT76_STA_EVENT_ASSOC: - ret = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_CONNECT, true); - if (ret) - return ret; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + int i, err; - ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); - if (ret) - return ret; + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; - msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; - msta_link->wcid.sta = 1; + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; - return 0; + switch (ev) { + case MT76_STA_EVENT_ASSOC: + err = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, + CONN_STATE_CONNECT, true); + if (err) + return err; - case MT76_STA_EVENT_AUTHORIZE: - return mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_PORT_SECURE, false); + err = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); + if (err) + return err; - case MT76_STA_EVENT_DISASSOC: - for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) - mt7996_mac_twt_teardown_flow(dev, msta, i); + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta_link->wcid.sta = 1; + break; + case MT76_STA_EVENT_AUTHORIZE: + err = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, + CONN_STATE_PORT_SECURE, false); + if (err) + return err; + break; + case MT76_STA_EVENT_DISASSOC: + for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) + mt7996_mac_twt_teardown_flow(dev, msta, i); - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_DISCONNECT, false); - msta_link->wcid.sta_disabled = 1; - msta_link->wcid.sta = 0; - - return 0; + mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, + CONN_STATE_DISCONNECT, false); + msta_link->wcid.sta_disabled = 1; + msta_link->wcid.sta = 0; + break; + } } return 0; From 382012a6c23d0a6289ebedf956c1fa31f68bf241 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 402/465] wifi: mt76: Check link_conf pointer in mt76_connac_mcu_sta_basic_tlv() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9890624c1b3948c1c7f1d0e19ef0bb7680b8c80d Author: Shayne Chen Date: Tue Mar 11 18:45:09 2025 +0100 wifi: mt76: Check link_conf pointer in mt76_connac_mcu_sta_basic_tlv() This is a preliminary patch to introduce MLO support for MT7996 driver. Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-10-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index d0e49d68c5db..bafcf5a279e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -391,7 +391,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC); if (vif->type == NL80211_IFTYPE_STATION && - !is_zero_ether_addr(link_conf->bssid)) { + link_conf && !is_zero_ether_addr(link_conf->bssid)) { memcpy(basic->peer_addr, link_conf->bssid, ETH_ALEN); basic->aid = cpu_to_le16(vif->cfg.aid); } else { From c2e52b401a506611d8e65c6918f70257c5aae89e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 403/465] wifi: mt76: mt7996: Update mt7996_mcu_add_sta to MLO support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c7e4fc362443ecbdae2f0adef16eda955f433473 Author: Shayne Chen Date: Tue Mar 11 18:45:10 2025 +0100 wifi: mt76: mt7996: Update mt7996_mcu_add_sta to MLO support Update mt7996_mcu_add_sta routine and all the called subroutines to support MLO. This is a preliminary patch to enable MLO for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-11-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 72 +++-- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 296 +++++++++--------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 9 +- 3 files changed, 208 insertions(+), 169 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 6d323b5e4e80..5ab4f08dba06 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -293,7 +293,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, * interface, since firmware only records BSSID when the entry is new */ if (vif->type != NL80211_IFTYPE_STATION) - mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_PORT_SECURE, true); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_PORT_SECURE, true); rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, link); @@ -311,7 +312,8 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt7996_dev *dev = phy->dev; int idx = msta_link->wcid.idx; - mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_DISCONNECT, false); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_DISCONNECT, false); mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, false); mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false); @@ -703,7 +705,7 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7996_mcu_add_bss_info(link->phy, vif, link_conf, &link->mt76, true); - mt7996_mcu_add_sta(dev, vif, &link->mt76, NULL, + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, CONN_STATE_PORT_SECURE, !!(changed & BSS_CHANGED_BSSID)); } @@ -717,17 +719,17 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u64 changed) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt76_vif_link *mvif; + struct mt7996_vif_link *link; struct mt7996_phy *phy; struct mt76_phy *mphy; mutex_lock(&dev->mt76.mutex); - mvif = mt76_vif_conf_link(&dev->mt76, vif, info); - if (!mvif) + link = mt7996_vif_conf_link(dev, vif, info); + if (!link) goto out; - mphy = mt76_vif_link_phy(mvif); + mphy = mt76_vif_link_phy(&link->mt76); if (!mphy) goto out; @@ -738,8 +740,9 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, */ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) || (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) { - mt7996_mcu_add_bss_info(phy, vif, info, mvif, true); - mt7996_mcu_add_sta(dev, vif, mvif, NULL, CONN_STATE_PORT_SECURE, + mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76, true); + mt7996_mcu_add_sta(dev, info, NULL, link, NULL, + CONN_STATE_PORT_SECURE, !!(changed & BSS_CHANGED_BSSID)); } @@ -756,11 +759,11 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } if (changed & BSS_CHANGED_MCAST_RATE) - mvif->mcast_rates_idx = + link->mt76.mcast_rates_idx = mt7996_get_rates_table(phy, info, false, true); if (changed & BSS_CHANGED_BASIC_RATES) - mvif->basic_rates_idx = + link->mt76.basic_rates_idx = mt7996_get_rates_table(phy, info, false, false); /* ensure that enable txcmd_mode after bss_info */ @@ -772,15 +775,15 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changed & BSS_CHANGED_HE_BSS_COLOR) { if ((vif->type == NL80211_IFTYPE_AP && - mvif->omac_idx <= HW_BSSID_MAX) || + link->mt76.omac_idx <= HW_BSSID_MAX) || vif->type == NL80211_IFTYPE_STATION) - mt7996_mcu_update_bss_color(dev, mvif, + mt7996_mcu_update_bss_color(dev, &link->mt76, &info->he_bss_color); } if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { - mvif->beacon_rates_idx = + link->mt76.beacon_rates_idx = mt7996_get_rates_table(phy, info, true, false); mt7996_mcu_add_beacon(hw, vif, info); @@ -816,10 +819,12 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw, } static int -mt7996_mac_sta_init_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct mt7996_vif_link *link, - struct ieee80211_sta *sta, unsigned int link_id) +mt7996_mac_sta_init_link(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, unsigned int link_id) { + 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_sta_link *msta_link; @@ -863,8 +868,8 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, rcu_assign_pointer(msta->link[link_id], msta_link); mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, - true); + mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link, + CONN_STATE_DISCONNECT, true); rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx); @@ -930,16 +935,27 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, int err; 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_vif_link *link; if (rcu_access_pointer(msta->link[link_id])) continue; + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + goto error_unlink; + link = mt7996_vif_link(dev, vif, link_id); if (!link) goto error_unlink; - err = mt7996_mac_sta_init_link(dev, vif, link, sta, link_id); + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) + goto error_unlink; + + err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link, + link_id); if (err) goto error_unlink; } @@ -1005,10 +1021,15 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, unsigned int link_id; for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct ieee80211_bss_conf *link_conf; struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; int i, err; + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + continue; + link = mt7996_vif_link(dev, vif, link_id); if (!link) continue; @@ -1019,7 +1040,8 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, switch (ev) { case MT76_STA_EVENT_ASSOC: - err = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, CONN_STATE_CONNECT, true); if (err) return err; @@ -1032,7 +1054,8 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, msta_link->wcid.sta = 1; break; case MT76_STA_EVENT_AUTHORIZE: - err = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, CONN_STATE_PORT_SECURE, false); if (err) return err; @@ -1041,8 +1064,9 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) mt7996_mac_twt_teardown_flow(dev, msta, i); - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_DISCONNECT, false); + mt7996_mcu_add_sta(dev, link_conf, link_sta, link, + msta_link, CONN_STATE_DISCONNECT, + false); msta_link->wcid.sta_disabled = 1; msta_link->wcid.sta = 0; break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 9018ede6efed..43e85308132e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -118,13 +118,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map) } static void -mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, - u16 mcs_map) +mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + __le16 *he_mcs, u16 mcs_map) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - enum nl80211_band band = msta->vif->deflink.phy->mt76->chandef.chan->band; - const u16 *mask = msta->vif->deflink.bitrate_mask.control[band].he_mcs; - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + 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; for (nss = 0; nss < max_nss; nss++) { int mcs; @@ -1182,15 +1182,17 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, } static void -mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_he_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { - struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct ieee80211_he_mcs_nss_supp mcs_map; struct sta_rec_he_v2 *he; struct tlv *tlv; int i = 0; - if (!sta->deflink.he_cap.has_he) + if (!link_sta->he_cap.has_he) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); @@ -1202,21 +1204,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) he->he_phy_cap[i] = elem->phy_cap_info[i]; } - mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; - switch (sta->deflink.bandwidth) { + mcs_map = link_sta->he_cap.he_mcs_nss_supp; + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (elem->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW8080], le16_to_cpu(mcs_map.rx_mcs_80p80)); - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW160], le16_to_cpu(mcs_map.rx_mcs_160)); fallthrough; default: - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW80], le16_to_cpu(mcs_map.rx_mcs_80)); break; @@ -1226,24 +1228,26 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) } static void -mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) { struct sta_rec_he_6g_capa *he_6g; struct tlv *tlv; - if (!sta->deflink.he_6ghz_capa.capa) + if (!link_sta->he_6ghz_capa.capa) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); he_6g = (struct sta_rec_he_6g_capa *)tlv; - he_6g->capa = sta->deflink.he_6ghz_capa.capa; + he_6g->capa = link_sta->he_6ghz_capa.capa; } static void -mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); struct ieee80211_eht_mcs_nss_supp *mcs_map; @@ -1251,11 +1255,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) struct sta_rec_eht *eht; struct tlv *tlv; - if (!sta->deflink.eht_cap.has_eht) + if (!link_sta->eht_cap.has_eht) return; - mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp; - elem = &sta->deflink.eht_cap.eht_cap_elem; + mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; + elem = &link_sta->eht_cap.eht_cap_elem; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); @@ -1266,7 +1270,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); if (vif->type != NL80211_IFTYPE_STATION && - (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | @@ -1282,48 +1286,48 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) } static void -mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_ht_uni *ht; struct tlv *tlv; - if (!sta->deflink.ht_cap.ht_supported) + if (!link_sta->ht_cap.ht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht_uni *)tlv; - ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); - ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor, + ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); + ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor, IEEE80211_HT_AMPDU_PARM_FACTOR) | - u8_encode_bits(sta->deflink.ht_cap.ampdu_density, + u8_encode_bits(link_sta->ht_cap.ampdu_density, IEEE80211_HT_AMPDU_PARM_DENSITY); } static void -mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_vht *vht; struct tlv *tlv; /* For 6G band, this tlv is necessary to let hw work normally */ - if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported) + if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; - vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); - vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; - vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; + vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); + vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; } static void mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt7996_sta_link *msta_link) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; struct sta_rec_amsdu *amsdu; struct tlv *tlv; @@ -1332,7 +1336,7 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, vif->type != NL80211_IFTYPE_AP) return; - if (!sta->deflink.agg.max_amsdu_len) + if (!link_sta->agg.max_amsdu_len) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); @@ -1341,7 +1345,7 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, amsdu->amsdu_en = true; msta_link->wcid.amsdu = true; - switch (sta->deflink.agg.max_amsdu_len) { + switch (link_sta->agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; @@ -1358,30 +1362,31 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, static void mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) { - struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct sta_rec_muru *muru; struct tlv *tlv; - if (vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_AP) + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); muru = (struct sta_rec_muru *)tlv; - muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer || - vif->bss_conf.he_mu_beamformer || - vif->bss_conf.vht_mu_beamformer || - vif->bss_conf.vht_mu_beamformee; + muru->cfg.mimo_dl_en = link_conf->eht_mu_beamformer || + link_conf->he_mu_beamformer || + link_conf->vht_mu_beamformer || + link_conf->vht_mu_beamformee; muru->cfg.ofdma_dl_en = true; - if (sta->deflink.vht_cap.vht_supported) + if (link_sta->vht_cap.vht_supported) muru->mimo_dl.vht_mu_bfee = - !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); - if (!sta->deflink.he_cap.has_he) + if (!link_sta->he_cap.has_he) return; muru->mimo_dl.partial_bw_dl_mimo = @@ -1412,49 +1417,50 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, } static inline bool -mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool bfee) +mt7996_is_ebf_supported(struct mt7996_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, bool bfee) { int sts = hweight16(phy->mt76->chainmask); - if (vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_AP) + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) return false; if (!bfee && sts < 2) return false; - if (sta->deflink.eht_cap.has_eht) { - struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap; + if (link_sta->eht_cap.has_eht) { + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; if (bfee) - return vif->bss_conf.eht_su_beamformee && + return link_conf->eht_su_beamformee && EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]); else - return vif->bss_conf.eht_su_beamformer && + return link_conf->eht_su_beamformer && EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]); } - if (sta->deflink.he_cap.has_he) { - struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; if (bfee) - return vif->bss_conf.he_su_beamformee && + return link_conf->he_su_beamformee && HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); else - return vif->bss_conf.he_su_beamformer && + return link_conf->he_su_beamformer && HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); } - if (sta->deflink.vht_cap.vht_supported) { - u32 cap = sta->deflink.vht_cap.cap; + if (link_sta->vht_cap.vht_supported) { + u32 cap = link_sta->vht_cap.cap; if (bfee) - return vif->bss_conf.vht_su_beamformee && + return link_conf->vht_su_beamformee && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); else - return vif->bss_conf.vht_su_beamformer && + return link_conf->vht_su_beamformer && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); } @@ -1475,10 +1481,11 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf, struct mt7996_phy *phy) } static void -mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, - struct sta_rec_bf *bf, bool explicit) +mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) { - struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; + struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs; u8 n = 0; bf->tx_mode = MT_PHY_TYPE_HT; @@ -1501,10 +1508,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, } static void -mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, - struct sta_rec_bf *bf, bool explicit) +mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) { - struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); @@ -1525,24 +1533,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, bf->ncol); - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->nrow = 1; } else { bf->nrow = tx_ant; bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->ibf_nrow = 1; } } static void -mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7996_phy *phy, struct sta_rec_bf *bf, - bool explicit) +mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) { - struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; + struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; const struct ieee80211_sta_he_cap *vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); @@ -1571,7 +1579,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160) return; /* go over for 160MHz and 80p80 */ @@ -1603,11 +1611,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, } static void -mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7996_phy *phy, struct sta_rec_bf *bf, - bool explicit) +mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) { - struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap; + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; const struct ieee80211_sta_eht_cap *vc = @@ -1631,10 +1639,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160) return; - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]); sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]); @@ -1662,13 +1670,15 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, static void mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { #define EBF_MODE BIT(0) #define IBF_MODE BIT(1) #define BF_MAT_ORDER 4 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mvif->deflink.phy; + 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; @@ -1680,10 +1690,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, }; bool ebf; - if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) return; - ebf = mt7996_is_ebf_supported(phy, vif, sta, false); + ebf = mt7996_is_ebf_supported(phy, link_conf, link_sta, false); if (!ebf && !dev->ibf) return; @@ -1694,28 +1704,29 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, * vht: support eBF and iBF * ht: iBF only, since mac80211 lacks of eBF support */ - if (sta->deflink.eht_cap.has_eht) - mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf, ebf); - else if (sta->deflink.he_cap.has_he) - mt7996_mcu_sta_bfer_he(sta, vif, phy, bf, ebf); - else if (sta->deflink.vht_cap.vht_supported) - mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf); - else if (sta->deflink.ht_cap.ht_supported) - mt7996_mcu_sta_bfer_ht(sta, phy, bf, ebf); + if (link_sta->eht_cap.has_eht) + mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf); + else if (link_sta->he_cap.has_he) + mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf); + else if (link_sta->vht_cap.vht_supported) + mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf); + else if (link_sta->ht_cap.ht_supported) + mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf); else return; bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0); if (is_mt7992(&dev->mt76) && tx_ant == 4) bf->bf_cap |= IBF_MODE; - bf->bw = sta->deflink.bandwidth; - bf->ibf_dbw = sta->deflink.bandwidth; + + bf->bw = link_sta->bandwidth; + bf->ibf_dbw = link_sta->bandwidth; bf->ibf_nrow = tx_ant; - if (sta->deflink.eht_cap.has_eht || sta->deflink.he_cap.has_he) + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he) bf->ibf_timeout = is_mt7996(&dev->mt76) ? MT7996_IBF_TIMEOUT : MT7992_IBF_TIMEOUT; - else if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY; else bf->ibf_timeout = MT7996_IBF_TIMEOUT; @@ -1729,7 +1740,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, matrix[bf->nrow][bf->ncol] : 0; } - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: case IEEE80211_STA_RX_BW_80: bf->mem_total = bf->mem_20m * 2; @@ -1745,31 +1756,32 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, static void mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mvif->deflink.phy; + struct mt7996_phy *phy = link->phy; int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; - if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) + if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he)) return; - if (!mt7996_is_ebf_supported(phy, vif, sta, true)) + if (!mt7996_is_ebf_supported(phy, link_conf, link_sta, true)) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); bfee = (struct sta_rec_bfee *)tlv; - if (sta->deflink.he_cap.has_he) { - struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, pe->phy_cap_info[5]); - } else if (sta->deflink.vht_cap.vht_supported) { - struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + } else if (link_sta->vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, pc->cap); @@ -2131,7 +2143,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, * update sta_rec_he here. */ if (changed) - mt7996_mcu_sta_he_tlv(skb, sta); + mt7996_mcu_sta_he_tlv(skb, &sta->deflink, &mvif->deflink); /* sta_rec_ra accommodates BW, NSS and only MCS range format * i.e 0-{7,8,9} for VHT. @@ -2179,67 +2191,67 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, sizeof(req), true); } -int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct mt76_vif_link *mlink, - struct ieee80211_sta *sta, int conn_state, bool newly) +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly) { - struct ieee80211_link_sta *link_sta = NULL; - struct mt76_wcid *wcid = mlink->wcid; + struct mt76_wcid *wcid = msta_link ? &msta_link->wcid : link->mt76.wcid; + struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL; struct sk_buff *skb; int ret; - if (sta) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - - wcid = &msta_link->wcid; - link_sta = &sta->deflink; - } - - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink, wcid, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec basic */ - mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta, + mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, link_conf, link_sta, conn_state, newly); if (conn_state == CONN_STATE_DISCONNECT) goto out; /* starec hdr trans */ - mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, wcid); + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, link_conf->vif, wcid); /* starec tx proc */ mt7996_mcu_sta_tx_proc_tlv(skb); /* tag order is in accordance with firmware dependency. */ - if (sta) { + if (link_sta) { /* starec hdrt mode */ mt7996_mcu_sta_hdrt_tlv(dev, skb); - /* starec bfer */ - mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta); + if (conn_state == CONN_STATE_CONNECT) { + /* starec bfer */ + mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, + link); + /* starec bfee */ + mt7996_mcu_sta_bfee_tlv(dev, skb, link_conf, link_sta, + link); + } /* starec ht */ - mt7996_mcu_sta_ht_tlv(skb, sta); + mt7996_mcu_sta_ht_tlv(skb, link_sta); /* starec vht */ - mt7996_mcu_sta_vht_tlv(skb, sta); + mt7996_mcu_sta_vht_tlv(skb, link_sta); /* starec uapsd */ - mt76_connac_mcu_sta_uapsd(skb, vif, sta); + mt76_connac_mcu_sta_uapsd(skb, link_conf->vif, sta); /* starec amsdu */ - mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta); + mt7996_mcu_sta_amsdu_tlv(dev, skb, link_conf->vif, link_sta, + msta_link); /* starec he */ - mt7996_mcu_sta_he_tlv(skb, sta); + mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* starec he 6g*/ - mt7996_mcu_sta_he_6g_tlv(skb, sta); + mt7996_mcu_sta_he_6g_tlv(skb, link_sta); /* starec eht */ - mt7996_mcu_sta_eht_tlv(skb, sta); + mt7996_mcu_sta_eht_tlv(skb, link_sta); /* starec muru */ - mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta); - /* starec bfee */ - mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta); + mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta); } - ret = mt7996_mcu_add_group(dev, vif, sta); + ret = mt7996_mcu_add_group(dev, link_conf->vif, sta); if (ret) { dev_kfree_skb(skb); return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index cf37baa91a8b..0dd9d798541f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -572,9 +572,12 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, 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, int enable); -int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct mt76_vif_link *mlink, - struct ieee80211_sta *sta, int conn_state, bool newly); +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly); int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, bool add); From 08847ec1f7460d0b43744aeec2773a98391070da Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 404/465] wifi: mt76: mt7996: Rely on mt7996_vif_link in mt7996_mcu_twt_agrt_update signature JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7854cc94ec353ee501074637a7caf87a49cff35b Author: Shayne Chen Date: Tue Mar 11 18:45:11 2025 +0100 wifi: mt76: mt7996: Rely on mt7996_vif_link in mt7996_mcu_twt_agrt_update signature This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-12-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 8 ++++---- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 776a41ee6a08..301c5e0e308d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2672,7 +2672,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, } flow->tsf = le64_to_cpu(twt_agrt->twt); - if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) + if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow, + MCU_TWT_AGRT_ADD)) goto unlock; setup_cmd = TWT_SETUP_CMD_ACCEPT; @@ -2705,7 +2706,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, return; flow = &msta_link->twt.flow[flowid]; - if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, + if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow, MCU_TWT_AGRT_DELETE)) return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 43e85308132e..e6ebb309a825 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4236,7 +4236,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, #define TWT_AGRT_PROTECT BIT(2) int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, - struct mt7996_vif *mvif, + struct mt7996_vif_link *link, struct mt7996_twt_flow *flow, int cmd) { @@ -4267,12 +4267,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, .len = cpu_to_le16(sizeof(req) - 4), .tbl_idx = flow->table_id, .cmd = cmd, - .own_mac_idx = mvif->deflink.mt76.omac_idx, + .own_mac_idx = link->mt76.omac_idx, .flowid = flow->id, .peer_id = cpu_to_le16(flow->wcid), .duration = flow->duration, - .bss = mvif->deflink.mt76.idx, - .bss_idx = mvif->deflink.mt76.idx, + .bss = link->mt76.idx, + .bss_idx = link->mt76.idx, .start_tsf = cpu_to_le64(flow->tsf), .mantissa = flow->mantissa, .exponent = flow->exp, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 0dd9d798541f..962022c7eec9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -563,7 +563,7 @@ int mt7996_run(struct mt7996_phy *phy); int mt7996_mcu_init(struct mt7996_dev *dev); int mt7996_mcu_init_firmware(struct mt7996_dev *dev); int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, - struct mt7996_vif *mvif, + struct mt7996_vif_link *link, struct mt7996_twt_flow *flow, int cmd); int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, From 5602039dbb36a2ef0a16a9c48c806eefc3c11755 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 405/465] wifi: mt76: mt7996: Rely on mt7996_vif/sta_link in twt teardown JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3c477b7fca1ddfe01ae9029b7c264fd099b05af6 Author: Lorenzo Bianconi Date: Tue Mar 11 18:45:12 2025 +0100 wifi: mt76: mt7996: Rely on mt7996_vif/sta_link in twt teardown This is a preliminary patch to enable MLO for MT7996 driver Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250311-mt7996-mlo-v2-13-31df6972519b@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 7 +++---- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 7 +++++-- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 301c5e0e308d..30bb7238e7df 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2691,10 +2691,10 @@ out: } void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, - struct mt7996_sta *msta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 flowid) { - struct mt7996_sta_link *msta_link = &msta->deflink; struct mt7996_twt_flow *flow; lockdep_assert_held(&dev->mt76.mutex); @@ -2706,8 +2706,7 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, return; flow = &msta_link->twt.flow[flowid]; - if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow, - MCU_TWT_AGRT_DELETE)) + if (mt7996_mcu_twt_agrt_update(dev, link, flow, MCU_TWT_AGRT_DELETE)) return; list_del_init(&flow->list); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 5ab4f08dba06..11ad95e05b94 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1062,7 +1062,8 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, break; case MT76_STA_EVENT_DISASSOC: for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) - mt7996_mac_twt_teardown_flow(dev, msta, i); + mt7996_mac_twt_teardown_flow(dev, link, + msta_link, i); mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link, CONN_STATE_DISCONNECT, @@ -1804,10 +1805,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw, u8 flowid) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_vif_link *link = &msta->vif->deflink; struct mt7996_dev *dev = mt7996_hw_dev(hw); mutex_lock(&dev->mt76.mutex); - mt7996_mac_twt_teardown_flow(dev, msta, flowid); + mt7996_mac_twt_teardown_flow(dev, link, msta_link, flowid); mutex_unlock(&dev->mt76.mutex); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 962022c7eec9..84b9ff707d56 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -709,7 +709,8 @@ void mt7996_mac_dump_work(struct work_struct *work); void mt7996_mac_sta_rc_work(struct work_struct *work); void mt7996_mac_update_stats(struct mt7996_phy *phy); void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, - struct mt7996_sta *msta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 flowid); void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, struct ieee80211_sta *sta, From 130df1b819e26386646bbfe8a3875d0a143351d6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:05 +0200 Subject: [PATCH 406/465] wifi: mt76: mt7996: Update mt7996_mcu_add_rate_ctrl to MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2660fde82f65caefadb5bb53bc5f1cf43fe224ff Author: Shayne Chen Date: Wed Mar 12 12:13:45 2025 +0100 wifi: mt76: mt7996: Update mt7996_mcu_add_rate_ctrl to MLO Update mt7996_mcu_add_rate_ctrl routine and all the called subroutines to support MLO. This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-1-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 9 +- .../net/wireless/mediatek/mt76/mt7996/main.c | 4 +- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 144 +++++++++--------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 15 +- 4 files changed, 96 insertions(+), 76 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 30bb7238e7df..6e0c66ef6021 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2294,10 +2294,15 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | IEEE80211_RC_NSS_CHANGED | IEEE80211_RC_BW_CHANGED)) - mt7996_mcu_add_rate_ctrl(dev, vif, sta, true); + mt7996_mcu_add_rate_ctrl(dev, vif, &vif->bss_conf, + &sta->deflink, + &msta->vif->deflink, + msta_link, true); if (changed & IEEE80211_RC_SMPS_CHANGED) - mt7996_mcu_set_fixed_field(dev, vif, sta, NULL, + mt7996_mcu_set_fixed_field(dev, &sta->deflink, + &msta->vif->deflink, + msta_link, NULL, RATE_PARAM_MMPS_UPDATE); spin_lock_bh(&dev->mt76.sta_poll_lock); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 11ad95e05b94..25dbf086c8ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1046,7 +1046,9 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, if (err) return err; - err = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); + err = mt7996_mcu_add_rate_ctrl(dev, vif, link_conf, + link_sta, link, + msta_link, false); if (err) return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index e6ebb309a825..0cc822a8a679 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -167,11 +167,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, } static void -mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, - const u16 *mask) +mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta, + __le16 *vht_mcs, const u16 *mask) { - u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { switch (mcs_map & 0x3) { @@ -193,13 +193,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, } static void -mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, - const u8 *mask) +mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, + u8 *ht_mcs, const u8 *mask) { - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++) - ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; + ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss]; } static int @@ -1888,18 +1888,18 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, MCU_WM_UNI_CMD(RA), true); } -int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *data, u32 field) +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + void *data, u32 field) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; struct sta_phy_uni *phy = data; struct sta_rec_ra_fixed_uni *ra; struct sk_buff *skb; struct tlv *tlv; - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) @@ -1919,7 +1919,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif ra->phy = *phy; break; case RATE_PARAM_MMPS_UPDATE: - ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); break; default: break; @@ -1931,12 +1931,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif } static int -mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &mvif->deflink.phy->mt76->chandef; - struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask; + struct cfg80211_chan_def *chandef = &link->phy->mt76->chandef; + struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; enum nl80211_band band = chandef->chan->band; struct sta_phy_uni phy = {}; int ret, nrates = 0; @@ -1957,11 +1958,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif } \ } while (0) - if (sta->deflink.he_cap.has_he) { + if (link_sta->he_cap.has_he) { __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); - } else if (sta->deflink.vht_cap.vht_supported) { + } else if (link_sta->vht_cap.vht_supported) { __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); - } else if (sta->deflink.ht_cap.ht_supported) { + } else if (link_sta->ht_cap.ht_supported) { __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); } else { nrates = hweight32(mask->control[band].legacy); @@ -1978,7 +1979,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed single rate */ if (nrates == 1) { - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_MCS); if (ret) return ret; @@ -1987,7 +1989,6 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed GI */ if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI || mask->control[band].he_gi != GENMASK(7, 0)) { - struct mt7996_sta_link *msta_link = (void *)sta->drv_priv; u32 addr; /* firmware updates only TXCMD but doesn't take WTBL into @@ -1995,12 +1996,13 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif * actual txrate hardware sends out. */ addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 7); - if (sta->deflink.he_cap.has_he) + if (link_sta->he_cap.has_he) mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); else mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_GI); if (ret) return ret; @@ -2008,7 +2010,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed HE_LTF */ if (mask->control[band].he_ltf != GENMASK(7, 0)) { - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_HE_LTF); if (ret) return ret; @@ -2019,30 +2022,32 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif static void mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { #define INIT_RCPI 180 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt76_phy *mphy = mvif->deflink.phy->mt76; + struct mt76_phy *mphy = link->phy->mt76; struct cfg80211_chan_def *chandef = &mphy->chandef; - struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask; + 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 sta_rec_ra_uni *ra; struct tlv *tlv; - u32 supp_rate = sta->deflink.supp_rates[band]; - u32 cap = sta->wme ? STA_CAP_WMM : 0; + u32 supp_rate = link_sta->supp_rates[band]; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra_uni *)tlv; ra->valid = true; ra->auto_rate = true; - ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink); + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta); ra->channel = chandef->chan->hw_value; - ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ? - CMD_CBW_320MHZ : sta->deflink.bandwidth; + ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ? + CMD_CBW_320MHZ : link_sta->bandwidth; ra->phy.bw = ra->bw; - ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); if (supp_rate) { supp_rate &= mask->control[band].legacy; @@ -2062,60 +2067,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, } } - if (sta->deflink.ht_cap.ht_supported) { + if (link_sta->ht_cap.ht_supported) { ra->supp_mode |= MODE_HT; - ra->af = sta->deflink.ht_cap.ampdu_factor; - ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + ra->af = link_sta->ht_cap.ampdu_factor; + ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); cap |= STA_CAP_HT; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) cap |= STA_CAP_SGI_20; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) cap |= STA_CAP_SGI_40; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) cap |= STA_CAP_TX_STBC; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) cap |= STA_CAP_RX_STBC; - if (vif->bss_conf.ht_ldpc && - (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + if (link_conf->ht_ldpc && + (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) cap |= STA_CAP_LDPC; - mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, + mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs, mask->control[band].ht_mcs); ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; } - if (sta->deflink.vht_cap.vht_supported) { + if (link_sta->vht_cap.vht_supported) { u8 af; ra->supp_mode |= MODE_VHT; af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, - sta->deflink.vht_cap.cap); + link_sta->vht_cap.cap); ra->af = max_t(u8, ra->af, af); cap |= STA_CAP_VHT; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) cap |= STA_CAP_VHT_SGI_80; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) cap |= STA_CAP_VHT_SGI_160; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) cap |= STA_CAP_VHT_RX_STBC; - if ((vif->type != NL80211_IFTYPE_AP || vif->bss_conf.vht_ldpc) && - (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) && + (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) cap |= STA_CAP_VHT_LDPC; - mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, + mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs, mask->control[band].vht_mcs); } - if (sta->deflink.he_cap.has_he) { + if (link_sta->he_cap.has_he) { ra->supp_mode |= MODE_HE; cap |= STA_CAP_HE; - if (sta->deflink.he_6ghz_capa.capa) - ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + if (link_sta->he_6ghz_capa.capa) + ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); } ra->sta_cap = cpu_to_le32(cap); @@ -2123,16 +2128,17 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi)); } -int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool changed) +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool changed) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; struct sk_buff *skb; int ret; - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) @@ -2143,19 +2149,19 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, * update sta_rec_he here. */ if (changed) - mt7996_mcu_sta_he_tlv(skb, &sta->deflink, &mvif->deflink); + mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* sta_rec_ra accommodates BW, NSS and only MCS range format * i.e 0-{7,8,9} for VHT. */ - mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); if (ret) return ret; - return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta); + return mt7996_mcu_add_rate_ctrl_fixed(dev, link_sta, link, msta_link); } static int diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 84b9ff707d56..a91a50b72218 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -593,16 +593,23 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, struct ieee80211_vif *vif, u32 changed); int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_he_obss_pd *he_obss_pd); -int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool changed); +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool changed); int mt7996_set_channel(struct mt76_phy *mphy); int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag); int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, void *data, u16 version); -int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *data, u32 field); +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + void *data, 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); From 198d05eadd1c20419c64e94c554a2ec192936c5c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 407/465] wifi: mt76: mt7996: Add mt7996_mcu_sta_mld_setup_tlv() and mt7996_mcu_sta_eht_mld_tlv() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 00cef41d9d8f5552c1bf14b507ac8bd6eab0aa8c Author: Shayne Chen Date: Wed Mar 12 12:13:46 2025 +0100 wifi: mt76: mt7996: Add mt7996_mcu_sta_mld_setup_tlv() and mt7996_mcu_sta_eht_mld_tlv() mt7996_mcu_sta_mld_setup_tlv is needed to push MLO configuration to the MCU. This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-2-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 79 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 32 ++++++++ 2 files changed, 111 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 0cc822a8a679..0c1e47f0c73a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2197,6 +2197,80 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, sizeof(req), true); } +static void +mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->valid_links; + unsigned int nlinks = hweight16(links); + struct mld_setup_link *mld_setup_link; + struct sta_rec_mld_setup *mld_setup; + struct mt7996_sta_link *msta_link; + struct ieee80211_vif *vif; + unsigned int link_id; + struct tlv *tlv; + + msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76); + if (!msta_link) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, + sizeof(struct sta_rec_mld_setup) + + sizeof(struct mld_setup_link) * nlinks); + + mld_setup = (struct sta_rec_mld_setup *)tlv; + memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN); + mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); + + if (nlinks > 1) { + link_id = __ffs(links & ~BIT(msta->deflink_id)); + msta_link = mt76_dereference(msta->link[msta->deflink_id], + &dev->mt76); + if (!msta_link) + return; + } + mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx); + mld_setup->link_num = nlinks; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + mld_setup_link = (struct mld_setup_link *)mld_setup->link_info; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + if (!msta_link) + continue; + + mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup_link->bss_idx = link->mt76.idx; + mld_setup_link++; + } +} + +static void +mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct sta_rec_eht_mld *eht_mld; + struct tlv *tlv; + int i; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); + eht_mld = (struct sta_rec_eht_mld *)tlv; + + for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++) + eht_mld->str_cap[i] = 0x7; +} + int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, @@ -2255,6 +2329,11 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, mt7996_mcu_sta_eht_tlv(skb, link_sta); /* starec muru */ mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta); + + if (sta->mlo) { + mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta); + mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta); + } } ret = mt7996_mcu_add_group(dev, link_conf->vif, sta); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index 5fdc47dad28c..2ab6a53bee86 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -625,6 +625,35 @@ struct sta_rec_hdr_trans { u8 mesh; } __packed; +struct sta_rec_mld_setup { + __le16 tag; + __le16 len; + u8 mld_addr[ETH_ALEN]; + __le16 primary_id; + __le16 seconed_id; + __le16 setup_wcid; + u8 link_num; + u8 info; + u8 __rsv[2]; + u8 link_info[]; +} __packed; + +struct sta_rec_eht_mld { + __le16 tag; + __le16 len; + u8 nsep; + u8 __rsv1[2]; + u8 str_cap[__MT_MAX_BAND]; + __le16 eml_cap; + u8 __rsv2[4]; +} __packed; + +struct mld_setup_link { + __le16 wcid; + u8 bss_idx; + u8 __rsv; +} __packed; + struct hdr_trans_en { __le16 tag; __le16 len; @@ -798,6 +827,9 @@ enum { sizeof(struct sta_rec_eht) + \ sizeof(struct sta_rec_hdrt) + \ sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct sta_rec_mld_setup) + \ + sizeof(struct mld_setup_link) * 3 + \ + sizeof(struct sta_rec_eht_mld) + \ sizeof(struct tlv)) #define MT7996_MAX_BEACON_SIZE 1338 From 701bb80ae65d36cc2eee51951c970e82fbfd30b3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 408/465] wifi: mt76: mt7996: Add mt7996_mcu_teardown_mld_sta rouine JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c1d6dd5d03eb504c06bda7ce983d223fc56d774a Author: Shayne Chen Date: Wed Mar 12 12:13:47 2025 +0100 wifi: mt76: mt7996: Add mt7996_mcu_teardown_mld_sta rouine mt7996_mcu_teardown_mld_sta is used to remove MLO configuration from the MCU. This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-3-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 12 +++++++++--- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 18 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 +++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 25dbf086c8ef..642d633b5126 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1017,6 +1017,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum mt76_sta_event ev) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->valid_links; struct ieee80211_link_sta *link_sta; unsigned int link_id; @@ -1067,11 +1068,16 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, mt7996_mac_twt_teardown_flow(dev, link, msta_link, i); - mt7996_mcu_add_sta(dev, link_conf, link_sta, link, - msta_link, CONN_STATE_DISCONNECT, - false); + if (sta->mlo && links == BIT(link_id)) /* last link */ + mt7996_mcu_teardown_mld_sta(dev, link, + msta_link); + else + mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_DISCONNECT, false); msta_link->wcid.sta_disabled = 1; msta_link->wcid.sta = 0; + links = links & ~BIT(link_id); break; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 0c1e47f0c73a..a5522f31c343 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2346,6 +2346,24 @@ out: MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + 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); + + mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); + + return 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_wcid *wcid, struct sk_buff *skb, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index a91a50b72218..c2a2916e0647 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -578,6 +578,9 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, int conn_state, bool newly); +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, bool add); From 029a24e9612bbdf8009bdca586495c450fff9b34 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 409/465] wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f0b0b239b8f36cdc55cf0bc1bac75ec07fc9fef1 Author: Shayne Chen Date: Wed Mar 12 12:13:48 2025 +0100 wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support Update mt7996_mac_write_txwi routine and all the called subroutines to support MLO. This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-4-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 6e0c66ef6021..06cb0e02222c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -730,9 +730,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, u32 val; if (wcid->sta) { - struct ieee80211_sta *sta; + struct ieee80211_sta *sta = wcid_to_sta(wcid); - sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); wmm = sta->wme; } @@ -759,7 +758,9 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, static void mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, - struct sk_buff *skb, struct ieee80211_key_conf *key) + struct sk_buff *skb, + struct ieee80211_key_conf *key, + struct mt76_wcid *wcid) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; @@ -767,6 +768,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + u16 seqno = le16_to_cpu(sc); u8 fc_type, fc_stype; u32 val; @@ -816,9 +818,13 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (info->flags & IEEE80211_TX_CTL_INJECTED) { - u16 seqno = le16_to_cpu(sc); + if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + } + if (info->flags & IEEE80211_TX_CTL_INJECTED) { if (ieee80211_is_back_req(hdr->frame_control)) { struct ieee80211_bar *bar; @@ -831,6 +837,19 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(val); txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } + + if (ieee80211_vif_is_mld(info->control.vif) && + (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + + if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && + ieee80211_vif_is_mld(info->control.vif)) { + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); + } + + if (!wcid->sta && ieee80211_is_mgmt(fc)) + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, @@ -846,6 +865,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; struct mt76_vif_link *mlink = NULL; struct mt7996_vif *mvif; + unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -853,13 +873,15 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - if (vif) { - mvif = (struct mt7996_vif *)vif->drv_priv; - if (wcid->offchannel) - mlink = rcu_dereference(mvif->mt76.offchannel_link); - if (!mlink) - mlink = &mvif->deflink.mt76; - } + if (wcid != &dev->mt76.global_wcid) + link_id = wcid->link_id; + else + link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; + if (mvif) + mlink = rcu_dereference(mvif->mt76.link[link_id]); if (mlink) { omac_idx = mlink->omac_idx; @@ -911,7 +933,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, val |= MT_TXD5_TX_STATUS_HOST; txwi[5] = cpu_to_le32(val); - val = MT_TXD6_DIS_MAT | MT_TXD6_DAS; + val = MT_TXD6_DAS; + if (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0) + val |= MT_TXD6_DIS_MAT; + if (is_mt7996(&dev->mt76)) val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1); else if (is_8023 || !ieee80211_is_mgmt(hdr->frame_control)) @@ -923,7 +948,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, if (is_8023) mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid); else - mt7996_mac_write_txwi_80211(dev, txwi, skb, key); + mt7996_mac_write_txwi_80211(dev, txwi, skb, key, wcid); if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { bool mcast = ieee80211_is_data(hdr->frame_control) && @@ -940,6 +965,8 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, } val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW; + if (mcast) + val |= MT_TXD6_DIS_MAT; txwi[6] |= cpu_to_le32(val); txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); } From 594afaadbdc33de39eefc53881a9751d63c6d48f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 410/465] wifi: mt76: mt7996: Rely on wcid_to_sta in mt7996_mac_add_txs_skb() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 19db942418f53959f6d9b6fb60fe43268552fe01 Author: Shayne Chen Date: Wed Mar 12 12:13:49 2025 +0100 wifi: mt76: mt7996: Rely on wcid_to_sta in mt7996_mac_add_txs_skb() This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-5-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 06cb0e02222c..31d27c94b76e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1287,7 +1287,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, struct ieee80211_sta *sta; u8 tid; - sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + sta = wcid_to_sta(wcid); tid = FIELD_GET(MT_TXS0_TID, txs); ieee80211_refresh_tx_agg_session_timer(sta, tid); } From 64c56fe49923a5d96f41ac23f8bd59bfcd096f65 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 411/465] wifi: mt76: mt7996: rework mt7996_rx_get_wcid to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7464b12b7d92b9641d4664735b9f3c3f0b6173d9 Author: Shayne Chen Date: Wed Mar 12 12:13:50 2025 +0100 wifi: mt76: mt7996: rework mt7996_rx_get_wcid to support MLO The wcid idx and band idx in the TXS are sometimes mismatched since the FW will select a transmission link according to a private algorithm. That is, the wcid idx in the TXS would be the one registered by the driver rather than the actual wcid idx used during transmission. However, the band idx in the TXS is the band select for transmission. Therefore, we should get the driver-registered wcid in order to notify the driver the packet has been acked; otherwise, the driver will be unable to match the transmitted packet and its TXS. This is a preliminary patch to enable MLO for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-6-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 31d27c94b76e..0e5b6f0423d9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -53,29 +53,48 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = { }; static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, - u16 idx, bool unicast) + u16 idx, u8 band_idx) { struct mt7996_sta_link *msta_link; struct mt7996_sta *msta; + struct mt7996_vif *mvif; struct mt76_wcid *wcid; + int i; if (idx >= ARRAY_SIZE(dev->mt76.wcid)) return NULL; wcid = rcu_dereference(dev->mt76.wcid[idx]); - if (unicast || !wcid) - return wcid; - - if (!wcid->sta) + if (!wcid) return NULL; + if (!mt7996_band_valid(dev, band_idx)) + return NULL; + + if (wcid->phy_idx == band_idx) + return wcid; + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); msta = msta_link->sta; - if (!msta || !msta->vif) return NULL; - return &msta->vif->deflink.msta_link.wcid; + mvif = msta->vif; + for (i = 0; i < ARRAY_SIZE(mvif->mt76.link); i++) { + struct mt76_vif_link *mlink; + + mlink = rcu_dereference(mvif->mt76.link[i]); + if (!mlink) + continue; + + if (mlink->band_idx != band_idx) + continue; + + msta_link = rcu_dereference(msta->link[i]); + break; + } + + return &msta_link->wcid; } bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) @@ -483,7 +502,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); - status->wcid = mt7996_rx_get_wcid(dev, idx, unicast); + status->wcid = mt7996_rx_get_wcid(dev, idx, band_idx); if (status->wcid) { struct mt7996_sta_link *msta_link; From f6aec035abc038ff22cb5fa418175d0bfa31b33c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 412/465] wifi: mt76: mt7996: rework mt7996_sta_set_4addr and mt7996_sta_set_decap_offload to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit aa99241833bffd44e604f92b97d7652f300cdce6 Author: Shayne Chen Date: Wed Mar 12 12:13:51 2025 +0100 wifi: mt76: mt7996: rework mt7996_sta_set_4addr and mt7996_sta_set_decap_offload to support MLO Rework mt7996_sta_set_4addr and mt7996_sta_set_decap_offload routines in order to properly support multi-link. This is a preliminary patch to enable MLO for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-7-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 74 ++++++++++++++----- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 11 +-- .../wireless/mediatek/mt76/mt7996/mt7996.h | 3 +- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 642d633b5126..f7703e00712c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1540,19 +1540,37 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool enabled) { - struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enabled) - set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); - else - clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + mutex_lock(&dev->mt76.mutex); - if (!msta_link->wcid.sta) - return; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; - mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + } + + mutex_unlock(&dev->mt76.mutex); } static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, @@ -1560,19 +1578,39 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool enabled) { - struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enabled) - set_bit(MT_WCID_FLAG_HDR_TRANS, &msta_link->wcid.flags); - else - clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta_link->wcid.flags); + mutex_lock(&dev->mt76.mutex); - if (!msta_link->wcid.sta) - return; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; - mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); + + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + } + + mutex_unlock(&dev->mt76.mutex); } static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index a5522f31c343..263eb1f552f0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4465,17 +4465,12 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link; - struct mt7996_sta *msta; struct sk_buff *skb; - msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; - msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link; - - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index c2a2916e0647..f226090bcd02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -749,7 +749,8 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif struct ieee80211_key_conf *key); int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); #ifdef CONFIG_MAC80211_DEBUGFS void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, From eeb8fd739d5ff850b0ab03ebc8da32938734e90c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 413/465] wifi: mt76: mt7996: Add mt7996_sta_link to mt7996_mcu_add_bss_info signature JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c9710b54763b2907740898f2b6032af2bb8369fd Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:52 2025 +0100 wifi: mt76: mt7996: Add mt7996_sta_link to mt7996_mcu_add_bss_info signature This is a preliminary patch to introduce MLO support for MT996 driver. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-8-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 13 ++++++++----- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f7703e00712c..4abbe761b732 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -195,7 +195,8 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (cmd == SET_KEY && !sta && !mlink->mt76.cipher) { mlink->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); - mt7996_mcu_add_bss_info(phy, vif, &vif->bss_conf, &mlink->mt76, true); + mt7996_mcu_add_bss_info(phy, vif, &vif->bss_conf, &mlink->mt76, + &mlink->msta_link, true); } if (cmd == SET_KEY) { @@ -288,7 +289,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mt7996_init_bitrate_mask(vif, link); - mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, true); + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, true); /* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA * interface, since firmware only records BSSID when the entry is new */ @@ -314,7 +315,7 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, CONN_STATE_DISCONNECT, false); - mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, 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); @@ -704,7 +705,8 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, continue; mt7996_mcu_add_bss_info(link->phy, vif, link_conf, - &link->mt76, true); + &link->mt76, &link->msta_link, + true); mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, CONN_STATE_PORT_SECURE, !!(changed & BSS_CHANGED_BSSID)); @@ -740,7 +742,8 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, */ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) || (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) { - mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76, true); + mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76, + &link->msta_link, true); mt7996_mcu_add_sta(dev, info, NULL, link, NULL, CONN_STATE_PORT_SECURE, !!(changed & BSS_CHANGED_BSSID)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 263eb1f552f0..2520feba1607 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1064,7 +1064,8 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int 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, int enable) + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable) { struct mt7996_dev *dev = phy->dev; struct sk_buff *skb; @@ -1081,7 +1082,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, /* bss_basic must be first */ mt7996_mcu_bss_basic_tlv(skb, vif, link_conf, mlink, phy->mt76, - mlink->wcid->idx, enable); + msta_link->wcid.idx, enable); mt7996_mcu_bss_sec_tlv(skb, mlink); if (vif->type == NL80211_IFTYPE_MONITOR) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index f226090bcd02..4d5ab3f4b78e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -571,7 +571,8 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink, bool enable); 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, int enable); + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable); int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, From 18c39eb33e0a98e25ec7bb6edf6044343b4400d4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 414/465] wifi: mt76: mt7996: rework mt7996_set_hw_key to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 01690494f6540702fdb9e83c7f9ad4946aabd963 Author: Shayne Chen Date: Wed Mar 12 12:13:53 2025 +0100 wifi: mt76: mt7996: rework mt7996_set_hw_key to support MLO Modify mt7996_set_hw_key routine to work in a multi-link setup. This is a preliminary patch to enable MLO for MT7996 driver Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-9-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 144 +++++++++++------- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 18 +-- .../wireless/mediatek/mt76/mt7996/mt7996.h | 4 +- 3 files changed, 102 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 4abbe761b732..a845c3e758ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -158,62 +158,101 @@ mt7996_init_bitrate_mask(struct ieee80211_vif *vif, struct mt7996_vif_link *mlin static int mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct mt7996_vif_link *mlink, struct ieee80211_key_conf *key) + struct ieee80211_key_conf *key) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt76_wcid *wcid = &mlink->msta_link.wcid; - struct mt7996_phy *phy; int idx = key->keyidx; - u8 *wcid_keyidx; + unsigned int link_id; + unsigned long links; - phy = mt7996_vif_link_phy(mlink); - if (!phy) - return -EINVAL; + if (key->link_id >= 0) + links = BIT(key->link_id); + else if (sta && sta->valid_links) + links = sta->valid_links; + else if (vif->valid_links) + links = vif->valid_links; + else + links = BIT(0); - if (sta) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + u8 *wcid_keyidx; + int err; - wcid = &msta->deflink.wcid; - if (!wcid->sta) - return -EOPNOTSUPP; - } - wcid_keyidx = &wcid->hw_key_idx; + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; - switch (key->cipher) { - case WLAN_CIPHER_SUITE_AES_CMAC: - case WLAN_CIPHER_SUITE_BIP_CMAC_256: - case WLAN_CIPHER_SUITE_BIP_GMAC_128: - case WLAN_CIPHER_SUITE_BIP_GMAC_256: - if (key->keyidx == 6 || key->keyidx == 7) { - wcid_keyidx = &wcid->hw_key_idx2; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + if (sta) { + struct mt7996_sta *msta; + + msta = (struct mt7996_sta *)sta->drv_priv; + msta_link = mt76_dereference(msta->link[link_id], + &dev->mt76); + if (!msta_link) + continue; + + if (!msta_link->wcid.sta) + return -EOPNOTSUPP; + } else { + msta_link = &link->msta_link; } - break; - default: - break; + wcid_keyidx = &msta_link->wcid.hw_key_idx; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + if (key->keyidx == 6 || key->keyidx == 7) { + wcid_keyidx = &msta_link->wcid.hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + } + break; + default: + break; + } + + if (cmd == SET_KEY && !sta && !link->mt76.cipher) { + struct ieee80211_bss_conf *link_conf; + + link_conf = link_conf_dereference_protected(vif, + link_id); + if (!link_conf) + link_conf = &vif->bss_conf; + + 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 (cmd == SET_KEY) { + *wcid_keyidx = idx; + } else { + if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + continue; + } + + mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key); + + if (key->keyidx == 6 || key->keyidx == 7) { + err = mt7996_mcu_bcn_prot_enable(dev, link, + msta_link, key); + if (err) + return err; + } + + err = mt7996_mcu_add_key(&dev->mt76, vif, key, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &msta_link->wcid, cmd); + if (err) + return err; } - if (cmd == SET_KEY && !sta && !mlink->mt76.cipher) { - mlink->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); - mt7996_mcu_add_bss_info(phy, vif, &vif->bss_conf, &mlink->mt76, - &mlink->msta_link, true); - } - - if (cmd == SET_KEY) { - *wcid_keyidx = idx; - } else { - if (idx == *wcid_keyidx) - *wcid_keyidx = -1; - return 0; - } - - mt76_wcid_key_setup(&dev->mt76, wcid, key); - - if (key->keyidx == 6 || key->keyidx == 7) - return mt7996_mcu_bcn_prot_enable(dev, vif, key); - - return mt7996_mcu_add_key(&dev->mt76, vif, key, - MCU_WMWA_UNI_CMD(STA_REC_UPDATE), wcid, cmd); + return 0; } static void @@ -221,12 +260,10 @@ mt7996_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data) { - struct mt7996_vif_link *mlink = data; - if (sta) return; - WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, mlink, key)); + WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, key)); } int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, @@ -298,7 +335,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, CONN_STATE_PORT_SECURE, true); rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); - ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, link); + ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, NULL); return 0; } @@ -486,7 +523,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_vif_link *mlink = &mvif->deflink; int err; /* The hardware does not support per-STA RX GTK, fallback @@ -521,11 +557,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return -EOPNOTSUPP; } - if (!mt7996_vif_link_phy(mlink)) - return 0; /* defer until after link add */ + if (!mt7996_vif_link_phy(&mvif->deflink)) + return 0; /* defer until after link add */ mutex_lock(&dev->mt76.mutex); - err = mt7996_set_hw_key(hw, cmd, vif, sta, mlink, key); + err = mt7996_set_hw_key(hw, cmd, vif, sta, key); mutex_unlock(&dev->mt76.mutex); return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 2520feba1607..2404569018a0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2430,18 +2430,17 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } -static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, - u8 *pn) +static int mt7996_mcu_get_pn(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 *pn) { #define TSC_TYPE_BIGTK_PN 2 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link = &mvif->deflink.msta_link; struct sta_rec_pn_info *pn_info; struct sk_buff *skb, *rskb; struct tlv *tlv; int ret; - skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2466,10 +2465,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, return 0; } -int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif, +int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, struct ieee80211_key_conf *key) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_mcu_bcn_prot_tlv *bcn_prot; struct sk_buff *skb; struct tlv *tlv; @@ -2478,7 +2478,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif sizeof(struct mt7996_mcu_bcn_prot_tlv); int ret; - skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76, len); + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2486,7 +2486,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv; - ret = mt7996_mcu_get_pn(dev, vif, pn); + ret = mt7996_mcu_get_pn(dev, link, msta_link, pn); if (ret) { dev_kfree_skb(skb); return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 4d5ab3f4b78e..1e79757b7a51 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -746,7 +746,9 @@ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len); int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_key_conf *key, int mcu_cmd, struct mt76_wcid *wcid, enum set_key_cmd cmd); -int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif, +int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, struct ieee80211_key_conf *key); int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, From 7ff9853a77d4b0e943098ac67269a417d8c87337 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:06 +0200 Subject: [PATCH 415/465] wifi: mt76: mt7996: rework mt7996_sta_hw_queue_read to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit cf88e159de3d32aeca223cb6fc953c951479eaf8 Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:54 2025 +0100 wifi: mt76: mt7996: rework mt7996_sta_hw_queue_read to support MLO Extend mt7996_sta_hw_queue_read to support multi-link setup. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-10-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../wireless/mediatek/mt76/mt7996/debugfs.c | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index d453c2fc97e4..4a28db17a287 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -616,29 +616,51 @@ static void mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_dev *dev = msta->vif->deflink.phy->dev; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_vif *mvif = msta->vif; + struct mt7996_dev *dev = mvif->deflink.phy->dev; + struct ieee80211_link_sta *link_sta; struct seq_file *s = data; - u8 ac; + struct ieee80211_vif *vif; + unsigned int link_id; - for (ac = 0; ac < 4; ac++) { - u32 qlen, ctrl, val; - u32 idx = msta_link->wcid.idx >> 5; - u8 offs = msta_link->wcid.idx & GENMASK(4, 0); + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); - ctrl = BIT(31) | BIT(11) | (ac << 24); - val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + rcu_read_lock(); - if (val & BIT(offs)) + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; + u8 ac; + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) continue; - mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx); - qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, - GENMASK(11, 0)); - seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", - sta->addr, msta_link->wcid.idx, - msta->vif->deflink.mt76.wmm_idx, ac, qlen); + msta_link = rcu_dereference(msta->link[link_id]); + if (!msta_link) + continue; + + for (ac = 0; ac < 4; ac++) { + u32 idx = msta_link->wcid.idx >> 5, qlen, ctrl, val; + u8 offs = msta_link->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, + MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta_link->wcid.idx, + mlink->wmm_idx, ac, qlen); + } } + + rcu_read_unlock(); } static int From 74488165ab221c16f314877aafc921bee70a4b63 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 416/465] wifi: mt76: mt7996: remove mt7996_mac_enable_rtscts() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 601e4adc6520bfd1e3a8ad26139f22cc0ccb2e58 Author: Shayne Chen Date: Wed Mar 12 12:13:55 2025 +0100 wifi: mt76: mt7996: remove mt7996_mac_enable_rtscts() It is controlled by FW, also, driver should not directly write WTBL to prevent WTBL overwritten issues. Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-11-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 14 -------------- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 --- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 -- 3 files changed, 19 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 0e5b6f0423d9..fb64a1e25fbc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -217,20 +217,6 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rcu_read_unlock(); } -void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, - struct ieee80211_vif *vif, bool enable) -{ - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta_link *msta_link = &mvif->deflink.msta_link; - u32 addr; - - addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 5); - if (enable) - mt76_set(dev, addr, BIT(5)); - else - mt76_clear(dev, addr, BIT(5)); -} - /* The HW does not translate the mac header to 802.3 for mesh point */ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index a845c3e758ad..8d7ad2dc2d7b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -785,9 +785,6 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, !!(changed & BSS_CHANGED_BSSID)); } - if (changed & BSS_CHANGED_ERP_CTS_PROT) - mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); - if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 1e79757b7a51..8a347dd8e648 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -707,8 +707,6 @@ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); void mt7996_mac_reset_counters(struct mt7996_phy *phy); void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); -void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, - struct ieee80211_vif *vif, bool enable); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, From d2de1cfa3b70ecaf0dc9a45781022d66839a39fc Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 417/465] wifi: mt76: mt7996: rework mt7996_mac_sta_rc_work to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0762bdd30279facd1ccf681d38b27ffe9dd2fa17 Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:56 2025 +0100 wifi: mt76: mt7996: rework mt7996_mac_sta_rc_work to support MLO Rework mt7996_mac_sta_rc_work routine in order to support multi-link setup. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Shayne Chen Signed-off-by: Shayne Chen Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-12-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 40 ++++++++++++++----- .../net/wireless/mediatek/mt76/mt7996/main.c | 30 +++++++++----- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index fb64a1e25fbc..03b4b4e2d6e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2300,13 +2300,20 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy) void mt7996_mac_sta_rc_work(struct work_struct *work) { struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + 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_vif_link *mlink; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct mt7996_sta *msta; - u32 changed; + struct mt7996_vif *mvif; LIST_HEAD(list); + u32 changed; + u8 link_id; + rcu_read_lock(); spin_lock_bh(&dev->mt76.sta_poll_lock); list_splice_init(&dev->sta_rc_list, &list); @@ -2319,21 +2326,35 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) msta_link->changed = 0; spin_unlock_bh(&dev->mt76.sta_poll_lock); + sta = wcid_to_sta(&msta_link->wcid); + link_id = msta_link->wcid.link_id; msta = msta_link->sta; - sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); - vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + mvif = msta->vif; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) + continue; + + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) + continue; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + continue; + + link = (struct mt7996_vif_link *)mlink; if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | IEEE80211_RC_NSS_CHANGED | IEEE80211_RC_BW_CHANGED)) - mt7996_mcu_add_rate_ctrl(dev, vif, &vif->bss_conf, - &sta->deflink, - &msta->vif->deflink, - msta_link, true); + mt7996_mcu_add_rate_ctrl(dev, vif, link_conf, + link_sta, link, msta_link, + true); if (changed & IEEE80211_RC_SMPS_CHANGED) - mt7996_mcu_set_fixed_field(dev, &sta->deflink, - &msta->vif->deflink, + mt7996_mcu_set_fixed_field(dev, link_sta, link, msta_link, NULL, RATE_PARAM_MMPS_UPDATE); @@ -2341,6 +2362,7 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) } spin_unlock_bh(&dev->mt76.sta_poll_lock); + rcu_read_unlock(); } void mt7996_mac_work(struct work_struct *work) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 8d7ad2dc2d7b..4d3098e47dbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -304,6 +304,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, INIT_LIST_HEAD(&msta_link->rc_list); msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_conf->link_id; msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; mt76_wcid_init(&msta_link->wcid, band_idx); @@ -1521,29 +1522,39 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, } } -static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) +static void mt7996_link_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_sta_link *msta_link; u32 *changed = data; + rcu_read_lock(); + + msta_link = rcu_dereference(msta->link[msta->deflink_id]); + if (!msta_link) + goto out; + spin_lock_bh(&dev->mt76.sta_poll_lock); + msta_link->changed |= *changed; if (list_empty(&msta_link->rc_list)) list_add_tail(&msta_link->rc_list, &dev->sta_rc_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); +out: + rcu_read_unlock(); } -static void mt7996_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta, - u32 changed) +static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct ieee80211_sta *sta = link_sta->sta; - mt7996_sta_rc_work(&changed, sta); + mt7996_link_rate_ctrl_update(&changed, sta); ieee80211_queue_work(hw, &dev->rc_work); } @@ -1565,7 +1576,8 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT * then multiple MCS setting (MCS 4,5,6) is not supported. */ - ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed); + ieee80211_iterate_stations_atomic(hw, mt7996_link_rate_ctrl_update, + &changed); ieee80211_queue_work(hw, &dev->rc_work); return 0; @@ -2023,7 +2035,7 @@ const struct ieee80211_ops mt7996_ops = { .link_info_changed = mt7996_link_info_changed, .sta_state = mt7996_sta_state, .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, - .link_sta_rc_update = mt7996_sta_rc_update, + .link_sta_rc_update = mt7996_link_sta_rc_update, .set_key = mt7996_set_key, .ampdu_action = mt7996_ampdu_action, .set_rts_threshold = mt7996_set_rts_threshold, From 03f8578c660961caaba125abc55f9c34388c6e02 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 418/465] wifi: mt76: mt7996: rework mt7996_mac_sta_poll to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 19a2239529c177a717146203ebc29db32221a214 Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:57 2025 +0100 wifi: mt76: mt7996: rework mt7996_mac_sta_poll to support MLO Rework mt7996_mac_sta_poll routine in order to support multi-link setup. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-13-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 03b4b4e2d6e2..252433cbe00a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -123,10 +123,12 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) [IEEE80211_AC_VO] = 6 }; struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; struct ieee80211_sta *sta; struct mt7996_sta *msta; u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; LIST_HEAD(sta_poll_list); + struct mt76_wcid *wcid; int i; spin_lock_bh(&dev->mt76.sta_poll_lock); @@ -150,10 +152,11 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) struct mt7996_sta_link, wcid.poll_list); msta = msta_link->sta; - list_del_init(&msta_link->wcid.poll_list); + wcid = &msta_link->wcid; + list_del_init(&wcid->poll_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); - idx = msta_link->wcid.idx; + idx = wcid->idx; /* refresh peer's airtime reporting */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); @@ -181,7 +184,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) sizeof(msta_link->airtime_ac)); } - if (!msta_link->wcid.sta) + if (!wcid->sta) continue; sta = container_of((void *)msta, struct ieee80211_sta, @@ -207,8 +210,15 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rssi[2] = to_rssi(GENMASK(23, 16), val); rssi[3] = to_rssi(GENMASK(31, 14), val); - msta_link->ack_signal = - mt76_rx_signal(msta->vif->deflink.phy->mt76->antenna_mask, rssi); + mlink = rcu_dereference(msta->vif->mt76.link[wcid->link_id]); + if (mlink) { + struct mt76_phy *mphy = mt76_vif_link_phy(mlink); + + if (mphy) + msta_link->ack_signal = + mt76_rx_signal(mphy->antenna_mask, + rssi); + } ewma_avg_signal_add(&msta_link->avg_ack_signal, -msta_link->ack_signal); From 678b357c936880d312ac9df8ce84b5aaf78f8bed Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 419/465] wifi: mt76: mt7996: rework mt7996_update_mu_group to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5c1fa8b219c8dac5e4a0b0ade771bb9c285f00bd Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:58 2025 +0100 wifi: mt76: mt7996: rework mt7996_update_mu_group to support MLO Rework mt7996_update_mu_group routine in order to support multi-link setup. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-14-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 4d3098e47dbb..2bf6453975ae 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -700,12 +700,11 @@ mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf, } static void -mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +mt7996_update_mu_group(struct ieee80211_hw *hw, struct mt7996_vif_link *link, struct ieee80211_bss_conf *info) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - u8 band = mvif->deflink.mt76.band_idx; + u8 band = link->mt76.band_idx; u32 *mu; mu = (u32 *)info->mu_group.membership; @@ -831,7 +830,7 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7996_mcu_beacon_inband_discov(dev, vif, changed); if (changed & BSS_CHANGED_MU_GROUPS) - mt7996_update_mu_group(hw, vif, info); + mt7996_update_mu_group(hw, link, info); if (changed & BSS_CHANGED_TXPOWER && info->txpower != phy->txpower) { From a4f4972a00add08c004ec230363474c06d2f126a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 420/465] wifi: mt76: mt7996: rework mt7996_net_fill_forward_path to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 856825941dcc912f1b67f2f45d3ccfd3d861ab89 Author: Lorenzo Bianconi Date: Wed Mar 12 12:13:59 2025 +0100 wifi: mt76: mt7996: rework mt7996_net_fill_forward_path to support MLO Rework mt7996_net_fill_forward_path routine in order to support multi-link setup. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-15-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 2bf6453975ae..4a33979ee853 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1973,13 +1973,26 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - struct mt7996_vif_link *mlink = &mvif->deflink; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct mt76_vif_link *mlink; struct mt7996_phy *phy; - phy = mt7996_vif_link_phy(mlink); + mlink = rcu_dereference(mvif->mt76.link[msta->deflink_id]); + if (!mlink) + return -EIO; + + msta_link = rcu_dereference(msta->link[msta->deflink_id]); + if (!msta_link) + return -EIO; + + if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA) + return -EIO; + + link = (struct mt7996_vif_link *)mlink; + phy = mt7996_vif_link_phy(link); if (!phy) return -ENODEV; @@ -1989,13 +2002,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, if (!mtk_wed_device_active(wed)) return -ENODEV; - if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA) - return -EIO; - path->type = DEV_PATH_MTK_WDMA; path->dev = ctx->dev; path->mtk_wdma.wdma_idx = wed->wdma_idx; - path->mtk_wdma.bss = mvif->deflink.mt76.idx; + path->mtk_wdma.bss = mlink->idx; path->mtk_wdma.queue = 0; path->mtk_wdma.wcid = msta_link->wcid.idx; From bba2fd93570d882c048138fbd44febc7319b0dbb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 421/465] wifi: mt76: mt7996: rework mt7996_mcu_add_obss_spr to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 2bd378d6b5da8c3c2b03f9942df03fa26e177c18 Author: Shayne Chen Date: Wed Mar 12 12:14:00 2025 +0100 wifi: mt76: mt7996: rework mt7996_mcu_add_obss_spr to support MLO Rework mt7996_mcu_add_obss_spr routine in order to support multi-link setup. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-16-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 11 ++++++----- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 4a33979ee853..eeb856984298 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -807,7 +807,7 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7996_mcu_set_tx(dev, vif, info); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); + mt7996_mcu_add_obss_spr(phy, link, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) { if ((vif->type == NL80211_IFTYPE_AP && @@ -1254,6 +1254,7 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mtxq = (struct mt76_txq *)txq->drv_priv; mutex_lock(&dev->mt76.mutex); + switch (action) { case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid, ssn, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 2404569018a0..dd8cabe6552a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4193,12 +4193,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, } static int -mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif, +mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = phy->dev; - u8 omac = mvif->deflink.mt76.omac_idx; + u8 omac = link->mt76.omac_idx; struct { u8 band_idx; u8 __rsv[3]; @@ -4270,7 +4270,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, sizeof(req), true); } -int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { int ret; @@ -4304,7 +4305,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, return ret; /* Set SR prohibit */ - ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + ret = mt7996_mcu_set_obss_spr_siga(phy, link, he_obss_pd); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 8a347dd8e648..45280f8f34fc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -595,7 +595,8 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, struct ieee80211_vif *vif, u32 changed); -int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd); int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, From 8b321561a9fe11b13304d528f3fc2cd7a65434ed Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 422/465] wifi: mt76: mt7996: rework mt7996_mcu_beacon_inband_discov to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c92fc81ba9e76973f4e5345183771af4d87addd8 Author: Shayne Chen Date: Wed Mar 12 12:14:01 2025 +0100 wifi: mt76: mt7996: rework mt7996_mcu_beacon_inband_discov to support MLO Rework mt7996_mcu_beacon_inband_discov routine in order to support multi-link setup. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-17-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 19 ++++++++++--------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index eeb856984298..bcca1850d471 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -827,7 +827,7 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | BSS_CHANGED_FILS_DISCOVERY)) - mt7996_mcu_beacon_inband_discov(dev, vif, changed); + mt7996_mcu_beacon_inband_discov(dev, info, link, changed); if (changed & BSS_CHANGED_MU_GROUPS) mt7996_update_mu_group(hw, link, info); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index dd8cabe6552a..4bd0c16500ab 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2717,13 +2717,14 @@ out: } int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, - struct ieee80211_vif *vif, u32 changed) + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed) { #define OFFLOAD_TX_MODE_SU BIT(0) #define OFFLOAD_TX_MODE_MU BIT(1) + struct ieee80211_vif *vif = link_conf->vif; struct ieee80211_hw *hw = mt76_hw(dev); - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + 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; @@ -2740,21 +2741,21 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, chandef = &phy->mt76->chandef; band = chandef->chan->band; - if (vif->bss_conf.nontransmitted) + if (link_conf->nontransmitted) return 0; - rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76, + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, MT7996_MAX_BSS_OFFLOAD_SIZE); if (IS_ERR(rskb)) return PTR_ERR(rskb); if (changed & BSS_CHANGED_FILS_DISCOVERY && - vif->bss_conf.fils_discovery.max_interval) { - interval = vif->bss_conf.fils_discovery.max_interval; + link_conf->fils_discovery.max_interval) { + interval = link_conf->fils_discovery.max_interval; skb = ieee80211_get_fils_discovery_tmpl(hw, vif); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && - vif->bss_conf.unsol_bcast_probe_resp_interval) { - interval = vif->bss_conf.unsol_bcast_probe_resp_interval; + link_conf->unsol_bcast_probe_resp_interval) { + interval = link_conf->unsol_bcast_probe_resp_interval; skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 45280f8f34fc..124a48e2706e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -594,7 +594,8 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, - struct ieee80211_vif *vif, u32 changed); + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed); int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd); From a145277b6f329dccde7722201a163db1e41b1744 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 423/465] wifi: mt76: mt7996: set vif default link_id adding/removing vif links JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a3316d2fc669f585428a3c0086efe01a9547a171 Author: Lorenzo Bianconi Date: Wed Mar 12 12:14:02 2025 +0100 wifi: mt76: mt7996: set vif default link_id adding/removing vif links This info will be consumed by subsequent patches (e.g. in set/get tsf routines). Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-18-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index bcca1850d471..40efba5bcaac 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -271,6 +271,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, 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; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; @@ -338,6 +339,9 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, NULL); + if (mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) + mvif->mt76.deflink_id = link_conf->link_id; + return 0; } @@ -346,6 +350,7 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, 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; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; @@ -359,6 +364,19 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + if (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); phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); @@ -443,6 +461,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw, mt76_vif_init(vif, &mvif->mt76); vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; out: mutex_unlock(&dev->mt76.mutex); From afc59f0c2130f03e5e7017d20f0cc336bed6ab8a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 424/465] wifi: mt76: mt7996: rework set/get_tsf callabcks to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a9384b36a42afc2d715eb74c19a7ee558f09ef82 Author: Shayne Chen Date: Wed Mar 12 12:14:03 2025 +0100 wifi: mt76: mt7996: rework set/get_tsf callabcks to support MLO This is a preliminary patch in order to enable MLO for MT7996 driver. Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-19-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- .../net/wireless/mediatek/mt76/mt7996/main.c | 48 ++++++++++++------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 2 +- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 252433cbe00a..5d90e28e1d07 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2732,7 +2732,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, flow->sched = true; flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow); - curr_tsf = __mt7996_get_tsf(hw, msta->vif); + curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink); div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); flow_tsf = curr_tsf + interval - rem; twt_agrt->twt = cpu_to_le64(flow_tsf); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 40efba5bcaac..8bcb4b8e3ef5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1336,10 +1336,10 @@ mt7996_get_stats(struct ieee80211_hw *hw, return 0; } -u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_phy *phy = link->phy; union { u64 t64; u32 t32[2]; @@ -1351,8 +1351,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) lockdep_assert_held(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; /* TSF software read */ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_READ); @@ -1370,7 +1370,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) u64 ret; mutex_lock(&dev->mt76.mutex); - ret = __mt7996_get_tsf(hw, mvif); + ret = __mt7996_get_tsf(hw, &mvif->deflink); mutex_unlock(&dev->mt76.mutex); return ret; @@ -1382,26 +1382,33 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; union { u64 t64; u32 t32[2]; } tsf = { .t64 = timestamp, }; u16 n; - if (!phy) - return; - mutex_lock(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; + phy = link->phy; + if (!phy) + goto unlock; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); /* TSF software overwrite */ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_WRITE); +unlock: mutex_unlock(&dev->mt76.mutex); } @@ -1411,26 +1418,33 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; union { u64 t64; u32 t32[2]; } tsf = { .t64 = timestamp, }; u16 n; - if (!phy) - return; - mutex_lock(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + phy = link->phy; + if (!phy) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); /* TSF software adjust*/ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_ADJUST); +unlock: mutex_unlock(&dev->mt76.mutex); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 124a48e2706e..815a3c68b872 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -535,7 +535,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, void __iomem *mem_base, u32 device_id); void mt7996_wfsys_reset(struct mt7996_dev *dev); irqreturn_t mt7996_irq_handler(int irq, void *dev_instance); -u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif); +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link); int mt7996_register_device(struct mt7996_dev *dev); void mt7996_unregister_device(struct mt7996_dev *dev); int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, From d57cfa21dbd3c70447d890ddf2925ec89dd23506 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:07 +0200 Subject: [PATCH 425/465] wifi: mt76: mt7996: rework mt7996_ampdu_action to support MLO JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ef3f5941e560320a430c00d359c12a206a36f2c1 Author: Lorenzo Bianconi Date: Wed Mar 12 12:14:04 2025 +0100 wifi: mt76: mt7996: rework mt7996_ampdu_action to support MLO Active/de-active TX/RX BA sessssion for each active links running mt7996_ampdu_action routine. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-20-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 89 ++++++++++++------- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 16 ++-- .../wireless/mediatek/mt76/mt7996/mt7996.h | 5 +- 3 files changed, 64 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 8bcb4b8e3ef5..adeb267b2801 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1259,12 +1259,13 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action = params->action; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct ieee80211_sta *sta = params->sta; - struct ieee80211_txq *txq = sta->txq[params->tid]; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct ieee80211_link_sta *link_sta; u16 tid = params->tid; u16 ssn = params->ssn; struct mt76_txq *mtxq; + unsigned int link_id; int ret = 0; if (!txq) @@ -1274,38 +1275,60 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_lock(&dev->mt76.mutex); - switch (action) { - case IEEE80211_AMPDU_RX_START: - mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid, ssn, - params->buf_size); - ret = mt7996_mcu_add_rx_ba(dev, params, true); - break; - case IEEE80211_AMPDU_RX_STOP: - mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, tid); - ret = mt7996_mcu_add_rx_ba(dev, params, false); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - mtxq->aggr = true; - mtxq->send_bar = false; - ret = mt7996_mcu_add_tx_ba(dev, params, true); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - mtxq->aggr = false; - clear_bit(tid, &msta_link->wcid.ampdu_state); - ret = mt7996_mcu_add_tx_ba(dev, params, false); - break; - case IEEE80211_AMPDU_TX_START: - set_bit(tid, &msta_link->wcid.ampdu_state); - ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - mtxq->aggr = false; - clear_bit(tid, &msta_link->wcid.ampdu_state); - ret = mt7996_mcu_add_tx_ba(dev, params, false); - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid, + ssn, params->buf_size); + ret = mt7996_mcu_add_rx_ba(dev, params, link, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, tid); + ret = mt7996_mcu_add_rx_ba(dev, params, link, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta_link->wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta_link->wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta_link->wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, false); + break; + } + + if (ret) + break; } + + if (action == IEEE80211_AMPDU_TX_STOP_CONT) + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + mutex_unlock(&dev->mt76.mutex); return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 4bd0c16500ab..ddd555942c73 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1160,26 +1160,20 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif, /** starec & wtbl **/ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool enable) { - struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - struct mt7996_vif *mvif = msta->vif; - if (enable && !params->amsdu) msta_link->wcid.amsdu = false; - return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, true); + return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, true); } int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct mt7996_vif_link *link, bool enable) { - struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; - struct mt7996_vif *mvif = msta->vif; - - return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, false); + return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, false); } static void diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 815a3c68b872..43e646ed6094 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -584,10 +584,11 @@ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, struct mt7996_sta_link *msta_link); int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool add); + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool enable); int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool add); + struct mt7996_vif_link *link, bool enable); int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct mt76_vif_link *mlink, struct cfg80211_he_bss_color *he_bss_color); From 9891d543192509c7d6f9a17adae72e69fa613f8c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 426/465] wifi: mt76: mt7996: Update mt7996_tx to MLO support JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 3ce8acb86b6614b9f7af794f119f9627efe6b302 Author: Lorenzo Bianconi Date: Wed Mar 12 12:14:05 2025 +0100 wifi: mt76: mt7996: Update mt7996_tx to MLO support Rework mt7996_tx routine in order to support multi-link setup. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250312-b4-mt7996-mlo-p2-v1-21-015b3d6fd928@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/mediatek/mt76/mt7996/main.c | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index adeb267b2801..91c64e3a0860 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1200,12 +1200,18 @@ static void mt7996_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + rcu_read_lock(); if (vif) { - struct mt7996_vif *mvif; + struct mt7996_vif *mvif = (void *)vif->drv_priv; + struct mt76_vif_link *mlink; - mvif = (struct mt7996_vif *)vif->drv_priv; - wcid = &mvif->deflink.msta_link.wcid; + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (mlink && mlink->wcid) + wcid = mlink->wcid; if (mvif->mt76.roc_phy && (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { @@ -1217,19 +1223,22 @@ static void mt7996_tx(struct ieee80211_hw *hw, } } - if (control->sta) { - struct mt7996_sta_link *msta_link; - - msta_link = (struct mt7996_sta_link *)control->sta->drv_priv; - wcid = &msta_link->wcid; - } - if (!mphy) { ieee80211_free_txskb(hw, skb); - return; + goto unlock; } + if (control->sta) { + struct mt7996_sta *msta = (void *)control->sta->drv_priv; + struct mt7996_sta_link *msta_link; + + msta_link = rcu_dereference(msta->link[link_id]); + if (msta_link) + wcid = &msta_link->wcid; + } mt76_tx(mphy, control->sta, wcid, skb); +unlock: + rcu_read_unlock(); } static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val) From 7cc2f66fe00dfacc52405855ad059f589f692a20 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 427/465] wifi: mt76: mt792x: re-register CHANCTX_STA_CSA only for the mt7921 series JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 06e70003d88218675c566584dd76867fcb39706d Author: Ming Yen Hsieh Date: Thu Mar 13 13:40:44 2025 +0800 wifi: mt76: mt792x: re-register CHANCTX_STA_CSA only for the mt7921 series CSA is currently not supported on mt7925, so CSA is only registered for the mt7921 series Cc: stable@vger.kernel.org Fixes: 8aa2f59260eb ("wifi: mt76: mt7921: introduce CSA support") Signed-off-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250313054044.2638837-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 8799627f6292..0f7806f6338d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -665,7 +665,8 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, CHANCTX_STA_CSA); + if (is_mt7921(&dev->mt76)) + ieee80211_hw_set(hw, CHANCTX_STA_CSA); if (dev->pm.enable) ieee80211_hw_set(hw, CONNECTION_MONITOR); From 2794aa1cecab5f637e1dc1c094dedd8138f54a70 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 428/465] wifi: mt76: mt76x2u: add TP-Link TL-WDN6200 ID to device table JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 06cccc2ebbe6c8a20f714f3a0ff3ff489d3004bb Author: Icenowy Zheng Date: Mon Mar 17 18:22:35 2025 +0800 wifi: mt76: mt76x2u: add TP-Link TL-WDN6200 ID to device table The TP-Link TL-WDN6200 "Driverless" version cards use a MT7612U chipset. Add the USB ID to mt76x2u driver. Signed-off-by: Icenowy Zheng Link: https://patch.msgid.link/20250317102235.1421726-1-uwu@icenowy.me Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 3747f9ed31e6..84ef80ab4afb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -22,6 +22,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */ { USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */ { USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */ + { USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */ { }, }; From d918145986cd76fd1c75af8d5cf4612dfa5a1154 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 429/465] wifi: mt76: mt7996: fix locking in mt7996_mac_sta_rc_work() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 1794d7ab34d2221ac7eb921b171e75b856e10561 Author: Johannes Berg Date: Wed Mar 19 19:44:25 2025 +0100 wifi: mt76: mt7996: fix locking in mt7996_mac_sta_rc_work() The 'continue' statements need to be under spinlock, since the spinlock needs to be held as a loop invariant. Fixes: 0762bdd30279 ("wifi: mt76: mt7996: rework mt7996_mac_sta_rc_work to support MLO") Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 5d90e28e1d07..5d987bf4ea74 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2334,7 +2334,6 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) changed = msta_link->changed; msta_link->changed = 0; - spin_unlock_bh(&dev->mt76.sta_poll_lock); sta = wcid_to_sta(&msta_link->wcid); link_id = msta_link->wcid.link_id; @@ -2354,6 +2353,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) if (!link_conf) continue; + spin_unlock_bh(&dev->mt76.sta_poll_lock); + link = (struct mt7996_vif_link *)mlink; if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | From afb70705c9a757b4bfab7ec532c5caed0f6251db Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 430/465] wifi: iwlwifi: mld: reduce scope for uninitialized variable JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 99f201a9a7d59d95da75695c41b3baf72c39efd3 Author: Yedidya Benshimol Date: Tue Apr 1 06:45:54 2025 +0300 wifi: iwlwifi: mld: reduce scope for uninitialized variable After resuming from D3, keeping the connection or disconnecting isn't relevant for the case of netdetect. Reduce the scope of the keep_connection indicator to wowlan only. Fixes: d1e879ec600f9 ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Yedidya Benshimol Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250401064530.769f76a9ad6e.I69e8f194997eb3a20e40d27fdc31002d5753d905@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 5a7207accd86..2c6e8ecd93b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1895,7 +1895,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) int link_id; int ret; bool fw_err = false; - bool keep_connection; lockdep_assert_wiphy(mld->wiphy); @@ -1965,7 +1964,7 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data); mld->netdetect = false; } else { - keep_connection = + bool keep_connection = iwl_mld_process_wowlan_status(mld, bss_vif, resume_data.wowlan_status); @@ -1973,11 +1972,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) if (keep_connection) iwl_mld_unblock_emlsr(mld, bss_vif, IWL_MLD_EMLSR_BLOCKED_WOWLAN); + else + ieee80211_resume_disconnect(bss_vif); } - if (!mld->netdetect && !keep_connection) - ieee80211_resume_disconnect(bss_vif); - goto out; err: From d76c75057cdf0596c12e5d486850732a0868db29 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 431/465] wifi: iwlwifi: mld: fix PM_SLEEP -Wundef warning JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 676b902db4766aaec049d6ca4800dfa093599005 Author: Johannes Berg Date: Tue Apr 1 06:45:55 2025 +0300 wifi: iwlwifi: mld: fix PM_SLEEP -Wundef warning Config symbols are not defined if turned off, so need to use #ifdef, not #if. Fixes: d1e879ec600f9 ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Johannes Berg Reviewed-by: Yedidya Ben Shimol Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250401064530.612020bcdaad.I4e885e6646576e29fb236250a1b5038d3f14b08e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/iface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index d1d56b081bf6..ec14d0736cee 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -166,7 +166,7 @@ struct iwl_mld_vif { struct iwl_mld_emlsr emlsr; -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP struct iwl_mld_wowlan_data wowlan_data; #endif #ifdef CONFIG_IWLWIFI_DEBUGFS From c815f2f5faa1fc715c050346f26f8ab50ac65813 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 432/465] iwlwifi: mld: fix building with CONFIG_PM_SLEEP disabled JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 44605365f93518eacfc500665d965e88db4791c2 Author: Arnd Bergmann Date: Tue Mar 25 09:43:30 2025 +0100 iwlwifi: mld: fix building with CONFIG_PM_SLEEP disabled The newly added driver causes multiple build problems when CONFIG_PM_SLEEP is disabled: drivers/net/wireless/intel/iwlwifi/mld/mac80211.c:1982:12: error: 'iwl_mld_resume' defined but not used [-Werror=unused-function] 1982 | static int iwl_mld_resume(struct ieee80211_hw *hw) | ^~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c:1960:1: error: 'iwl_mld_suspend' defined but not used [-Werror=unused-function] 1960 | iwl_mld_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | ^~~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c:1946:13: error: 'iwl_mld_set_wakeup' defined but not used [-Werror=unused-function] 1946 | static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled) | ^~~~~~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c: In function 'iwl_mld_mac80211_start': drivers/net/wireless/intel/iwlwifi/mld/mac80211.c:504:20: error: 'ret' is used uninitialized [-Werror=uninitialized] 504 | if (!in_d3 || ret) { | ^~ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c:478:13: note: 'ret' was declared here 478 | int ret; | ^~~ Hide the unused functions and make sure the 'ret' variable is not used before the initialization. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20250325084340.378724-1-arnd@kernel.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 6851064b82da..0b5bc5abb82d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -501,7 +501,7 @@ int iwl_mld_mac80211_start(struct ieee80211_hw *hw) iwl_mld_restart_cleanup(mld); } - if (!in_d3 || ret) { + if (!in_d3) { ret = iwl_mld_start_fw(mld); if (ret) goto error; @@ -537,7 +537,8 @@ void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) /* if the suspend flow fails the fw is in error. Stop it here, and it * will be started upon wakeup */ - if (!suspend || iwl_mld_no_wowlan_suspend(mld)) + if (!suspend || + (IS_ENABLED(CONFIG_PM_SLEEP) && iwl_mld_no_wowlan_suspend(mld))) iwl_mld_stop_fw(mld); /* HW is stopped, no more coming RX. OTOH, the worker can't run as the @@ -1943,6 +1944,7 @@ static void iwl_mld_sta_rc_update(struct ieee80211_hw *hw, } } +#ifdef CONFIG_PM_SLEEP static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); @@ -1994,6 +1996,7 @@ static int iwl_mld_resume(struct ieee80211_hw *hw) return 0; } +#endif static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta, From 4ab1117232d211b76b51a37352c85c1481217b29 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 433/465] wifi: mac80211: Update skb's control block key in ieee80211_tx_dequeue() JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a104042e2bf6528199adb6ca901efe7b60c2c27f Author: Remi Pommarel Date: Mon Mar 24 17:28:20 2025 +0100 wifi: mac80211: Update skb's control block key in ieee80211_tx_dequeue() The ieee80211 skb control block key (set when skb was queued) could have been removed before ieee80211_tx_dequeue() call. ieee80211_tx_dequeue() already called ieee80211_tx_h_select_key() to get the current key, but the latter do not update the key in skb control block in case it is NULL. Because some drivers actually use this key in their TX callbacks (e.g. ath1{1,2}k_mac_op_tx()) this could lead to the use after free below: BUG: KASAN: slab-use-after-free in ath11k_mac_op_tx+0x590/0x61c Read of size 4 at addr ffffff803083c248 by task kworker/u16:4/1440 CPU: 3 UID: 0 PID: 1440 Comm: kworker/u16:4 Not tainted 6.13.0-ge128f627f404 #2 Hardware name: HW (DT) Workqueue: bat_events batadv_send_outstanding_bcast_packet Call trace: show_stack+0x14/0x1c (C) dump_stack_lvl+0x58/0x74 print_report+0x164/0x4c0 kasan_report+0xac/0xe8 __asan_report_load4_noabort+0x1c/0x24 ath11k_mac_op_tx+0x590/0x61c ieee80211_handle_wake_tx_queue+0x12c/0x1c8 ieee80211_queue_skb+0xdcc/0x1b4c ieee80211_tx+0x1ec/0x2bc ieee80211_xmit+0x224/0x324 __ieee80211_subif_start_xmit+0x85c/0xcf8 ieee80211_subif_start_xmit+0xc0/0xec4 dev_hard_start_xmit+0xf4/0x28c __dev_queue_xmit+0x6ac/0x318c batadv_send_skb_packet+0x38c/0x4b0 batadv_send_outstanding_bcast_packet+0x110/0x328 process_one_work+0x578/0xc10 worker_thread+0x4bc/0xc7c kthread+0x2f8/0x380 ret_from_fork+0x10/0x20 Allocated by task 1906: kasan_save_stack+0x28/0x4c kasan_save_track+0x1c/0x40 kasan_save_alloc_info+0x3c/0x4c __kasan_kmalloc+0xac/0xb0 __kmalloc_noprof+0x1b4/0x380 ieee80211_key_alloc+0x3c/0xb64 ieee80211_add_key+0x1b4/0x71c nl80211_new_key+0x2b4/0x5d8 genl_family_rcv_msg_doit+0x198/0x240 <...> Freed by task 1494: kasan_save_stack+0x28/0x4c kasan_save_track+0x1c/0x40 kasan_save_free_info+0x48/0x94 __kasan_slab_free+0x48/0x60 kfree+0xc8/0x31c kfree_sensitive+0x70/0x80 ieee80211_key_free_common+0x10c/0x174 ieee80211_free_keys+0x188/0x46c ieee80211_stop_mesh+0x70/0x2cc ieee80211_leave_mesh+0x1c/0x60 cfg80211_leave_mesh+0xe0/0x280 cfg80211_leave+0x1e0/0x244 <...> Reset SKB control block key before calling ieee80211_tx_h_select_key() to avoid that. Fixes: bb42f2d13ffc ("mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue") Signed-off-by: Remi Pommarel Link: https://patch.msgid.link/06aa507b853ca385ceded81c18b0a6dd0f081bc8.1742833382.git.repk@triplefau.lt Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0eff35562a31..cf1c4cb115f4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3894,6 +3894,7 @@ begin: * The key can be removed while the packet was queued, so need to call * this here to get the current key. */ + info->control.hw_key = NULL; r = ieee80211_tx_h_select_key(&tx); if (r != TX_CONTINUE) { ieee80211_free_txskb(&local->hw, skb); From aed462f9517c953f6e7f8d2fbf26d60d05a500a4 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 434/465] wifi: mac80211: Purge vif txq in ieee80211_do_stop() JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37794 commit 378677eb8f44621ecc9ce659f7af61e5baa94d81 Author: Remi Pommarel Date: Mon Mar 24 17:28:21 2025 +0100 wifi: mac80211: Purge vif txq in ieee80211_do_stop() After ieee80211_do_stop() SKB from vif's txq could still be processed. Indeed another concurrent vif schedule_and_wake_txq call could cause those packets to be dequeued (see ieee80211_handle_wake_tx_queue()) without checking the sdata current state. Because vif.drv_priv is now cleared in this function, this could lead to driver crash. For example in ath12k, ahvif is store in vif.drv_priv. Thus if ath12k_mac_op_tx() is called after ieee80211_do_stop(), ahvif->ah can be NULL, leading the ath12k_warn(ahvif->ah,...) call in this function to trigger the NULL deref below. Unable to handle kernel paging request at virtual address dfffffc000000001 KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] batman_adv: bat0: Interface deactivated: brbh1337 Mem abort info: ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x04: level 0 translation fault Data abort info: ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [dfffffc000000001] address between user and kernel address ranges Internal error: Oops: 0000000096000004 [#1] SMP CPU: 1 UID: 0 PID: 978 Comm: lbd Not tainted 6.13.0-g633f875b8f1e #114 Hardware name: HW (DT) pstate: 10000005 (nzcV daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : ath12k_mac_op_tx+0x6cc/0x29b8 [ath12k] lr : ath12k_mac_op_tx+0x174/0x29b8 [ath12k] sp : ffffffc086ace450 x29: ffffffc086ace450 x28: 0000000000000000 x27: 1ffffff810d59ca4 x26: ffffff801d05f7c0 x25: 0000000000000000 x24: 000000004000001e x23: ffffff8009ce4926 x22: ffffff801f9c0800 x21: ffffff801d05f7f0 x20: ffffff8034a19f40 x19: 0000000000000000 x18: ffffff801f9c0958 x17: ffffff800bc0a504 x16: dfffffc000000000 x15: ffffffc086ace4f8 x14: ffffff801d05f83c x13: 0000000000000000 x12: ffffffb003a0bf03 x11: 0000000000000000 x10: ffffffb003a0bf02 x9 : ffffff8034a19f40 x8 : ffffff801d05f818 x7 : 1ffffff0069433dc x6 : ffffff8034a19ee0 x5 : ffffff801d05f7f0 x4 : 0000000000000000 x3 : 0000000000000001 x2 : 0000000000000000 x1 : dfffffc000000000 x0 : 0000000000000008 Call trace: ath12k_mac_op_tx+0x6cc/0x29b8 [ath12k] (P) ieee80211_handle_wake_tx_queue+0x16c/0x260 ieee80211_queue_skb+0xeec/0x1d20 ieee80211_tx+0x200/0x2c8 ieee80211_xmit+0x22c/0x338 __ieee80211_subif_start_xmit+0x7e8/0xc60 ieee80211_subif_start_xmit+0xc4/0xee0 __ieee80211_subif_start_xmit_8023.isra.0+0x854/0x17a0 ieee80211_subif_start_xmit_8023+0x124/0x488 dev_hard_start_xmit+0x160/0x5a8 __dev_queue_xmit+0x6f8/0x3120 br_dev_queue_push_xmit+0x120/0x4a8 __br_forward+0xe4/0x2b0 deliver_clone+0x5c/0xd0 br_flood+0x398/0x580 br_dev_xmit+0x454/0x9f8 dev_hard_start_xmit+0x160/0x5a8 __dev_queue_xmit+0x6f8/0x3120 ip6_finish_output2+0xc28/0x1b60 __ip6_finish_output+0x38c/0x638 ip6_output+0x1b4/0x338 ip6_local_out+0x7c/0xa8 ip6_send_skb+0x7c/0x1b0 ip6_push_pending_frames+0x94/0xd0 rawv6_sendmsg+0x1a98/0x2898 inet_sendmsg+0x94/0xe0 __sys_sendto+0x1e4/0x308 __arm64_sys_sendto+0xc4/0x140 do_el0_svc+0x110/0x280 el0_svc+0x20/0x60 el0t_64_sync_handler+0x104/0x138 el0t_64_sync+0x154/0x158 To avoid that, empty vif's txq at ieee80211_do_stop() so no packet could be dequeued after ieee80211_do_stop() (new packets cannot be queued because SDATA_STATE_RUNNING is cleared at this point). Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation") Signed-off-by: Remi Pommarel Link: https://patch.msgid.link/ff7849e268562456274213c0476e09481a48f489.1742833382.git.repk@triplefau.lt Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/iface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b0423046028c..183b63235000 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -659,6 +659,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ieee80211_txq_remove_vlan(local, sdata); + if (sdata->vif.txq) + ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq)); + sdata->bss = NULL; if (local->open_count == 0) From 1705f18310f1117f8534cd6e5602516324c8ccb9 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:08 +0200 Subject: [PATCH 435/465] wifi: iwlwifi: mld: silence uninitialized variable warning JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ff4ec537e48cfb84400f52ad102f6d82fe934580 Author: Dan Carpenter Date: Fri Mar 21 17:36:00 2025 +0300 wifi: iwlwifi: mld: silence uninitialized variable warning The "resp_len" isn't initialized if iwl_dhc_resp_data() fails. Fixes: b611cf6b57a8 ("wifi: iwlwifi: mld: add support for DHC_TOOLS_UMAC_GET_TAS_STATUS command") Signed-off-by: Dan Carpenter Acked-by: Miri Korenblit Link: https://patch.msgid.link/add9c9e2-3b44-4e0a-a4aa-7326f6425baf@stanley.mountain [fix typo in commit message] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 453ce2ba39d1..89d95e9b4f30 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -396,8 +396,8 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf, .data[0] = &cmd, }; struct iwl_dhc_tas_status_resp *resp = NULL; + u32 resp_len = 0; ssize_t pos = 0; - u32 resp_len; u32 status; int ret; From 400b9f352c95b1797f9969af02a0e165295a0527 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 436/465] wifi: brcmfmac: fix memory leak in brcmf_get_module_param JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 9e935c0fe3f806ff700a804c00832f0f340b6061 Author: Abdun Nihaal Date: Sun Mar 30 16:04:24 2025 +0530 wifi: brcmfmac: fix memory leak in brcmf_get_module_param The memory allocated for settings is not freed when brcmf_of_probe fails. Fix that by freeing settings before returning in error path. Fixes: 0ff0843310b7 ("wifi: brcmfmac: Add optional lpo clock enable support") Signed-off-by: Abdun Nihaal Acked-by: Arend van Spriel Link: https://patch.msgid.link/20250330103425.44197-1-abdun.nihaal@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 58d50918dd17..6c86e9e22a32 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -561,8 +561,10 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, if (!found) { /* No platform data for this device, try OF and DMI data */ brcmf_dmi_probe(settings, chip, chiprev); - if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) + if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) { + kfree(settings); return ERR_PTR(-EPROBE_DEFER); + } brcmf_acpi_probe(dev, bus_type, settings); } return settings; From 8862ac59a36a08edf4f05f9ad7d03e1b30f9ad01 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 437/465] wifi: iwlwifi: pcie: set state to no-FW before reset handshake JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 5f05c14e7c198415abe936514a6905f8b545b63b Author: Johannes Berg Date: Fri Apr 11 10:40:54 2025 +0200 wifi: iwlwifi: pcie: set state to no-FW before reset handshake The reset handshake attempts to kill the firmware, and it'll go into a pretty much dead state once we do that. However, if it times out, then we'll attempt to dump the firmware to be able to see why it didn't respond. During this dump, we cannot treat it as if it was still running, since we just tried to kill it, otherwise dumping will attempt to send a DBGC stop command. As this command will time out, we'll go into a reset loop. For now, fix this by setting the trans->state to say firmware isn't running before doing the reset handshake. In the longer term, we should clean up the way this state is handled. It's not entirely clear but it seems likely that this issue was introduced by my rework of the error handling, prior to that it would've been synchronous at that point and (I think) not have attempted to reset since it was already doing down. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219967 Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219968 Fixes: 7391b2a4f7db ("wifi: iwlwifi: rework firmware error handling") Reviewed-by: Miriam Rachel Korenblit Link: https://patch.msgid.link/20250411104054.63aa4f56894d.Ife70cfe997db03f0d07fdef2b164695739a05a63@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 3ece34e30d58..472f26f83ba8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -147,8 +147,14 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) return; if (trans->state >= IWL_TRANS_FW_STARTED && - trans_pcie->fw_reset_handshake) + trans_pcie->fw_reset_handshake) { + /* + * Reset handshake can dump firmware on timeout, but that + * should assume that the firmware is already dead. + */ + trans->state = IWL_TRANS_NO_FW; iwl_trans_pcie_fw_reset_handshake(trans); + } trans_pcie->is_down = true; From 7d1407051d8f2c9e7a892c0007a6d208bd7ef8e2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 438/465] wifi: iwlwifi: mld: Restart firmware on iwl_mld_no_wowlan_resume() error JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 575fe08c221567cdbf63e078baecaeaed08a1d17 Author: Lukas Wunner Date: Fri Apr 11 12:21:05 2025 +0200 wifi: iwlwifi: mld: Restart firmware on iwl_mld_no_wowlan_resume() error Commit 44605365f935 ("iwlwifi: mld: fix building with CONFIG_PM_SLEEP disabled") sought to fix build breakage, but inadvertently introduced a new issue: iwl_mld_mac80211_start() no longer calls iwl_mld_start_fw() after having called iwl_mld_stop_fw() in the error path of iwl_mld_no_wowlan_resume(). Fix it. Fixes: 44605365f935 ("iwlwifi: mld: fix building with CONFIG_PM_SLEEP disabled") Reported-by: Miri Korenblit Closes: https://lore.kernel.org/r/MW5PR11MB58106D6BC6403845C330C7AAA3A22@MW5PR11MB5810.namprd11.prod.outlook.com/ Signed-off-by: Lukas Wunner Acked-by: Miri Korenblit Acked-by: Arnd Bergmann Link: https://patch.msgid.link/d3ba1006a1b72ceb58c593fa62b9bd7c73e2e4ed.1744366815.git.lukas@wunner.de Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 0b5bc5abb82d..99e13cfd1e5f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -475,8 +475,8 @@ static int iwl_mld_mac80211_start(struct ieee80211_hw *hw) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - int ret; bool in_d3 = false; + int ret = 0; lockdep_assert_wiphy(mld->wiphy); @@ -501,7 +501,7 @@ int iwl_mld_mac80211_start(struct ieee80211_hw *hw) iwl_mld_restart_cleanup(mld); } - if (!in_d3) { + if (!in_d3 || ret) { ret = iwl_mld_start_fw(mld); if (ret) goto error; From 6f022849fcb09724f411fb8252ea6053e61bdf64 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 439/465] Revert "wifi: mac80211: Update skb's control block key in ieee80211_tx_dequeue()" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0937cb5f345c79d702b4d0d744e2a2529b551cb2 Author: Johannes Berg Date: Fri Apr 11 16:13:34 2025 +0200 Revert "wifi: mac80211: Update skb's control block key in ieee80211_tx_dequeue()" This reverts commit a104042e2bf6528199adb6ca901efe7b60c2c27f. Since the original bug seems to have been around for years, but a new issue was report with the fix, revert the fix for now. We have a couple of weeks to figure it out for this release, if needed. Reported-by: Bert Karwatzki Closes: https://lore.kernel.org/linux-wireless/20250410215527.3001-1-spasswolf@web.de Fixes: a104042e2bf6 ("wifi: mac80211: Update skb's control block key in ieee80211_tx_dequeue()") Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/tx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cf1c4cb115f4..0eff35562a31 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3894,7 +3894,6 @@ begin: * The key can be removed while the packet was queued, so need to call * this here to get the current key. */ - info->control.hw_key = NULL; r = ieee80211_tx_h_select_key(&tx); if (r != TX_CONTINUE) { ieee80211_free_txskb(&local->hw, skb); From ef944b94bb177b807d1e43d9fe429fca801dc2fa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 440/465] gcc-15: work around sequence-point warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ac71fabf15679fc7bc56c51bc92bd4b626564c37 Author: Linus Torvalds Date: Sun Apr 20 11:30:11 2025 -0700 gcc-15: work around sequence-point warning The C sequence points are complicated things, and gcc-15 has apparently added a warning for the case where an object is both used and modified multiple times within the same sequence point. That's a great warning. Or rather, it would be a great warning, except gcc-15 seems to not really be very exact about it, and doesn't notice that the modification are to two entirely different members of the same object: the array counter and the array entries. So that seems kind of silly. That said, the code that gcc complains about is unnecessarily complicated, so moving the array counter update into a separate statement seems like the most straightforward fix for these warnings: drivers/net/wireless/intel/iwlwifi/mld/d3.c: In function ‘iwl_mld_set_netdetect_info’: drivers/net/wireless/intel/iwlwifi/mld/d3.c:1102:66: error: operation on ‘netdetect_info->n_matches’ may be undefined [-Werror=sequence-point] 1102 | netdetect_info->matches[netdetect_info->n_matches++] = match; | ~~~~~~~~~~~~~~~~~~~~~~~~~^~ drivers/net/wireless/intel/iwlwifi/mld/d3.c:1120:58: error: operation on ‘match->n_channels’ may be undefined [-Werror=sequence-point] 1120 | match->channels[match->n_channels++] = | ~~~~~~~~~~~~~~~~~^~ side note: the code at that second warning is actively buggy, and only works on little-endian machines that don't do strict alignment checks. The code casts an array of integers into an array of unsigned long in order to use our bitmap iterators. That happens to work fine on any sane architecture, but it's still wrong. This does *not* fix that more serious problem. This only splits the two assignments into two statements and fixes the compiler warning. I need to get rid of the new warnings in order to be able to actually do any build testing. Signed-off-by: Linus Torvalds Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 2c6e8ecd93b7..ee99298eebf5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1099,7 +1099,8 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld, if (!match) return; - netdetect_info->matches[netdetect_info->n_matches++] = match; + netdetect_info->matches[netdetect_info->n_matches] = match; + netdetect_info->n_matches++; /* We inverted the order of the SSIDs in the scan * request, so invert the index here. @@ -1116,9 +1117,11 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld, for_each_set_bit(j, (unsigned long *)&matches[i].matching_channels[0], - sizeof(matches[i].matching_channels)) - match->channels[match->n_channels++] = + sizeof(matches[i].matching_channels)) { + match->channels[match->n_channels] = netdetect_cfg->channels[j]->center_freq; + match->n_channels++; + } } } From 70ae4ddc8f38c5646dcad5a1e1a176095730acc7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 441/465] wifi: mac80211: restore monitor for outgoing frames JIRA: https://issues.redhat.com/browse/RHEL-89168 commit abf078c0a322159f5ebe2adaa0cd69dc45b1e710 Author: Johannes Berg Date: Tue Apr 22 21:32:51 2025 +0200 wifi: mac80211: restore monitor for outgoing frames This code was accidentally dropped during the cooked monitor removal, but really should've been simplified instead. Add the simple version back. Fixes: 286e69677065 ("wifi: mac80211: Drop cooked monitor support") Link: https://patch.msgid.link/20250422213251.b3d65fd0f323.Id2a6901583f7af86bbe94deb355968b238f350c6@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/status.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 41ba1d979613..cc9d87126cdf 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -1085,7 +1085,13 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp); - if (status->free_list) + /* + * This is a bit racy but we can avoid a lot of work + * with this test... + */ + if (local->tx_mntrs) + ieee80211_tx_monitor(local, skb, retry_count, status); + else if (status->free_list) list_add_tail(&skb->list, status->free_list); else dev_kfree_skb(skb); From c5c27f90b23ceb20ea827a60f91f32aa2b0d2f96 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 442/465] Revert "wifi: iwlwifi: add support for BE213" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 72bb272541808188c54d0df30b7edce14d979538 Author: Miri Korenblit Date: Sun Apr 20 12:01:49 2025 +0300 Revert "wifi: iwlwifi: add support for BE213" This reverts commit 16a8d9a739430bec9c11eda69226c5a39f3478aa. This device needs commit 75a3313f52b7 ("wifi: iwlwifi: make no_160 more generic"), which has a bug and is being reverted until it is fixed. Since this device wasn't shipped yet it is ok to not support it. Reported-by: Todd Brandt Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220029 Fixes: 16a8d9a73943 ("wifi: iwlwifi: add support for BE213") Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420115541.581160ae3e4b.Icecc46baee8a797c00ad04fab92d7d1114b84829@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 -- .../net/wireless/intel/iwlwifi/iwl-config.h | 1 - .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 12 ++++------- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 21 +++---------------- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 670031fd60dc..59af36960f9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -142,8 +142,6 @@ const struct iwl_cfg_trans_params iwl_sc_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const char iwl_sp_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz"; - const struct iwl_cfg iwl_cfg_sc = { .fw_name_mac = "sc", IWL_DEVICE_SC, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index b9bd89bfdd74..7e4864c00780 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -550,7 +550,6 @@ extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_fm_name[]; extern const char iwl_wh_name[]; -extern const char iwl_sp_name[]; extern const char iwl_gl_name[]; extern const char iwl_mtp_name[]; extern const char iwl_dr_name[]; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index cd1b0048bb6d..08269168b2fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -944,8 +944,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: - if (!trans->reduced_cap_sku && - trans->bw_limit >= 320) { + if (!trans->reduced_cap_sku) { iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |= @@ -1099,18 +1098,15 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - if (trans->bw_limit < 320 || trans->reduced_cap_sku) { + if (trans->reduced_cap_sku) { memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0, sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320)); - iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= - ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; - } - - if (trans->reduced_cap_sku) { iftype_data->eht_cap.eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_mcs_nss_supp.bw._160.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[8] &= ~IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA; + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; } } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 93446c374008..03f7eb46bbc7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1187,13 +1187,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_wh_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - 160, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc, iwl_sp_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, @@ -1207,13 +1202,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_wh_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - 160, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2, iwl_sp_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, @@ -1227,13 +1217,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_wh_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - 160, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2f, iwl_sp_name), /* Dr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, From 31b565537040ace82e79d4547e8513294cf6c1c8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 443/465] Revert "wifi: iwlwifi: make no_160 more generic" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 64dc5d5e341d6145cce65e35bdfa0d6ab9fc0a75 Author: Miri Korenblit Date: Sun Apr 20 12:01:50 2025 +0300 Revert "wifi: iwlwifi: make no_160 more generic" This reverts commit 75a3313f52b7e08e7e73746f69a68c2b7c28bb2b. The indication of the BW limitation in the sub-device ID is not applicable for Killer devices. For those devices, bw_limit will hold a random value, so a matching dev_info might not be found, which leads to a probe failure. Until it is properly fixed, revert this. Reported-by: Todd Brandt Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220029 Fixes: 75a3313f52b7 ("wifi: iwlwifi: make no_160 more generic") Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420115541.36dd3007151e.I66b6b78db09bfea12ae84dd85603cf1583271474@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-config.h | 15 +- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 +- .../net/wireless/intel/iwlwifi/iwl-trans.h | 7 +- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 206 +++++++++--------- .../wireless/intel/iwlwifi/tests/devinfo.c | 15 +- 5 files changed, 118 insertions(+), 129 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 7e4864c00780..acafee538b8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -451,8 +451,11 @@ struct iwl_cfg { #define IWL_CFG_RF_ID_HR 0x7 #define IWL_CFG_RF_ID_HR1 0x4 -#define IWL_CFG_BW_NO_LIM (U16_MAX - 1) -#define IWL_CFG_BW_ANY U16_MAX +#define IWL_CFG_NO_160 0x1 +#define IWL_CFG_160 0x0 + +#define IWL_CFG_NO_320 0x1 +#define IWL_CFG_320 0x0 #define IWL_CFG_CORES_BT 0x0 #define IWL_CFG_CORES_BT_GNSS 0x5 @@ -464,7 +467,7 @@ struct iwl_cfg { #define IWL_CFG_IS_JACKET 0x1 #define IWL_SUBDEVICE_RF_ID(subdevice) ((u16)((subdevice) & 0x00F0) >> 4) -#define IWL_SUBDEVICE_BW_LIM(subdevice) ((u16)((subdevice) & 0x0200) >> 9) +#define IWL_SUBDEVICE_NO_160(subdevice) ((u16)((subdevice) & 0x0200) >> 9) #define IWL_SUBDEVICE_CORES(subdevice) ((u16)((subdevice) & 0x1C00) >> 10) struct iwl_dev_info { @@ -472,10 +475,10 @@ struct iwl_dev_info { u16 subdevice; u16 mac_type; u16 rf_type; - u16 bw_limit; u8 mac_step; u8 rf_step; u8 rf_id; + u8 no_160; u8 cores; u8 cdb; u8 jacket; @@ -489,7 +492,7 @@ extern const unsigned int iwl_dev_info_table_size; const struct iwl_dev_info * iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 bw_limit, u8 cores, u8 rf_step); + u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step); extern const struct pci_device_id iwl_hw_card_ids[]; #endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 08269168b2fa..c381511e9ec6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1094,7 +1094,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs13_max_nss = 0; } - if (trans->bw_limit < 160) + if (trans->no_160) iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 25fb4c50e38b..18698f0bd2e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -876,7 +876,7 @@ struct iwl_txq { * only valid for discrete (not integrated) NICs * @invalid_tx_cmd: invalid TX command buffer * @reduced_cap_sku: reduced capability supported SKU - * @bw_limit: the max bandwidth + * @no_160: device not supporting 160 MHz * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz * @restart: restart worker data * @restart.wk: restart worker @@ -911,8 +911,7 @@ struct iwl_trans { char hw_id_str[52]; u32 sku_id[3]; bool reduced_cap_sku; - u16 bw_limit; - bool step_urm; + u8 no_160:1, step_urm:1; u8 dsbr_urm_fw_dependent:1, dsbr_urm_permanent:1; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 03f7eb46bbc7..6c22c95f28d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -552,17 +552,16 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_hw_card_ids); #define _IWL_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \ - _rf_id, _rf_step, _bw_limit, _cores, _cdb, _cfg, _name) \ + _rf_id, _rf_step, _no_160, _cores, _cdb, _cfg, _name) \ { .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg), \ .name = _name, .mac_type = _mac_type, .rf_type = _rf_type, .rf_step = _rf_step, \ - .bw_limit = _bw_limit, .cores = _cores, .rf_id = _rf_id, \ + .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id, \ .mac_step = _mac_step, .cdb = _cdb, .jacket = IWL_CFG_ANY } #define IWL_DEV_INFO(_device, _subdevice, _cfg, _name) \ _IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_ANY, \ - _cfg, _name) + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ + IWL_CFG_ANY, _cfg, _name) VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { #if IS_ENABLED(CONFIG_IWLMVM) @@ -725,66 +724,66 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_2ac_cfg_soc, iwl9560_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9270_160_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9270_name), _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9162_160_name), _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9162_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9260_160_name), _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9260_2ac_cfg, iwl9260_name), /* Qu with Jf */ @@ -792,132 +791,132 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550i_name), /* Qu C step */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550i_name), /* QuZ */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9462_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550s_name), _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550i_name), /* Qu with Hr */ @@ -925,189 +924,189 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_b0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_b0_hr_b0, iwl_ax203_name), /* Qu C step */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr_b0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_qu_c0_hr_b0, iwl_ax201_name), /* QuZ */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_quz_a0_hr1_b0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_quz_a0_hr_b0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_quz_a0_hr_b0, iwl_ax201_name), /* Ma */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_ma, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_ma, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_ma, iwl_ax231_name), /* So with Hr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax201_name), /* So-F with Hr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax203_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - 80, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax101_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_so_a0_hr_a0, iwl_ax201_name), /* So-F with Gf */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), /* SoF with JF2 */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), /* SoF with JF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), /* So with GF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_CDB, + IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), /* So with JF2 */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), /* So with JF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - 80, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, + IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), #endif /* CONFIG_IWLMVM */ @@ -1116,13 +1115,13 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, @@ -1134,104 +1133,104 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax201_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_wh_name), /* Ga (Gl) */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_NO_LIM, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_gl, iwl_gl_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - 160, IWL_CFG_ANY, IWL_CFG_NO_CDB, + IWL_CFG_NO_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, iwl_cfg_gl, iwl_mtp_name), /* Sc */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2, iwl_wh_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_ax211_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_wh_name), /* Dr */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_dr, iwl_dr_name), /* Br */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BR, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_BW_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_br, iwl_br_name), #endif /* CONFIG_IWLMLD */ }; @@ -1383,7 +1382,7 @@ out: VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info * iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 bw_limit, u8 cores, u8 rf_step) + u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step) { int num_devices = ARRAY_SIZE(iwl_dev_info_table); int i; @@ -1426,15 +1425,8 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device, dev_info->rf_id != rf_id) continue; - /* - * Check that bw_limit have the same "boolean" value since - * IWL_SUBDEVICE_BW_LIM can only return a boolean value and - * dev_info->bw_limit encodes a non-boolean value. - * dev_info->bw_limit == IWL_CFG_BW_NO_LIM must be equal to - * !bw_limit to have a match. - */ - if (dev_info->bw_limit != IWL_CFG_BW_ANY && - (dev_info->bw_limit == IWL_CFG_BW_NO_LIM) == !!bw_limit) + if (dev_info->no_160 != (u8)IWL_CFG_ANY && + dev_info->no_160 != no_160) continue; if (dev_info->cores != (u8)IWL_CFG_ANY && @@ -1572,13 +1564,13 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) CSR_HW_RFID_IS_CDB(iwl_trans->hw_rf_id), CSR_HW_RFID_IS_JACKET(iwl_trans->hw_rf_id), IWL_SUBDEVICE_RF_ID(pdev->subsystem_device), - IWL_SUBDEVICE_BW_LIM(pdev->subsystem_device), + IWL_SUBDEVICE_NO_160(pdev->subsystem_device), IWL_SUBDEVICE_CORES(pdev->subsystem_device), CSR_HW_RFID_STEP(iwl_trans->hw_rf_id)); if (dev_info) { iwl_trans->cfg = dev_info->cfg; iwl_trans->name = dev_info->name; - iwl_trans->bw_limit = dev_info->bw_limit; + iwl_trans->no_160 = dev_info->no_160 == IWL_CFG_NO_160; } #if IS_ENABLED(CONFIG_IWLMVM) diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c index 59e33fbd8361..7361b6d0cdb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c +++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c @@ -2,7 +2,7 @@ /* * KUnit tests for the iwlwifi device info table * - * Copyright (C) 2023-2025 Intel Corporation + * Copyright (C) 2023-2024 Intel Corporation */ #include #include @@ -13,9 +13,9 @@ MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di) { - printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,bw_limit=%d,cores=%.2x\n", + printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,no_160=%d,cores=%.2x\n", pfx, di->device, di->subdevice, di->mac_type, di->mac_step, - di->rf_type, di->cdb, di->jacket, di->rf_id, di->bw_limit, + di->rf_type, di->cdb, di->jacket, di->rf_id, di->no_160, di->cores); } @@ -31,13 +31,8 @@ static void devinfo_table_order(struct kunit *test) di->mac_type, di->mac_step, di->rf_type, di->cdb, di->jacket, di->rf_id, - di->bw_limit != IWL_CFG_BW_NO_LIM, - di->cores, di->rf_step); - if (!ret) { - iwl_pci_print_dev_info("No entry found for: ", di); - KUNIT_FAIL(test, - "No entry found for entry at index %d\n", idx); - } else if (ret != di) { + di->no_160, di->cores, di->rf_step); + if (ret != di) { iwl_pci_print_dev_info("searched: ", di); iwl_pci_print_dev_info("found: ", ret); KUNIT_FAIL(test, From f5de93d59973ec711ee28e4df9f762cd2b637512 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:09 +0200 Subject: [PATCH 444/465] wifi: iwlwifi: mld: properly handle async notification in op mode start JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 4f7a07791944e57ea5f12ce03939e3ad0fd50504 Author: Miri Korenblit Date: Sun Apr 20 09:59:55 2025 +0300 wifi: iwlwifi: mld: properly handle async notification in op mode start From the moment that we have ALIVE, we can receive notification that are handled asynchronously. Some notifications (for example iwl_rfi_support_notif) requires an operational FW. So we need to make sure that they were handled in iwl_op_mode_mld_start before we stop the FW. Flush the async_handlers_wk there to achieve that. Also, if loading the FW in op mode start failed, we need to cancel these notifications, as they are from a dead FW. More than that, not doing so can cause us to access freed memory if async_handlers_wk is executed after ieee80211_free_hw is called. Fix this by canceling all async notifications if a failure occurred in init (after ALIVE). Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.1a8579662437.Ifd77d9c1a29fdd278b0a7bfc2709dd5d5e5efdb1@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/fw.c | 13 ++++++++++--- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 5 +++++ drivers/net/wireless/intel/iwlwifi/mld/mld.h | 5 ----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 62da137e1024..4b083d447ee2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -333,19 +333,22 @@ int iwl_mld_load_fw(struct iwl_mld *mld) ret = iwl_trans_start_hw(mld->trans); if (ret) - return ret; + goto err; ret = iwl_mld_run_fw_init_sequence(mld); if (ret) - return ret; + goto err; ret = iwl_mld_init_mcc(mld); if (ret) - return ret; + goto err; mld->fw_status.running = true; return 0; +err: + iwl_mld_stop_fw(mld); + return ret; } void iwl_mld_stop_fw(struct iwl_mld *mld) @@ -358,6 +361,10 @@ void iwl_mld_stop_fw(struct iwl_mld *mld) iwl_trans_stop_device(mld->trans); + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); + + iwl_mld_purge_async_handlers_list(mld); + mld->fw_status.running = false; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 5e1fcd952da0..3d1be55dd236 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -417,6 +417,11 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, goto free_hw; } + /* We are about to stop the FW. Notifications may require an + * operational FW, so handle them all here before we stop. + */ + wiphy_work_flush(mld->wiphy, &mld->async_handlers_wk); + iwl_mld_stop_fw(mld); wiphy_unlock(mld->wiphy); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 5eceaaf7696d..a4a16da6ebf3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -298,11 +298,6 @@ iwl_cleanup_mld(struct iwl_mld *mld) #endif iwl_mld_low_latency_restart_cleanup(mld); - - /* Empty the list of async notification handlers so we won't process - * notifications from the dead fw after the reconfig flow. - */ - iwl_mld_purge_async_handlers_list(mld); } enum iwl_power_scheme { From 5344990553d6df55f89ea32b907a466fe43dfc4d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 445/465] wifi: iwlwifi: mld: inform trans on init failure JIRA: https://issues.redhat.com/browse/RHEL-89168 commit c155f7c3ad1e70a1e203047b20e3bca235ada207 Author: Miri Korenblit Date: Sun Apr 20 09:59:56 2025 +0300 wifi: iwlwifi: mld: inform trans on init failure If starting the op mode failed, the opmode memory is being freed, so trans->op_mode needs to be NULLified. Otherwise, trans will access already freed memory. Call iwl_trans_op_mode_leave in that case. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.3331d1686556.Ifaf15bdd8ef8c59e04effbd2e7aa0034b30eeacb@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 3d1be55dd236..d64bd7ef93f3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -414,7 +414,7 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, wiphy_unlock(mld->wiphy); rtnl_unlock(); iwl_fw_flush_dumps(&mld->fwrt); - goto free_hw; + goto err; } /* We are about to stop the FW. Notifications may require an @@ -460,7 +460,8 @@ leds_exit: iwl_mld_leds_exit(mld); free_nvm: kfree(mld->nvm_data); -free_hw: +err: + iwl_trans_op_mode_leave(mld->trans); ieee80211_free_hw(mld->hw); return ERR_PTR(ret); } From 67c97aa684a5b8f3b60f7595c1a4fb9d2f77e5b6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 446/465] wifi: iwlwifi: mld: only create debugfs symlink if it does not exist JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d1ee2c1922566257cd6cc4ac7c21974d708fea62 Author: Benjamin Berg Date: Sun Apr 20 09:59:57 2025 +0300 wifi: iwlwifi: mld: only create debugfs symlink if it does not exist When mac80211 switches between non-MLO and MLO it will recreate the debugfs directories. This results in the add_if_debugfs handler being called multiple times. As the convenience symlink is created in the mld debugfs directory and not the vif, it will not be removed by mac80211 when this happens and still exists. Add a check and only create the convenience symlink if we have not yet done so. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.2490696f032a.I74319c7cf18f7e16a3d331cb96e38504b9fbab66@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 5 +++-- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 89d95e9b4f30..93f9f78e4276 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -949,8 +949,9 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); snprintf(target, sizeof(target), "../../../%pd3/iwlmld", vif->debugfs_dir); - mld_vif->dbgfs_slink = - debugfs_create_symlink(name, mld->debugfs_dir, target); + if (!mld_vif->dbgfs_slink) + mld_vif->dbgfs_slink = + debugfs_create_symlink(name, mld->debugfs_dir, target); if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && vif->type == NL80211_IFTYPE_STATION) { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 99e13cfd1e5f..68d97d3b8f02 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -651,6 +651,7 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink); + iwl_mld_vif_from_mac80211(vif)->dbgfs_slink = NULL; #endif iwl_mld_rm_vif(mld, vif); From aafe356fc50e89222440d9eac9563c9c8b94635f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 447/465] wifi: iwlwifi: back off on continuous errors JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d49437a6afc707951e5767ef70c9726b6c05da08 Author: Johannes Berg Date: Sun Apr 20 09:59:58 2025 +0300 wifi: iwlwifi: back off on continuous errors When errors occur repeatedly, the driver shouldn't go into a tight loop trying to reset the device. Implement the backoff I had already defined IWL_TRANS_RESET_DELAY for, but clearly forgotten the implementation of. Fixes: 9a2f13c40c63 ("wifi: iwlwifi: implement reset escalation") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.8816e299efa2.I82cde34e2345a2b33b1f03dbb040f5ad3439a5aa@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-trans.c | 27 ++++++++++++++----- .../net/wireless/intel/iwlwifi/iwl-trans.h | 7 +++-- .../net/wireless/intel/iwlwifi/pcie/trans.c | 3 ++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index c1607b6d0759..6125fe70ce72 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -21,6 +21,7 @@ struct iwl_trans_dev_restart_data { struct list_head list; unsigned int restart_count; time64_t last_error; + bool backoff; char name[]; }; @@ -125,13 +126,20 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) if (!data) return at_least; - if (ktime_get_boottime_seconds() - data->last_error >= + if (!data->backoff && + ktime_get_boottime_seconds() - data->last_error >= IWL_TRANS_RESET_OK_TIME) data->restart_count = 0; index = data->restart_count; - if (index >= ARRAY_SIZE(escalation_list)) + if (index >= ARRAY_SIZE(escalation_list)) { index = ARRAY_SIZE(escalation_list) - 1; + if (!data->backoff) { + data->backoff = true; + return IWL_RESET_MODE_BACKOFF; + } + data->backoff = false; + } return max(at_least, escalation_list[index]); } @@ -140,7 +148,8 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) static void iwl_trans_restart_wk(struct work_struct *wk) { - struct iwl_trans *trans = container_of(wk, typeof(*trans), restart.wk); + struct iwl_trans *trans = container_of(wk, typeof(*trans), + restart.wk.work); struct iwl_trans_reprobe *reprobe; enum iwl_reset_mode mode; @@ -168,6 +177,12 @@ static void iwl_trans_restart_wk(struct work_struct *wk) return; mode = iwl_trans_determine_restart_mode(trans); + if (mode == IWL_RESET_MODE_BACKOFF) { + IWL_ERR(trans, "Too many device errors - delay next reset\n"); + queue_delayed_work(system_unbound_wq, &trans->restart.wk, + IWL_TRANS_RESET_DELAY); + return; + } iwl_trans_inc_restart_count(trans->dev); @@ -227,7 +242,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, trans->dev = dev; trans->num_rx_queues = 1; - INIT_WORK(&trans->restart.wk, iwl_trans_restart_wk); + INIT_DELAYED_WORK(&trans->restart.wk, iwl_trans_restart_wk); return trans; } @@ -271,7 +286,7 @@ int iwl_trans_init(struct iwl_trans *trans) void iwl_trans_free(struct iwl_trans *trans) { - cancel_work_sync(&trans->restart.wk); + cancel_delayed_work_sync(&trans->restart.wk); kmem_cache_destroy(trans->dev_cmd_pool); } @@ -403,7 +418,7 @@ void iwl_trans_op_mode_leave(struct iwl_trans *trans) iwl_trans_pcie_op_mode_leave(trans); - cancel_work_sync(&trans->restart.wk); + cancel_delayed_work_sync(&trans->restart.wk); trans->op_mode = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 18698f0bd2e4..ce4954b0d524 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -961,7 +961,7 @@ struct iwl_trans { struct iwl_dma_ptr invalid_tx_cmd; struct { - struct work_struct wk; + struct delayed_work wk; struct iwl_fw_error_dump_mode mode; bool during_reset; } restart; @@ -1162,7 +1162,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_work(system_unbound_wq, &trans->restart.wk); + queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0); } static inline void iwl_trans_fw_error(struct iwl_trans *trans, @@ -1261,6 +1261,9 @@ enum iwl_reset_mode { IWL_RESET_MODE_RESCAN, IWL_RESET_MODE_FUNC_RESET, IWL_RESET_MODE_PROD_RESET, + + /* keep last - special backoff value */ + IWL_RESET_MODE_BACKOFF, }; void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d52c211adb04..3ff656941888 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2351,7 +2351,8 @@ void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode) struct iwl_trans_pcie_removal *removal; char _msg = 0, *msg = &_msg; - if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY)) + if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY || + mode == IWL_RESET_MODE_BACKOFF)) return; if (test_bit(STATUS_TRANS_DEAD, &trans->status)) From f14ccd84f0f367135b5b990a5f5a052aad02933b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 448/465] wifi: iwlwifi: mld: fix BAID validity check JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 60d418e8540402f4732cce4e8df428e747d79e47 Author: Johannes Berg Date: Sun Apr 20 09:59:59 2025 +0300 wifi: iwlwifi: mld: fix BAID validity check Perhaps IWL_FW_CHECK() is a bit misnamed, but it just returns the value of the inner condition. Therefore, the current code skips the actual function when it has the BAID data and makes it crash later when it doesn't. Fix the logic. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.9c0b84c44c3b.Ied236258854b149960eb357ec61bf3a572503fbc@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/agg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index db9e0f04f4b7..687a9450ac98 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -124,9 +124,9 @@ void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, rcu_read_lock(); baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); - if (!IWL_FW_CHECK(mld, !baid_data, - "Got valid BAID %d but not allocated, invalid BAR release!\n", - baid)) + if (IWL_FW_CHECK(mld, !baid_data, + "Got valid BAID %d but not allocated, invalid BAR release!\n", + baid)) goto out_unlock; if (IWL_FW_CHECK(mld, tid != baid_data->tid || From 92fd43af4d13c89645ac10b417a909c0e9c925f3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 449/465] wifi: iwlwifi: don't warn if the NIC is gone in resume JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 15220a257319ffe3bf95796326dfe0aacdbeb1c4 Author: Emmanuel Grumbach Date: Sun Apr 20 10:00:00 2025 +0300 wifi: iwlwifi: don't warn if the NIC is gone in resume Some BIOSes decide to power gate the WLAN device during S3. Since iwlwifi doesn't expect this, it gets very noisy reporting that the device is no longer available. Wifi is still available because iwlwifi recovers, but it spews scary prints in the log. Fix that by failing gracefully. Fixes: e8bb19c1d590 ("wifi: iwlwifi: support fast resume") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219597 Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.d8d58146c829.I569ca15eaaa774d633038a749cc6ec7448419714@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- .../net/wireless/intel/iwlwifi/iwl-trans.c | 1 - drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 20 ++++++++++++++++--- .../wireless/intel/iwlwifi/pcie/internal.h | 9 +++++---- .../net/wireless/intel/iwlwifi/pcie/trans.c | 13 +++++++++--- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 2 +- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 6125fe70ce72..e7b2e08645ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -555,7 +555,6 @@ void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans) { iwl_trans_pcie_release_nic_access(trans); - __release(nic_access); } IWL_EXPORT_SYMBOL(iwl_trans_release_nic_access); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 6c22c95f28d5..3a605e7c070e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1737,10 +1737,24 @@ static int _iwl_pci_resume(struct device *device, bool restore) * need to reset it completely. * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan, * so assume that any bits there mean that the device is usable. + * For older devices, just try silently to grab the NIC. */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ && - !iwl_read32(trans, CSR_FUNC_SCRATCH)) - device_was_powered_off = true; + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (!iwl_read32(trans, CSR_FUNC_SCRATCH)) + device_was_powered_off = true; + } else { + /* + * bh are re-enabled by iwl_trans_pcie_release_nic_access, + * so re-enable them if _iwl_trans_pcie_grab_nic_access fails. + */ + local_bh_disable(); + if (_iwl_trans_pcie_grab_nic_access(trans, true)) { + iwl_trans_pcie_release_nic_access(trans); + } else { + device_was_powered_off = true; + local_bh_enable(); + } + } if (restore || device_was_powered_off) { trans->state = IWL_TRANS_NO_FW; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 45460f93d24a..114a9195ad7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -558,10 +558,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions, struct device *dev); -bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); -#define _iwl_trans_pcie_grab_nic_access(trans) \ +bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent); +#define _iwl_trans_pcie_grab_nic_access(trans, silent) \ __cond_lock(nic_access_nobh, \ - likely(__iwl_trans_pcie_grab_nic_access(trans))) + likely(__iwl_trans_pcie_grab_nic_access(trans, silent))) void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev); void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev); @@ -1105,7 +1105,8 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); -void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); +void __releases(nic_access_nobh) +iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); /* transport gen 1 exported functions */ void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3ff656941888..debaa0ade349 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2406,7 +2406,7 @@ EXPORT_SYMBOL(iwl_trans_pcie_reset); * This version doesn't disable BHs but rather assumes they're * already disabled. */ -bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) +bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent) { int ret; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2458,6 +2458,11 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) if (unlikely(ret < 0)) { u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL); + if (silent) { + spin_unlock(&trans_pcie->reg_lock); + return false; + } + WARN_ONCE(1, "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", cntrl); @@ -2489,7 +2494,7 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) bool ret; local_bh_disable(); - ret = __iwl_trans_pcie_grab_nic_access(trans); + ret = __iwl_trans_pcie_grab_nic_access(trans, false); if (ret) { /* keep BHs disabled until iwl_trans_pcie_release_nic_access */ return ret; @@ -2498,7 +2503,8 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } -void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) +void __releases(nic_access_nobh) +iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2525,6 +2531,7 @@ void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) * scheduled on different CPUs (after we drop reg_lock). */ out: + __release(nic_access_nobh); spin_unlock_bh(&trans_pcie->reg_lock); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index baf43f50c7bc..eed5246eec9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1021,7 +1021,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, * returned. This needs to be done only on NICs that have * apmg_wake_up_wa set (see above.) */ - if (!_iwl_trans_pcie_grab_nic_access(trans)) + if (!_iwl_trans_pcie_grab_nic_access(trans, false)) return -EIO; /* From 855361ee3a114110bcda7a0fae31b6ac1a9ad823 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 450/465] wifi: iwlwifi: fix the check for the SCRATCH register upon resume JIRA: https://issues.redhat.com/browse/RHEL-89168 commit a17821321a9b42f26e77335cd525fee72dc1cd63 Author: Emmanuel Grumbach Date: Sun Apr 20 10:00:01 2025 +0300 wifi: iwlwifi: fix the check for the SCRATCH register upon resume We can't rely on the SCRATCH register being 0 on platform that power gate the NIC in S3. Even in those platforms, the SCRATCH register is still returning 0x1010000. Make sure that we understand that those platforms have powered off the device. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219597 Fixes: cb347bd29d0d ("wifi: iwlwifi: mvm: fix hibernation") Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250420095642.a7e082ee785c.I9418d76f860f54261cfa89e1f7ac10300904ba40@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index be9e464c9b7b..3ff493e920d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -148,6 +148,7 @@ * during a error FW error. */ #define CSR_FUNC_SCRATCH_INIT_VALUE (0x01010101) +#define CSR_FUNC_SCRATCH_POWER_OFF_MASK 0xFFFF /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH (0x0000000F) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 3a605e7c070e..debeea2b3ae5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1736,11 +1736,13 @@ static int _iwl_pci_resume(struct device *device, bool restore) * Scratch value was altered, this means the device was powered off, we * need to reset it completely. * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan, - * so assume that any bits there mean that the device is usable. + * but not bits [15:8]. So if we have bits set in lower word, assume + * the device is alive. * For older devices, just try silently to grab the NIC. */ if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { - if (!iwl_read32(trans, CSR_FUNC_SCRATCH)) + if (!(iwl_read32(trans, CSR_FUNC_SCRATCH) & + CSR_FUNC_SCRATCH_POWER_OFF_MASK)) device_was_powered_off = true; } else { /* From 2b48644711a5760a13b016eac625c5dab72fefdb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 451/465] wifi: brcm80211: fmac: Add error handling for brcmf_usb_dl_writeimage() JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37990 commit 8e089e7b585d95122c8122d732d1d5ef8f879396 Author: Wentao Liang Date: Tue Apr 22 12:22:02 2025 +0800 wifi: brcm80211: fmac: Add error handling for brcmf_usb_dl_writeimage() The function brcmf_usb_dl_writeimage() calls the function brcmf_usb_dl_cmd() but dose not check its return value. The 'state.state' and the 'state.bytes' are uninitialized if the function brcmf_usb_dl_cmd() fails. It is dangerous to use uninitialized variables in the conditions. Add error handling for brcmf_usb_dl_cmd() to jump to error handling path if the brcmf_usb_dl_cmd() fails and the 'state.state' and the 'state.bytes' are uninitialized. Improve the error message to report more detailed error information. Fixes: 71bb244ba2fd ("brcm80211: fmac: add USB support for bcm43235/6/8 chipsets") Cc: stable@vger.kernel.org # v3.4+ Signed-off-by: Wentao Liang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20250422042203.2259-1-vulab@iscas.ac.cn Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 2821c27f317e..d06c724f63d9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -896,14 +896,16 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) } /* 1) Prepare USB boot loader for runtime image */ - brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); + err = brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); + if (err) + goto fail; rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); /* 2) Check we are in the Waiting state */ if (rdlstate != DL_WAITING) { - brcmf_err("Failed to DL_START\n"); + brcmf_err("Invalid DL state: %u\n", rdlstate); err = -EINVAL; goto fail; } From 9fa24b24ff3b537ad085c58f91b3193f033d8afb Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 452/465] wifi: iwlwifi: restore missing initialization of async_handlers_list JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 175e69e33c66904dfe910c5f43edfe5c95b32f0c Author: Itamar Shalev Date: Wed Apr 23 12:25:02 2025 +0300 wifi: iwlwifi: restore missing initialization of async_handlers_list The initialization of async_handlers_list was accidentally removed in a previous change. This patch restores the missing initialization to ensure proper handler registration. Fixes: 6895d74c11d8 ("wifi: iwlwifi: mld: initialize regulatory early") Signed-off-by: Itamar Shalev Acked-by: Miri Korenblit Link: https://patch.msgid.link/20250423092503.35206-1-itamar.shalev@intel.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index d64bd7ef93f3..b24da50f33eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -75,6 +75,7 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, /* Setup async RX handling */ spin_lock_init(&mld->async_handlers_lock); + INIT_LIST_HEAD(&mld->async_handlers_list); wiphy_work_init(&mld->async_handlers_wk, iwl_mld_async_handlers_wk); From 3a1bcb44dd4f7bdfe76d104eaefc02147a478690 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 453/465] wifi: cfg80211: fix out-of-bounds access during multi-link element defragmentation JIRA: https://issues.redhat.com/browse/RHEL-89168 CVE: CVE-2025-37973 commit 023c1f2f0609218103cbcb48e0104b144d4a16dc Author: Veerendranath Jakkam Date: Thu Apr 24 18:01:42 2025 +0530 wifi: cfg80211: fix out-of-bounds access during multi-link element defragmentation Currently during the multi-link element defragmentation process, the multi-link element length added to the total IEs length when calculating the length of remaining IEs after the multi-link element in cfg80211_defrag_mle(). This could lead to out-of-bounds access if the multi-link element or its corresponding fragment elements are the last elements in the IEs buffer. To address this issue, correctly calculate the remaining IEs length by deducting the multi-link element end offset from total IEs end offset. Cc: stable@vger.kernel.org Fixes: 2481b5da9c6b ("wifi: cfg80211: handle BSS data contained in ML probe responses") Signed-off-by: Veerendranath Jakkam Link: https://patch.msgid.link/20250424-fix_mle_defragmentation_oob_access-v1-1-84412a1743fa@quicinc.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9865f305275d..ddd3a97f6609 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2681,7 +2681,7 @@ cfg80211_defrag_mle(const struct element *mle, const u8 *ie, size_t ielen, /* Required length for first defragmentation */ buf_len = mle->datalen - 1; for_each_element(elem, mle->data + mle->datalen, - ielen - sizeof(*mle) + mle->datalen) { + ie + ielen - mle->data - mle->datalen) { if (elem->id != WLAN_EID_FRAGMENT) break; From 489162ccfbb6c89b34875d0c9882cb011fd8093e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:10 +0200 Subject: [PATCH 454/465] wifi: mac80211: fix the type of status_code for negotiated TID to Link Mapping JIRA: https://issues.redhat.com/browse/RHEL-89168 commit e12a42f64fc3d74872b349eedd47f90c6676b78a Author: Michael-CY Lee Date: Mon May 5 16:19:46 2025 +0800 wifi: mac80211: fix the type of status_code for negotiated TID to Link Mapping The status code should be type of __le16. Fixes: 83e897a961b8 ("wifi: ieee80211: add definitions for negotiated TID to Link map") Fixes: 8f500fbc6c65 ("wifi: mac80211: process and save negotiated TID to Link mapping request") Signed-off-by: Michael-CY Lee Link: https://patch.msgid.link/20250505081946.3927214-1-michael-cy.lee@mediatek.com Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- include/linux/ieee80211.h | 2 +- net/mac80211/mlme.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index ac5bd6674958..571a2ea22b33 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1526,7 +1526,7 @@ struct ieee80211_mgmt { struct { u8 action_code; u8 dialog_token; - u8 status_code; + __le16 status_code; u8 variable[]; } __packed ttlm_res; struct { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d564d7e2aa6..16c217f369b6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7675,6 +7675,7 @@ ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.ttlm_res); int ttlm_max_len = 2 + 1 + sizeof(struct ieee80211_ttlm_elem) + 1 + 2 * 2 * IEEE80211_TTLM_NUM_TIDS; + u16 status_code; skb = dev_alloc_skb(local->tx_headroom + hdr_len + ttlm_max_len); if (!skb) @@ -7697,19 +7698,18 @@ ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, WARN_ON(1); fallthrough; case NEG_TTLM_RES_REJECT: - mgmt->u.action.u.ttlm_res.status_code = - WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING; + status_code = WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING; break; case NEG_TTLM_RES_ACCEPT: - mgmt->u.action.u.ttlm_res.status_code = WLAN_STATUS_SUCCESS; + status_code = WLAN_STATUS_SUCCESS; break; case NEG_TTLM_RES_SUGGEST_PREFERRED: - mgmt->u.action.u.ttlm_res.status_code = - WLAN_STATUS_PREF_TID_TO_LINK_MAPPING_SUGGESTED; + status_code = WLAN_STATUS_PREF_TID_TO_LINK_MAPPING_SUGGESTED; ieee80211_neg_ttlm_add_suggested_map(skb, neg_ttlm); break; } + mgmt->u.action.u.ttlm_res.status_code = cpu_to_le16(status_code); ieee80211_tx_skb(sdata, skb); } @@ -7875,7 +7875,7 @@ void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, * This can be better implemented in the future, to handle request * rejections. */ - if (mgmt->u.action.u.ttlm_res.status_code != WLAN_STATUS_SUCCESS) + if (le16_to_cpu(mgmt->u.action.u.ttlm_res.status_code) != WLAN_STATUS_SUCCESS) __ieee80211_disconnect(sdata); } From 1d01d1e4718f1e3126666d60d48bea59943c135a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 455/465] wifi: iwlwifi: add support for Killer on MTL JIRA: https://issues.redhat.com/browse/RHEL-89168 commit ebedf8b7f05b9c886d68d63025db8d1b12343157 Author: Johannes Berg Date: Tue May 6 21:42:59 2025 +0200 wifi: iwlwifi: add support for Killer on MTL For now, we need another entry for these devices, this will be changed completely for 6.16. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219926 Link: https://patch.msgid.link/20250506214258.2efbdc9e9a82.I31915ec252bd1c74bd53b89a0e214e42a74b6f2e@changeid Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index debeea2b3ae5..00056e76ea3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -588,6 +588,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), + IWL_DEV_INFO(0x7F70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), + IWL_DEV_INFO(0x7F70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name), IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma, iwl_ax411_killer_1690s_name), From eecacf3a45cddcda25abbec4a2243673f9c6275f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 456/465] wifi: mt76: disable napi on driver removal JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 78ab4be549533432d97ea8989d2f00b508fa68d8 Author: Fedor Pchelkin Date: Tue May 6 14:55:39 2025 +0300 wifi: mt76: disable napi on driver removal A warning on driver removal started occurring after commit 9dd05df8403b ("net: warn if NAPI instance wasn't shut down"). Disable tx napi before deleting it in mt76_dma_cleanup(). WARNING: CPU: 4 PID: 18828 at net/core/dev.c:7288 __netif_napi_del_locked+0xf0/0x100 CPU: 4 UID: 0 PID: 18828 Comm: modprobe Not tainted 6.15.0-rc4 #4 PREEMPT(lazy) Hardware name: ASUS System Product Name/PRIME X670E-PRO WIFI, BIOS 3035 09/05/2024 RIP: 0010:__netif_napi_del_locked+0xf0/0x100 Call Trace: mt76_dma_cleanup+0x54/0x2f0 [mt76] mt7921_pci_remove+0xd5/0x190 [mt7921e] pci_device_remove+0x47/0xc0 device_release_driver_internal+0x19e/0x200 driver_detach+0x48/0x90 bus_remove_driver+0x6d/0xf0 pci_unregister_driver+0x2e/0xb0 __do_sys_delete_module.isra.0+0x197/0x2e0 do_syscall_64+0x7b/0x160 entry_SYSCALL_64_after_hwframe+0x76/0x7e Tested with mt7921e but the same pattern can be actually applied to other mt76 drivers calling mt76_dma_cleanup() during removal. Tx napi is enabled in their *_dma_init() functions and only toggled off and on again inside their suspend/resume/reset paths. So it should be okay to disable tx napi in such a generic way. Found by Linux Verification Center (linuxtesting.org). Fixes: 2ac515a5d74f ("mt76: mt76x02: use napi polling for tx cleanup") Cc: stable@vger.kernel.org Signed-off-by: Fedor Pchelkin Tested-by: Ming Yen Hsieh Link: https://patch.msgid.link/20250506115540.19045-1-pchelkin@ispras.ru Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 844af16ee551..35b4ec91979e 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1011,6 +1011,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) int i; mt76_worker_disable(&dev->tx_worker); + napi_disable(&dev->tx_napi); netif_napi_del(&dev->tx_napi); for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { From c4ed41dcd30c0ec0d9924825026ae1637e2e2d0f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 457/465] wifi: mt76: mt7925: fix missing hdr_trans_tlv command for broadcast wtbl JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 0aa8496adda570c2005410a30df963a16643a3dc Author: Ming Yen Hsieh Date: Fri May 9 09:04:20 2025 +0800 wifi: mt76: mt7925: fix missing hdr_trans_tlv command for broadcast wtbl Ensure that the hdr_trans_tlv command is included in the broadcast wtbl to prevent the IPv6 and multicast packet from being dropped by the chip. Cc: stable@vger.kernel.org Fixes: cb1353ef3473 ("wifi: mt76: mt7925: integrate *mlo_sta_cmd and *sta_cmd") Reported-by: Benjamin Xiao Tested-by: Niklas Schnelle Signed-off-by: Ming Yen Hsieh Link: https://lore.kernel.org/lkml/EmWnO5b-acRH1TXbGnkx41eJw654vmCR-8_xMBaPMwexCnfkvKCdlU5u19CGbaapJ3KRu-l3B-tSUhf8CCQwL0odjo6Cd5YG5lvNeB-vfdg=@pm.me/ Link: https://patch.msgid.link/20250509010421.403022-1-mingyen.hsieh@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index e61da76b2097..14b1f603fb62 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1924,14 +1924,14 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy, mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta); } - - mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); } if (!info->enable) { mt7925_mcu_sta_remove_tlv(skb); 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); } return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); From 7686ea0dd0d64869ecd8fa37a6ffe76f3e45c4a0 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 458/465] wifi: mac80211: Set n_channels after allocating struct cfg80211_scan_request JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 82bbe02b2500ef0a62053fe2eb84773fe31c5a0a Author: Kees Cook Date: Fri May 9 11:46:45 2025 -0700 wifi: mac80211: Set n_channels after allocating struct cfg80211_scan_request Make sure that n_channels is set after allocating the struct cfg80211_registered_device::int_scan_req member. Seen with syzkaller: UBSAN: array-index-out-of-bounds in net/mac80211/scan.c:1208:5 index 0 is out of range for type 'struct ieee80211_channel *[] __counted_by(n_channels)' (aka 'struct ieee80211_channel *[]') This was missed in the initial conversions because I failed to locate the allocation likely due to the "sizeof(void *)" not matching the "channels" array type. Reported-by: syzbot+4bcdddd48bb6f0be0da1@syzkaller.appspotmail.com Closes: https://lore.kernel.org/lkml/680fd171.050a0220.2b69d1.045e.GAE@google.com/ Fixes: e3eac9f32ec0 ("wifi: cfg80211: Annotate struct cfg80211_scan_request with __counted_by") Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://patch.msgid.link/20250509184641.work.542-kees@kernel.org Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- net/mac80211/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2130c638bc1f..37ac6991eef4 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1354,10 +1354,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); - local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + - sizeof(void *) * channels, GFP_KERNEL); + local->int_scan_req = kzalloc(struct_size(local->int_scan_req, + channels, channels), + GFP_KERNEL); if (!local->int_scan_req) return -ENOMEM; + local->int_scan_req->n_channels = channels; eth_broadcast_addr(local->int_scan_req->bssid); From e01f0f7b21cadf0916f0e2074081be7d81de98db Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 459/465] wifi: ath12k: Fix memory corruption during MLO multicast tx JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 6f8a27a584b23e9dedefd6cb110dd2587b84a6d4 Author: P Praneesh Date: Wed Apr 2 23:27:14 2025 +0530 wifi: ath12k: Fix memory corruption during MLO multicast tx The struct sk_buff's control buffer is shared by mac80211's struct ieee80211_tx_info and ath12k's struct ath12k_skb_cb. When the driver wants to transmit an skb, it caches all the mac80211-specific information from struct ieee80211_tx_info, then performs a memset on the control buffer before writing the ath12k-specific information using struct ath12k_skb_cb. However, during multicast tx, the key is being filled into the driver data, which overwrites some crucial members like link_id and flags in struct ath12k_skb_cb. This causes invalid information retrieval when the driver accesses these fields during ath12k_dp_tx(). Fix this issue by removing the key filling logic during MLO multicast tx, as it is not used anywhere in the tx path. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: 2f50de725677 ("wifi: ath12k: Add support for MLO Multicast handling in driver") Signed-off-by: P Praneesh Link: https://patch.msgid.link/20250402175714.2667270-1-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/ath/ath12k/mac.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index dfa05f0ee6c9..f1935202f810 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7429,7 +7429,6 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, info_flags); skb_cb = ATH12K_SKB_CB(msdu_copied); - info = IEEE80211_SKB_CB(msdu_copied); skb_cb->link_id = link_id; /* For open mode, skip peer find logic */ @@ -7452,7 +7451,6 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, if (key) { skb_cb->cipher = key->cipher; skb_cb->flags |= ATH12K_SKB_CIPHER_SET; - info->control.hw_key = key; hdr = (struct ieee80211_hdr *)msdu_copied->data; if (!ieee80211_has_protected(hdr->frame_control)) From a2c36e99945ba9adfbf76999b5cd7642a2218711 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 460/465] wifi: mt76: mt7925: Fix logical vs bitwise typo JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 88224119863c39fa67581874e1ba218fa56113b4 Author: Dan Carpenter Date: Fri Mar 21 17:35:40 2025 +0300 wifi: mt76: mt7925: Fix logical vs bitwise typo This was supposed to be & instead of &&. Fixes: f0317215b367 ("wifi: mt76: mt7925: add EHT control support based on the CLC data") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/d323a443-4e81-4064-8563-b62274b53ef4@stanley.mountain Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7925/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 63cb08f4d87c..79639be0d29a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -89,7 +89,7 @@ void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2) } /* Check the last one */ - if (rule->flag && BIT(0)) + if (rule->flag & BIT(0)) break; pos += sizeof(*rule); From 296a7f76f324a53fd42e9703fb10d2bc15bb3566 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 461/465] wifi: mt76: scan: Fix 'mlink' dereferenced before IS_ERR_OR_NULL check JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 7e1fcf687c2fb22ad25cf3fae322a37452f5f560 Author: Feng Jiang Date: Wed Apr 2 14:24:15 2025 +0800 wifi: mt76: scan: Fix 'mlink' dereferenced before IS_ERR_OR_NULL check Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202504011739.HvUKtUUe-lkp@intel.com/ Fixes: 3ba20af886d1 ("wifi: mt76: scan: set vif offchannel link for scanning/roc") Signed-off-by: Feng Jiang Link: https://patch.msgid.link/20250402062415.25434-1-jiangfeng@kylinos.cn Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/channel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index e7b839e74290..cc2d888e3f17 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -302,11 +302,13 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt76_dev *dev = phy->dev; - struct mt76_vif_data *mvif = mlink->mvif; + struct mt76_vif_data *mvif; if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel) return; + mvif = mlink->mvif; + rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); kfree(mlink); From 8141896e3e130f135c3dd1b90ee9da4a106038e6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 462/465] wifi: mt76: mt7996: prevent uninit return in mt7996_mac_sta_add_links JIRA: https://issues.redhat.com/browse/RHEL-89168 commit d9bc625861d490cb76ae8af86fac6f8ab0655a18 Author: Qasim Ijaz Date: Mon Apr 21 12:05:50 2025 +0100 wifi: mt76: mt7996: prevent uninit return in mt7996_mac_sta_add_links If link_conf_dereference_protected() or mt7996_vif_link() or link_sta_dereference_protected() fail the code jumps to the error_unlink label and returns ret which is uninitialised. Fix this by setting err before jumping to error_unlink. Fixes: c7e4fc362443 ("wifi: mt76: mt7996: Update mt7996_mcu_add_sta to MLO support") Fixes: dd82a9e02c05 ("wifi: mt76: mt7996: Rely on mt7996_sta_link in sta_add/sta_remove callbacks") Signed-off-by: Qasim Ijaz Link: https://patch.msgid.link/20250421110550.9839-1-qasdev00@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 91c64e3a0860..70823bbb165c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -998,16 +998,22 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, continue; link_conf = link_conf_dereference_protected(vif, link_id); - if (!link_conf) + if (!link_conf) { + err = -EINVAL; goto error_unlink; + } link = mt7996_vif_link(dev, vif, link_id); - if (!link) + if (!link) { + err = -EINVAL; goto error_unlink; + } link_sta = link_sta_dereference_protected(sta, link_id); - if (!link_sta) + if (!link_sta) { + err = -EINVAL; goto error_unlink; + } err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link, link_id); From b071576cbb2f491243e29d03f5662c50b0bed64f Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:11 +0200 Subject: [PATCH 463/465] Revert "wifi: mt76: Check link_conf pointer in mt76_connac_mcu_sta_basic_tlv()" JIRA: https://issues.redhat.com/browse/RHEL-89168 commit f22037407cb40a4e7bfa4b5d1853fa6c2f3bef6f Author: Lorenzo Bianconi Date: Tue Mar 25 17:12:05 2025 +0100 Revert "wifi: mt76: Check link_conf pointer in mt76_connac_mcu_sta_basic_tlv()" In mt76_connac_mcu_sta_basic_tlv() link_conf is always not NULL. Revert the commit '9890624c1b39 ("wifi: mt76: Check link_conf pointer in mt76_connac_mcu_sta_basic_tlv()")' in order to fix the following warning: drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c:394 mt76_connac_mcu_sta_basic_tlv() warn: variable dereferenced before check 'link_conf' This reverts commit 9890624c1b3948c1c7f1d0e19ef0bb7680b8c80d. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250325-mt76_connac_mcu_sta_basic_tlv-link_conf-revert-v1-1-b84efefb74ee@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index bafcf5a279e2..d0e49d68c5db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -391,7 +391,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC); if (vif->type == NL80211_IFTYPE_STATION && - link_conf && !is_zero_ether_addr(link_conf->bssid)) { + !is_zero_ether_addr(link_conf->bssid)) { memcpy(basic->peer_addr, link_conf->bssid, ETH_ALEN); basic->aid = cpu_to_le16(vif->cfg.aid); } else { From 6305ccefbc11d26b14da6bc3b81889d4fc0a44a1 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Tue, 17 Jun 2025 18:01:12 +0200 Subject: [PATCH 464/465] Revert "wifi: mwifiex: Fix HT40 bandwidth issue." JIRA: https://issues.redhat.com/browse/RHEL-89168 commit 570896604f47d44d4ff6882d2a588428d2a6ef17 Author: Francesco Dolcini Date: Thu Jun 5 15:03:02 2025 +0200 Revert "wifi: mwifiex: Fix HT40 bandwidth issue." This reverts commit 4fcfcbe45734 ("wifi: mwifiex: Fix HT40 bandwidth issue.") That commit introduces a regression, when HT40 mode is enabled, received packets are lost, this was experience with W8997 with both SDIO-UART and SDIO-SDIO variants. From an initial investigation the issue solves on its own after some time, but it's not clear what is the reason. Given that this was just a performance optimization, let's revert it till we have a better understanding of the issue and a proper fix. Cc: Jeff Chen Cc: stable@vger.kernel.org Fixes: 4fcfcbe45734 ("wifi: mwifiex: Fix HT40 bandwidth issue.") Closes: https://lore.kernel.org/all/20250603203337.GA109929@francesco-nb/ Signed-off-by: Francesco Dolcini Link: https://patch.msgid.link/20250605130302.55555-1-francesco@dolcini.it [fix commit reference format] Signed-off-by: Johannes Berg Signed-off-by: Jose Ignacio Tornos Martinez --- drivers/net/wireless/marvell/mwifiex/11n.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 738bafc3749b..66f0f5377ac1 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -403,14 +403,12 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) { - chan_list->chan_scan_param[0].radio_type |= - CHAN_BW_40MHZ << 2; + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. radio_type, (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - } + *buffer += struct_size(chan_list, chan_scan_param, 1); ret_len += struct_size(chan_list, chan_scan_param, 1); } From 544c87ff34099fbfa0b65e6a161edd2248c0be0d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Wed, 4 Jun 2025 10:28:13 +0200 Subject: [PATCH 465/465] wifi: save new module iwlmld-tests in internal rpm JIRA: https://issues.redhat.com/browse/RHEL-89168 Upstream-status: RHEL only The new module iwlmld-tests is to be used with kunit tests, so it needs to be saved in internals rpm. Signed-off-by: Jose Ignacio Tornos Martinez --- redhat/mod-internal.list | 1 + 1 file changed, 1 insertion(+) diff --git a/redhat/mod-internal.list b/redhat/mod-internal.list index 8d861e386264..7795cdaae7bc 100644 --- a/redhat/mod-internal.list +++ b/redhat/mod-internal.list @@ -98,6 +98,7 @@ checksum_kunit arm-smmu-v3-test iwlwifi-tests iwlmvm-tests +iwlmld-tests sound_kunit amd-pstate-ut ptp_mock