From c7427f297ddb01f593217c21b2416f1093b80194 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 28 Apr 2026 13:50:17 -0700 Subject: wifi: ath11k: use kzalloc_flex for struct scan_req_params Convert kzalloc_obj + kcalloc to kzalloc_flex to save an allocation. Add __counted_by to get extra runtime analysis. Move counting variable assignment immediately after allocation before any potential accesses. kzalloc_flex does this anyway for GCC >= 15. Signed-off-by: Rosen Penev Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260428205017.26288-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/mac.c | 72 +++++++++++++---------------------- drivers/net/wireless/ath/ath11k/wmi.h | 2 +- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index a48b6bf1f29a..2d55cdc4d165 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4227,13 +4227,14 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, req->n_channels); if (!arg) { ret = -ENOMEM; goto exit; } + arg->num_chan = req->n_channels; ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -4261,38 +4262,27 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, arg->scan_f_passive = 1; } - if (req->n_channels) { - arg->num_chan = req->n_channels; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); + for (i = 0; i < arg->num_chan; i++) { + if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, + ar->ab->wmi_ab.svc_map)) { + arg->chan_list[i] = + u32_encode_bits(req->channels[i]->center_freq, + WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - if (!arg->chan_list) { - ret = -ENOMEM; - goto exit; - } - - for (i = 0; i < arg->num_chan; i++) { - if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, - ar->ab->wmi_ab.svc_map)) { - arg->chan_list[i] = - u32_encode_bits(req->channels[i]->center_freq, - WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - - /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan - * flags, then scan all PSC channels in 6 GHz band and - * those non-PSC channels where RNR IE is found during - * the legacy 2.4/5 GHz scan. - * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, - * then all channels in 6 GHz will be scanned. - */ - if (req->channels[i]->band == NL80211_BAND_6GHZ && - req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && - !cfg80211_channel_is_psc(req->channels[i])) - arg->chan_list[i] |= - WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; - } else { - arg->chan_list[i] = req->channels[i]->center_freq; - } + /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan + * flags, then scan all PSC channels in 6 GHz band and + * those non-PSC channels where RNR IE is found during + * the legacy 2.4/5 GHz scan. + * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, + * then all channels in 6 GHz will be scanned. + */ + if (req->channels[i]->band == NL80211_BAND_6GHZ && + req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && + !cfg80211_channel_is_psc(req->channels[i])) + arg->chan_list[i] |= + WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; + } else { + arg->chan_list[i] = req->channels[i]->center_freq; } } @@ -4335,7 +4325,6 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, exit: if (arg) { - kfree(arg->chan_list); kfree(arg->extraie.ptr); kfree(arg); } @@ -9735,19 +9724,14 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, 1); if (!arg) { ret = -ENOMEM; goto exit; } - ath11k_wmi_start_scan_init(ar, arg); + arg->num_chan = 1; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); - if (!arg->chan_list) { - ret = -ENOMEM; - goto free_arg; - } + ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -9768,7 +9752,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); ar->scan.state = ATH11K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); - goto free_chan_list; + goto free_arg; } ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); @@ -9778,7 +9762,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); ret = -ETIMEDOUT; - goto free_chan_list; + goto free_arg; } ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, @@ -9786,8 +9770,6 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, ret = 0; -free_chan_list: - kfree(arg->chan_list); free_arg: kfree(arg); exit: diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index baed501b640b..b2dade0516ac 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3423,7 +3423,6 @@ struct scan_req_params { u32 num_bssid; u32 num_ssids; u32 n_probes; - u32 *chan_list; u32 notify_scan_events; struct wlan_ssid ssid[WLAN_SCAN_PARAMS_MAX_SSID]; struct wmi_mac_addr bssid_list[WLAN_SCAN_PARAMS_MAX_BSSID]; @@ -3436,6 +3435,7 @@ struct scan_req_params { struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; struct wmi_mac_addr mac_addr; struct wmi_mac_addr mac_mask; + u32 chan_list[] __counted_by(num_chan); }; struct wmi_ssid_arg { -- cgit v1.2.3 From 6b471e9aefee9ed73278eb1141e0d8530a56fae9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:23:50 +0800 Subject: wifi: ath11k: fix invalid data access in ath11k_dp_rx_h_undecap_nwifi In certain cases, hardware might provide packets with a length greater than the maximum native Wi-Fi header length. This can lead to accessing and modifying fields in the header within the ath11k_dp_rx_h_undecap_nwifi() function for the DP_RX_DECAP_TYPE_NATIVE_WIFI decap type and potentially result in invalid data access and memory corruption. Kernel stack is corrupted in: ath11k_dp_rx_h_undecap+0x6b0/0x6b0 [ath11k] Call trace: ath11k_dp_rx_h_mpdu+0x0/0x2e8 [ath11k] ath11k_dp_rx_h_mpdu+0x1e0/0x2e8 [ath11k] ath11k_dp_rx_wbm_err+0x1e0/0x450 [ath11k] ath11k_dp_rx_process_wbm_err+0x2fc/0x460 [ath11k] ath11k_dp_service_srng+0x2e0/0x348 [ath11k] Add a sanity check before processing the SKB to prevent invalid data access in the undecap native Wi-Fi function for the DP_RX_DECAP_TYPE_NATIVE_WIFI decap type. This adapted from the discussion/patch of the ath12k driver [1]. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Link: https://lore.kernel.org/linux-wireless/20250211090302.4105141-1-tamizh.raja@oss.qualcomm.com/ # [1] Signed-off-by: Miaoqing Pan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512022351.2033155-2-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 50 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 2a413e3a07a7..923421281254 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2503,6 +2503,29 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); } +static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab, + struct hal_rx_desc *rx_desc, + struct sk_buff *msdu) +{ + struct ieee80211_hdr *hdr; + u8 decap_type; + u32 hdr_len; + + decap_type = ath11k_dp_rx_h_msdu_start_decap_type(ab, rx_desc); + if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI) + return true; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN)) + return true; + + ab->soc_stats.invalid_rbm++; + WARN_ON_ONCE(1); + return false; +} + static int ath11k_dp_rx_process_msdu(struct ath11k *ar, struct sk_buff *msdu, struct sk_buff_head *msdu_list, @@ -2573,6 +2596,11 @@ static int ath11k_dp_rx_process_msdu(struct ath11k *ar, } } + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) { + ret = -EINVAL; + goto free_out; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); @@ -3262,6 +3290,12 @@ mic_fail: RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, rx_desc, + msdu))) { + dev_kfree_skb_any(msdu); + return -EINVAL; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); @@ -3954,6 +3988,10 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } + + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, desc, msdu))) + return -EINVAL; + ath11k_dp_rx_h_ppdu(ar, desc, status); ath11k_dp_rx_h_mpdu(ar, msdu, desc, status); @@ -3998,7 +4036,7 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, return drop; } -static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, +static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { u16 msdu_len; @@ -4006,6 +4044,7 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, u8 l3pad_bytes; struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; + struct ath11k_base *ab = ar->ab; rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ar->ab, desc); rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ar->ab, desc); @@ -4015,6 +4054,9 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return true; + ath11k_dp_rx_h_ppdu(ar, desc, status); status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | @@ -4022,19 +4064,21 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, ath11k_dp_rx_h_undecap(ar, msdu, desc, HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); + + return false; } static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); - bool drop = false; + bool drop; ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: - ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); + drop = ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); break; default: /* TODO: Review other rxdma error code to check if anything is -- cgit v1.2.3 From 4d8af936b4fe377f3d7700540f301d8e45e8759b Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:23:51 +0800 Subject: wifi: ath11k: add MSDU length validation for TKIP MIC error In the WBM error path, while processing TKIP MIC errors, MSDU length is fetched from the hal_rx_desc's msdu_end. This MSDU length is directly passed to skb_put() without validation. In stress test scenarios, the WBM error ring may receive invalid descriptors, which could lead to an invalid MSDU length. To fix this, add a check to drop the skb when the calculated MSDU length is greater than the skb size. This is adapted from the discussion/patch of the ath12k driver [1]. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Link: https://lore.kernel.org/linux-wireless/20250416021903.3178962-1-nithyanantham.paramasivam@oss.qualcomm.com/ # [1] Signed-off-by: Miaoqing Pan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512022351.2033155-3-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 923421281254..9e90d8e3f155 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4051,6 +4051,15 @@ static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, desc); msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ar->ab, desc); + + if (unlikely(hal_rx_desc_sz + l3pad_bytes + msdu_len > DP_RX_BUFFER_SIZE)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "invalid msdu len in tkip mic err %u\n", msdu_len); + ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", desc, + sizeof(*desc)); + return true; + } + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); -- cgit v1.2.3 From 209887467581116a93490e6122b87b6fe0787627 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Sun, 24 May 2026 19:07:11 -0700 Subject: wifi: ath11k: raise max vdevs to 4 on hardware with P2P and dual-station support When P2P support is enabled, wpa_supplicant creates a p2p-device interface by default, which implicitly consumes one vdev. On systems managed by NetworkManager, this interface cannot be reliably disabled, leaving only two usable interfaces for user configurations. Increase num_vdevs to four for QCA6390 hw2.0, WCN6855 hw2.0/hw2.1, QCA2066 hw2.1, and QCA6698AQ hw2.1 to account for the implicit p2p-device and enable common concurrency scenarios such as AP + AP + STA. This change increases interface concurrency in the two-channel scenario by raising the maximum vdev limit, while keeping other combination rules unchanged. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 Tested-on: QCA6698AQ hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Signed-off-by: Wei Zhang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525020711.2590815-1-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 3f6f4db5b7ee..8dacc878c006 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -267,7 +267,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), @@ -445,7 +445,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -535,7 +535,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -797,7 +797,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -886,7 +886,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), -- cgit v1.2.3 From 10085a654a4c2331d5f0cdc20bfc839a49fbb886 Mon Sep 17 00:00:00 2001 From: Kwan Lai Chee Hou Date: Thu, 7 May 2026 09:53:35 +0800 Subject: wifi: ath12k: fix incorrect HT/VHT/HE/EHT MCS reporting in monitor mode In monitor mode, the driver incorrectly assigns the legacy rate to the rate_idx field of the radiotap header for HT/VHT/HE/EHT frames, ignoring the actual MCS value parsed from the hardware. This causes packet analyzers (like Wireshark) to display incorrect MCS values (e.g., legacy base rates instead of the true MCS). Fix this by assigning ppdu_info->mcs as the default rate_mcs in ath12k_dp_mon_fill_rx_rate(), and remove rate_idx assignments in ath12k_dp_mon_update_radiotap() to preserve the previously calculated MCS values (including the HT NSS offset). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ Fixes: 5393dcb45209 ("wifi: ath12k: change the status update in the monitor Rx") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220864 Signed-off-by: Kwan Lai Chee Hou Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260507015336.14636-1-laicheehou9@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa46..44c5cff75f16 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { struct ath12k *ar; -- cgit v1.2.3 From 98d4f92ab6a1af2ea2ab590d7e2801b203110981 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:11:08 +0800 Subject: wifi: ath12k: fix memory leak in ath12k_wifi7_dp_rx_h_verify_tkip_mic() In ath12k_wifi7_dp_rx_h_verify_tkip_mic(), the call to ath12k_dp_rx_check_nwifi_hdr_len_valid() may return false when the NWIFI header length is invalid, causing the function to abort early with -EINVAL. When this happens, the error propagates to ath12k_wifi7_dp_rx_h_defrag(), which clears first_frag by setting it to NULL. As a result, the corresponding MSDU is no longer referenced by the defragmentation path and is never freed. This leads to a memory leak for the affected MSDU on this error path. Proper cleanup is required to ensure the MSDU is released when header validation fails during TKIP MIC verification. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 9a0dddfb30f1 ("wifi: ath12k: Fix invalid data access in ath12k_dp_rx_h_undecap_nwifi") Signed-off-by: Miaoqing Pan Reviewed-by: Tamizh Chelvam Raja Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512021108.2031651-1-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 945680b3ebdf..a5e290edaa89 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1028,8 +1028,10 @@ mic_fail: skb_pull(msdu, hal_rx_desc_sz); if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, - rx_info))) + rx_info))) { + dev_kfree_skb_any(msdu); return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, -- cgit v1.2.3 From 51d510589f5cb54b9251f4ecf4c13f219dead928 Mon Sep 17 00:00:00 2001 From: Hangtian Zhu Date: Tue, 12 May 2026 10:57:32 +0800 Subject: wifi: ath12k: allow peer_id 0 in dp peer lookup For some chipsets, firmware can report HTT_T2H_MSG_TYPE_PEER_MAP2 with peer_id 0 as a valid value for mapping ath12k_dp_link_peer to ath12k_dp_peer. ath12k_dp_peer_find_by_peerid() currently treats peer_id 0 as invalid. When firmware assigns peer_id 0, peer lookup fails. As a result, DHCP OFFER packets are dropped in __ieee80211_rx_handle_packet() because pubsta is NULL. ath12k_dp_rx_deliver_msdu() <- rx_info->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ieee80211_rx_napi <- pubsta NULL ieee80211_rx_list __ieee80211_rx_handle_packet <- pubsta NULL, skb undelivered The following error in the TX completion path is caused by the same issue: ath12k_wifi7_pci 0000:04:00.0: dp_tx: failed to find the peer with peer_id 0 The error message is triggered by: ath12k_wifi7_dp_tx_complete_msdu ath12k_dp_link_peer_find_by_peerid <- ts->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ath12k_dp_tx_htt_tx_complete_buf ath12k_dp_link_peer_find_by_peerid <- peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL Fix this by allowing peer_id 0 in ath12k_dp_peer_find_by_peerid() and rejecting only values >= ATH12K_DP_PEER_ID_INVALID. Also update peer_id 0 handling in monitor path: Always call ath12k_dp_link_peer_find_by_peerid() in ath12k_dp_rx_h_find_link_peer() to fetch the peer, including when peer_id is 0. Always store peer_id in ppdu_info->peer_id in ath12k_wifi7_dp_mon_rx_parse_status_tlv(), including peer_id 0. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hangtian Zhu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512025732.1297849-1-hangtian.zhu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.c | 2 +- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 +-- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index a1100782d45e..47d009a0d61f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -419,7 +419,7 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "ath12k dp peer find by peerid index called without rcu lock"); - if (!peer_id || peer_id >= ATH12K_DP_PEER_ID_INVALID) + if (peer_id >= ATH12K_DP_PEER_ID_INVALID) return NULL; index = ath12k_dp_peer_get_peerid_index(dp, peer_id); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index a68b28aa1f4b..8b69afc69ad4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1202,8 +1202,7 @@ ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *ms lockdep_assert_held(&dp->dp_lock); - if (rxcb->peer_id) - peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) return peer; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 77f5d23be78d..7dd4a49d64d5 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -1779,8 +1779,7 @@ ath12k_wifi7_dp_mon_rx_parse_status_tlv(struct ath12k_pdev_dp *dp_pdev, info[1] = __le32_to_cpu(mpdu_start->info1); peer_id = u32_get_bits(info[1], HAL_RX_MPDU_START_INFO1_PEERID); - if (peer_id) - ppdu_info->peer_id = peer_id; + ppdu_info->peer_id = peer_id; ppdu_info->mpdu_len += u32_get_bits(info[1], HAL_RX_MPDU_START_INFO2_MPDU_LEN); -- cgit v1.2.3 From c972636efc63f0f43d725b59805dd1ae5bc4b31e Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:04 -0700 Subject: wifi: ath12k: fix inconsistent arvif state in vdev_create error paths ath12k_mac_vdev_create() has three error path issues that leave arvif in an inconsistent state: 1. When ath12k_wmi_vdev_create() fails, the function returns directly without clearing arvif->ar, which was already set before the WMI call. Subsequent code checking arvif->ar to determine vdev readiness will see a non-NULL value despite no vdev existing in firmware. 2. When ath12k_wmi_send_peer_delete_cmd() fails in err_peer_del, the code jumped to err: skipping the DP peer cleanup and vdev rollback, leaving num_created_vdevs, vdev maps and arvif list membership live. 3. When ath12k_wait_for_peer_delete_done() fails, the code jumped to err_vdev_del: skipping the DP peer cleanup. Fix by changing the ath12k_wmi_vdev_create() failure to goto err instead of returning directly, routing both err_peer_del failure paths through err_dp_peer_del: for proper DP peer and vdev rollback, and consolidating the arvif state cleanup at err:. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 477cabfdb776 ("wifi: ath12k: modify link arvif creation and removal for MLO") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-2-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 87b27f7cff5d..b9b95f7f1f60 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10302,7 +10302,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - return ret; + goto err; } ar->num_created_vdevs++; @@ -10449,13 +10449,13 @@ err_peer_del: if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arvif->vdev_id, arvif->bssid); - goto err; + goto err_dp_peer_del; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arvif->bssid); if (ret) - goto err_vdev_del; + goto err_dp_peer_del; ar->num_peers--; } @@ -10472,8 +10472,6 @@ err_vdev_del: ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; - arvif->is_created = false; - arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -10482,6 +10480,7 @@ err_vdev_del: spin_unlock_bh(&ar->data_lock); err: + arvif->is_created = false; arvif->ar = NULL; return ret; } -- cgit v1.2.3 From 47809a7c8348bc4a332ccc26a37c7145a5f609f8 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:05 -0700 Subject: wifi: ath12k: fix NULL deref in change_sta_links for unready link _ieee80211_set_active_links() calls _ieee80211_link_use_channel() for each newly-added link and WARN_ON_ONCE()s if it fails. The call uses assign_on_failure=true, which allows mac80211 to continue despite driver failures, but when a mac80211-level channel validation fails (e.g., combinations check, DFS, or no available radio), drv_assign_vif_chanctx() is never reached. Since ath12k_mac_vdev_create() is only called from that path, arvif->is_created remains false and arvif->ar remains NULL for the failed link. The subsequent drv_change_sta_links() call reaches ath12k_mac_op_change_sta_links(), which allocates an arsta and sets ahsta->links_map |= BIT(link_id) for the broken link before checking whether the link is ready. When the vdev was never created, only station_add() is skipped, but the link remains in links_map. Any subsequent operation iterating links_map and dereferencing arvif->ar without a NULL check will crash. Two observed examples are NULL deref in ath12k_mac_ml_station_remove() on disconnect and in ath12k_mac_op_set_key() when wpa_supplicant installs PTK keys. BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] Call trace: ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] ath12k_mac_op_sta_state+0xb60/0x1720 [ath12k] drv_sta_state+0x100/0xbd8 [mac80211] __sta_info_destroy_part2+0x148/0x178 [mac80211] ieee80211_set_disassoc+0x500/0x678 [mac80211] BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] Call trace: ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] drv_set_key+0x70/0x100 [mac80211] ieee80211_key_enable_hw_accel+0x78/0x260 [mac80211] ieee80211_add_key+0x16c/0x2ac [mac80211] nl80211_new_key+0x138/0x280 [cfg80211] Fix this by checking arvif->is_created before calling ath12k_mac_alloc_assign_link_sta(). This prevents the broken link from entering links_map, so all subsequent operations iterating the bitmap are protected. The reliability of arvif->is_created across all error paths is ensured by the preceding patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: a27fa6148dac ("wifi: ath12k: support change_sta_links() mac80211 op") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-3-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b9b95f7f1f60..58deddab5f50 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8049,16 +8049,16 @@ int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { -- cgit v1.2.3 From 05337d0b9c5a7ab3b60473490705ebe90d5316aa Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Fri, 15 May 2026 08:39:09 +0530 Subject: wifi: ath12k: add hardware parameters for maximum supported clients Currently, the driver uses memory profile parameters to determine the maximum number of supported clients, with a default limit of 512 for single-radio and 128 for DBS and DBS+SBS configurations. However, some devices have lower hardware limits depending on the radio configuration. Exceeding these hardware-specific limits can lead to firmware crashes. Add hardware parameters in ath12k_hw_params to define the maximum supported clients for each radio configuration. The driver uses the minimum of the memory profile limit and the hardware capability limit to prevent exceeding hardware constraints. Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260515030909.3312511-1-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/hw.h | 25 ++++++++++++++++++++++--- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index a9888e0521a1..d135b2936378 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -19,14 +19,28 @@ #define TARGET_NUM_VDEVS(ab) ((ab)->profile_param->num_vdevs) /* Max num of stations for Single Radio mode */ -#define TARGET_NUM_STATIONS_SINGLE(ab) ((ab)->profile_param->max_client_single) +#define TARGET_NUM_STATIONS_SINGLE(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_single, \ + _ab->profile_param->max_client_single); \ +}) /* Max num of stations for DBS */ -#define TARGET_NUM_STATIONS_DBS(ab) ((ab)->profile_param->max_client_dbs) +#define TARGET_NUM_STATIONS_DBS(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs, \ + _ab->profile_param->max_client_dbs); \ +}) /* Max num of stations for DBS_SBS */ #define TARGET_NUM_STATIONS_DBS_SBS(ab) \ - ((ab)->profile_param->max_client_dbs_sbs) +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs_sbs, \ + _ab->profile_param->max_client_dbs_sbs); \ +}) #define TARGET_NUM_STATIONS(ab, x) TARGET_NUM_STATIONS_##x(ab) @@ -213,6 +227,11 @@ struct ath12k_hw_params { /* setup REO queue, frag etc only for primary link peer */ bool dp_primary_link_only:1; + struct { + u32 max_client_single; + u32 max_client_dbs; + u32 max_client_dbs_sbs; + } client; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index cb3185850439..98bf9293dd33 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -434,6 +434,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "wcn7850 hw2.0", @@ -520,6 +525,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcn9274 hw2.0", @@ -602,6 +612,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5332 hw1.0", @@ -677,6 +692,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .bdf_addr_offset = 0xC00000, .dp_primary_link_only = true, + .client = { + .max_client_single = 256, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcc2072 hw1.0", @@ -764,6 +784,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5424 hw1.0", @@ -843,6 +868,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, }; -- cgit v1.2.3 From 675aa75bfc29fb18c6e4d58904a91c1d37228217 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 22 May 2026 14:48:28 +0530 Subject: wifi: ath12k: Prevent incorrect vif chanctx switch when handling multi-radio contexts When multiple links switch channel contexts around the same time, mac80211 may complete CSA for several links together and invoke ath12k_mac_op_switch_vif_chanctx() with an array of vifs spanning more than one underlying radio in a single-wiphy configuration. The driver currently assumes that all entries in the vifs array belong to the same radio and derives the radio context from the first element. On multi-radio hardware, this can lead to incorrect vdev selection/updates and may corrupt driver state when the number of vifs exceeds what a single radio supports. Fix this by validating each vif's switch request and then processing vifs grouped by their associated radio. For each vif, ensure the band does not change across the switch and that both old/new channel contexts resolve to a valid ath12k device. Reject attempts to move a vif between radios (not supported for now) and return -EOPNOTSUPP to upper layers. Then, iterate through the input vifs, collect all unprocessed entries that map to the same radio, and invoke ath12k_mac_update_vif_chan() separately for each radio group. This removes any reliance on mac80211 providing the array grouped by radio or sharing old_ctx pointers across vifs. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Co-developed-by: Maharaja Kennadyrajan Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260522091828.3199584-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 87 ++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 58deddab5f50..d13936be857d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -11479,6 +11479,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, continue; } + if (WARN_ON(!arvif)) + continue; + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", arvif->vdev_id, @@ -12270,23 +12273,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k *ar; + struct ath12k *curr_ar, *new_ar, *group_ar; + struct ieee80211_vif_chanctx_switch *v; + int i, j, count = 0; lockdep_assert_wiphy(hw->wiphy); - ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx); - if (!ar) - return -EINVAL; + if (n_vifs == 0) + return 0; - /* Switching channels across radio is not allowed */ - if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) - return -EINVAL; + struct ath12k **ar_map __free(kfree) = kzalloc_objs(*ar_map, n_vifs); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "mac chanctx switch n_vifs %d mode %d\n", - n_vifs, mode); - ath12k_mac_update_vif_chan(ar, vifs, n_vifs); + if (!ar_map) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + + if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac chanctx switch band change not supported\n"); + return -EOPNOTSUPP; + } + + curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx); + new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx); + + if (!curr_ar || !new_ar) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "unable to determine device for the passed channel ctx\n"); + ath12k_generic_dbg(ATH12K_DBG_MAC, + "Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n", + v->old_ctx->def.chan->center_freq, + curr_ar ? "valid" : "invalid", + v->new_ctx->def.chan->center_freq, + new_ar ? "valid" : "invalid"); + return -EINVAL; + } + /* Switching a vif between two radios is not allowed */ + if (curr_ar != new_ar) { + ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch to another radio not supported\n"); + return -EOPNOTSUPP; + } + + ar_map[i] = curr_ar; + } + + /* Group vifs by radio (ar) and process each group independently. */ + bool *processed __free(kfree) = kzalloc_objs(*processed, n_vifs); + + if (!processed) + return -ENOMEM; + + struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) = + kzalloc_objs(*group_vifs, n_vifs); + + if (!group_vifs) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + if (processed[i]) + continue; + + group_ar = ar_map[i]; + + count = 0; + for (j = 0; j < n_vifs; j++) { + if (!processed[j] && ar_map[j] == group_ar) { + group_vifs[count++] = vifs[j]; + processed[j] = true; + } + } + + ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + count, mode); + ath12k_mac_update_vif_chan(group_ar, group_vifs, count); + } return 0; } EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx); -- cgit v1.2.3 From e1125b0ab6fdda21dde19f7be631a477d14b684c Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:37 +0530 Subject: wifi: ath12k: Set WDS vdev parameter for 4-address station interface Set WDS vdev parameter during station interface creation to enable 4-address mode. Unlike AP interfaces that set peer-specific 4-address mode parameters after receiving 4-address frames from stations, station interfaces must send all data frames in 4-address mode immediately after association, including 4-address NULL frames. Firmware requires 4-address notification for station interfaces during vdev creation. Configure the WDS vdev parameter for station interfaces. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-2-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d13936be857d..0f68b4beeb6b 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10235,6 +10235,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) struct ieee80211_hw *hw = ah->hw; struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); struct ath12k_wmi_vdev_create_arg vdev_arg = {}; struct ath12k_wmi_peer_create_arg peer_param = {}; struct ieee80211_bss_conf *link_conf = NULL; @@ -10403,6 +10404,25 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) goto err_peer_del; } + /* + * There could be race condition in firmware for the station + * interface between enabling 4-address peer WMI param and + * sending 4-address frame (NULL or EAPOL via TCL). + * Make the station as WDS while bringup itself + * to avoid the race condition + */ + if (vif->type == NL80211_IFTYPE_STATION && + (wdev && wdev->use_4addr)) { + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_WDS, + 1); + if (ret) { + ath12k_warn(ar->ab, "failed to set WDS vdev param: %d\n", + ret); + goto err_peer_del; + } + } + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && ahvif->vdev_type == WMI_VDEV_TYPE_STA && ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { -- cgit v1.2.3 From 2f57f737dbf3005951a045eb9d1daaff0095f6c1 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:38 +0530 Subject: wifi: ath12k: Add support for 4-address mode The current driver does not support enabling 4-address mode data traffic in WDS mode. Add the required functionality by introducing the sta_set_4addr() API, which is invoked when a 4-address AP/STA connects. This API sends the WMI_PEER_USE_4ADDR peer parameter to notify firmware about the 4-address peer, allowing firmware and hardware to transmit and receive frames in 4-address format for that peer. For 4-address multicast packet transmission, update the handling to set peer metadata values in HAL_TCL_DATA_CMD_INFO1_CMD_NUM instead of using vdev metadata values. Vdev metadata is used only for 3-address and 4-address unicast traffic and for 3-address multicast traffic. The peer metadata path embeds the correct peer_id, enabling proper multicast transmission in 4-address mode. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-3-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 6 ++ drivers/net/wireless/ath/ath12k/mac.c | 79 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/mac.h | 3 + drivers/net/wireless/ath/ath12k/peer.c | 7 ++- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 10 +++- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h | 4 +- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 18 +++++- drivers/net/wireless/ath/ath12k/wmi.h | 5 ++ 8 files changed, 125 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index f6d8ec9ef7b0..70ad9742c21d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -353,6 +353,8 @@ struct ath12k_link_vif { u16 num_stations; bool is_csa_in_progress; struct wiphy_work bcn_tx_work; + + bool set_wds_vdev_param; }; struct ath12k_vif { @@ -492,6 +494,8 @@ struct ath12k_link_sta { /* link address similar to ieee80211_link_sta */ u8 addr[ETH_ALEN]; + u16 tcl_metadata; + /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ u32 bw; @@ -527,6 +531,8 @@ struct ath12k_sta { u16 free_logical_link_idx_map; enum ieee80211_sta_state state; + + bool enable_4addr; }; #define ATH12K_HALF_20MHZ_BW 10 diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 0f68b4beeb6b..616d13261305 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6571,6 +6571,62 @@ static int ath12k_mac_station_disassoc(struct ath12k *ar, return 0; } +static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta) +{ + struct ath12k_dp_link_peer *peer; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + struct ath12k_dp *dp; + unsigned long links; + struct ath12k *ar; + u8 link_id; + int ret; + + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arsta = wiphy_dereference(wiphy, ahsta->link[link_id]); + if (!arsta) + continue; + + arvif = arsta->arvif; + ar = arvif->ar; + + if (arvif->set_wds_vdev_param) + goto skip_use_4addr; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "setting USE_4ADDR for peer %pM\n", arsta->addr); + + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME); + if (ret) { + ath12k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", + arsta->addr, ret); + return ret; + } + +skip_use_4addr: + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, + arsta->addr); + if (peer && peer->dp_peer) { + peer->dp_peer->ucast_ra_only = true; + } else { + spin_unlock_bh(&dp->dp_lock); + ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", + arsta->addr); + return -ENOENT; + } + + spin_unlock_bh(&dp->dp_lock); + } + + return 0; +} + static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) { struct ieee80211_link_sta *link_sta; @@ -7865,6 +7921,28 @@ out: } EXPORT_SYMBOL(ath12k_mac_op_sta_set_txpwr); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + + lockdep_assert_wiphy(hw->wiphy); + + /* + * 4-address mode disabled option is available only for station + * interface from mac80211, and we have wds_vdev_param for station + * interface and target will not allow to disable the wds_vdev_param + * during run time. So, add support only for enable case, for + * disable case station interface needs to be reconnect. + */ + if (enabled && !ahsta->enable_4addr) { + if (!ath12k_mac_sta_set_4addr(hw->wiphy, ahsta)) + ahsta->enable_4addr = true; + } +} +EXPORT_SYMBOL(ath12k_mac_op_sta_set_4addr); + void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, @@ -10421,6 +10499,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ret); goto err_peer_del; } + arvif->set_wds_vdev_param = true; } if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 7b50c5976384..aba98afd4365 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -255,6 +255,9 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 2e875176baaa..42488b465540 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -218,7 +218,12 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, ahsta->link[link_id]); - + /* TODO: Split DP related field usage to DP peer structure */ + arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, + HTT_TCL_META_DATA_VALID_HTT); peer->link_id = arsta->link_id; /* Fill ML info into created peer */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 629084aa36d8..5f298133dee9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -59,8 +59,8 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast) + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast) { struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_hal *hal = dp->hal; @@ -125,6 +125,12 @@ tcl_ring_sel: ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + if (ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + } + if (dp_vif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) { if (skb_cb->flags & ATH12K_SKB_CIPHER_SET) { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h index 24cf7972d41b..86bc813878c0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h @@ -8,8 +8,8 @@ #define ATH12K_DP_TX_WIFI7_H int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast); + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast); void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id); u32 ath12k_wifi7_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_link_vif *arvif); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 98bf9293dd33..3d59fa452ec0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -889,7 +889,9 @@ static void ath12k_wifi7_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_sta *arsta = NULL; struct ath12k_link_vif *tmp_arvif; + struct ath12k_sta *ahsta = NULL; u32 info_flags = info->flags; struct sk_buff *msdu_copied; struct ath12k *ar, *tmp_ar; @@ -971,6 +973,12 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) is_mcast = is_multicast_ether_addr(hdr->addr1); + if (sta) { + ahsta = ath12k_sta_to_ahsta(control->sta); + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + } + /* 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); @@ -991,7 +999,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!vif->valid_links || !is_mcast || is_dvlan || (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) || test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { - ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, skb, false, 0, is_mcast); + ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, arsta, skb, false, 0, is_mcast); if (unlikely(ret)) { ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); ieee80211_free_txskb(ar->ah->hw, skb); @@ -1029,6 +1037,11 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, skb_cb->vif = vif; skb_cb->ar = tmp_ar; + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + else + arsta = NULL; + /* For open mode, skip peer find logic */ if (unlikely(!ahvif->dp_vif.key_cipher)) goto skip_peer_find; @@ -1060,7 +1073,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, spin_unlock_bh(&tmp_dp->dp_lock); skip_peer_find: - ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, + ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, arsta, msdu_copied, true, mcbc_gsn, is_mcast); if (unlikely(ret)) { if (ret == -ENOMEM) { @@ -1105,6 +1118,7 @@ static const struct ieee80211_ops ath12k_ops_wifi7 = { .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, .link_sta_rc_update = ath12k_mac_op_link_sta_rc_update, + .sta_set_4addr = ath12k_mac_op_sta_set_4addr, .conf_tx = ath12k_mac_op_conf_tx, .set_antenna = ath12k_mac_op_set_antenna, .get_antenna = ath12k_mac_op_get_antenna, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 14b8dcdf881d..a827b3c99f8f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2334,6 +2334,11 @@ enum wmi_preamble { WMI_VDEV_PREAMBLE_SHORT = 2, }; +enum wmi_peer_4addr_allow_frame { + WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, +}; + enum wmi_peer_smps_state { WMI_PEER_SMPS_PS_NONE = 0, WMI_PEER_SMPS_STATIC = 1, -- cgit v1.2.3 From 729cad3c3c9e09ca9900744fe2a02b25e23cdab5 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:39 +0530 Subject: wifi: ath12k: Add 4-address mode support for eth offload Currently driver does not enable the hardware/firmware support for handling 4-address multicast frames in the Tx/Rx path when 8023_ENCAP_OFFLOAD is enabled. Add the required support to ensure correct processing of multicast traffic in 4-address mode. Enable this functionality by setting the WMI_VDEV_PARAM_AP_ENABLE_NAWDS vdev parameter when the 8023_ENCAP_OFFLOAD feature is active. Override peer metadata values for 4-address multicast packet transmission by using the station's ast_hash and ast_idx instead of vdev-level metadata, and set HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE to indicate this override. Suppress firmware peer-map events for 4-address frames by setting the WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE flag during WMI initialization. This prevents inconsistencies in the host's peer list. Add the IEEE80211_OFFLOAD_ENCAP_4ADDR VIF offload flag to notify mac80211 that 4-address Ethernet encapsulation offload is supported. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-4-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 3 +++ drivers/net/wireless/ath/ath12k/mac.c | 28 +++++++++++++++++---- drivers/net/wireless/ath/ath12k/peer.c | 2 ++ drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 35 +++++++++++++++++++++----- drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c | 4 ++- drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h | 1 + drivers/net/wireless/ath/ath12k/wmi.c | 3 ++- drivers/net/wireless/ath/ath12k/wmi.h | 9 +++++++ 8 files changed, 72 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 70ad9742c21d..fc5127b5c1a3 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -355,6 +355,7 @@ struct ath12k_link_vif { struct wiphy_work bcn_tx_work; bool set_wds_vdev_param; + bool nawds_enabled; }; struct ath12k_vif { @@ -495,6 +496,8 @@ struct ath12k_link_sta { u8 addr[ETH_ALEN]; u16 tcl_metadata; + u16 ast_hash; + u16 ast_idx; /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 616d13261305..2c1eadf548ac 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6576,6 +6576,7 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst struct ath12k_dp_link_peer *peer; struct ath12k_link_vif *arvif; struct ath12k_link_sta *arsta; + struct ath12k_vif *ahvif; struct ath12k_dp *dp; unsigned long links; struct ath12k *ar; @@ -6589,10 +6590,11 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst continue; arvif = arsta->arvif; + ahvif = arvif->ahvif; ar = arvif->ar; if (arvif->set_wds_vdev_param) - goto skip_use_4addr; + goto skip_nawds; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "setting USE_4ADDR for peer %pM\n", arsta->addr); @@ -6607,7 +6609,21 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst return ret; } -skip_use_4addr: + if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET) + goto skip_nawds; + + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WDS_EXT_ENABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n", + arvif->vdev_id, ret); + return ret; + } + + arvif->nawds_enabled = true; + +skip_nawds: dp = ath12k_ab_to_dp(ar->ab); spin_lock_bh(&dp->dp_lock); peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, @@ -10098,12 +10114,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED | IEEE80211_OFFLOAD_DECAP_ENABLED); - if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) + if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET; - else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + } else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_RAW; - else + } else { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI; + } ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param_id, ahvif->dp_vif.tx_encap_type); diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 42488b465540..f7f0f613b393 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -224,6 +224,8 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, HTT_TCL_META_DATA_PEER_ID) | u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); + arsta->ast_hash = peer->ast_hash; + arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; /* Fill ML info into created peer */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 5f298133dee9..d2749de44553 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -57,6 +57,8 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) return 0; } +#define ATH12K_AST_HASH_MASK 0xF + /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, struct ath12k_link_sta *arsta, struct sk_buff *skb, @@ -78,6 +80,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a struct ath12k_dp_vif *dp_vif = &ahvif->dp_vif; struct ath12k_dp_link_vif *dp_link_vif; struct dp_tx_ring *tx_ring; + struct ethhdr *eth = NULL; u8 pool_id; u8 hal_ring_id; int ret; @@ -96,6 +99,9 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a !ieee80211_is_data(hdr->frame_control)) return -EOPNOTSUPP; + if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) + eth = (struct ethhdr *)skb->data; + pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1); /* Let the default ring selection be based on current processor @@ -124,9 +130,16 @@ tcl_ring_sel: ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + ti.bss_ast_hash = dp_link_vif->ast_hash; + ti.bss_ast_idx = dp_link_vif->ast_idx; - if (ieee80211_has_a4(hdr->frame_control) && - is_multicast_ether_addr(hdr->addr3) && arsta) { + if (eth && is_multicast_ether_addr(eth->h_dest) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.bss_ast_hash = arsta->ast_hash & ATH12K_AST_HASH_MASK; + ti.bss_ast_idx = arsta->ast_idx; + ti.lookup_override = true; + } else if (!eth && ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { ti.meta_data_flags = arsta->tcl_metadata; ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); } @@ -146,7 +159,7 @@ tcl_ring_sel: msdu_ext_desc = true; } - if (gsn_valid) { + if (gsn_valid && !ti.lookup_override) { /* Reset and Initialize meta_data_flags with Global Sequence * Number (GSN) info. */ @@ -154,6 +167,14 @@ tcl_ring_sel: 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); + + /* + * Since NAWDS enabled for this vdev firmware expects + * this flag to be set for sending 3-address multicast frame. + */ + ti.meta_data_flags |= + u32_encode_bits(arvif->nawds_enabled, + HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED); } ti.encap_type = ath12k_dp_tx_get_encap_type(ab, skb); @@ -164,11 +185,13 @@ tcl_ring_sel: ti.lmac_id = dp_link_vif->lmac_id; ti.vdev_id = dp_link_vif->vdev_id; - if (gsn_valid) + + if (gsn_valid && !ti.lookup_override) ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + else if (arvif->nawds_enabled && is_mcast && !ti.lookup_override) + ti.meta_data_flags |= + u32_encode_bits(1, HTT_TCL_META_DATA_HOST_INSPECTED_MISSION); - ti.bss_ast_hash = dp_link_vif->ast_hash; - ti.bss_ast_idx = dp_link_vif->ast_idx; ti.dscp_tid_tbl_idx = 0; if (skb->ip_summed == CHECKSUM_PARTIAL && diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c index 02d3cadf03fe..eeabe9db776e 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c @@ -59,7 +59,9 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) | le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID); - tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx, + tcl_cmd->info4 = le32_encode_bits(ti->lookup_override, + HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE) | + le32_encode_bits(ti->bss_ast_idx, HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) | le32_encode_bits(ti->bss_ast_hash, HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h index 9d2b1552c2f5..c548a0fbf1bb 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h @@ -34,6 +34,7 @@ struct hal_tx_info { u8 dscp_tid_tbl_idx; bool enable_mesh; int bank_id; + bool lookup_override; }; /* TODO: Check if the actual desc macros can be used instead */ diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 18f91051199c..d9f1b4615575 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4192,7 +4192,8 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); - wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); + wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | + WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index a827b3c99f8f..13d82f706d79 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2334,6 +2334,14 @@ enum wmi_preamble { WMI_VDEV_PREAMBLE_SHORT = 2, }; +/* + * This will be used to set for WMI_VDEV_PARAM_AP_ENABLE_NAWDS + * whenever 4addr station connects in wds offload case. + * This is for enabling multicast to unicast conversion support in + * firmware + */ +#define WDS_EXT_ENABLE 1 + enum wmi_peer_4addr_allow_frame { WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, @@ -2559,6 +2567,7 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) +#define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { -- cgit v1.2.3 From 6d0572f61539c5d4e2971139e7b501e37b7632d6 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:40 +0530 Subject: wifi: ath12k: Add support for 4-address NULL frame handling Currently, the firmware processes all NULL frames internally and does not forward them to the host. As a result, the host never receives 4-address NULL frames sent by a 4-address station. These 4-address NULL frames are sent by the station to indicate to the AP that it is operating in 4-address mode. Enable WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT flag during WMI initialization after verifying the WMI_SERVICE_WDS_NULL_FRAME_SUPPORT service capability. This enables the firmware to forward all NULL frames to the host. Add host-side handling to parse 4-address NULL frames and forward them to mac80211 to support proper AP_VLAN interface creation. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Sarika Sharma Signed-off-by: Sarika Sharma Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-5-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/peer.c | 7 +++--- drivers/net/wireless/ath/ath12k/wmi.c | 44 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 3 +++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index f7f0f613b393..b5a0ba149a28 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -220,10 +220,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta->link[link_id]); /* TODO: Split DP related field usage to DP peer structure */ arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | - u16_encode_bits(peer->peer_id, - HTT_TCL_META_DATA_PEER_ID) | - u16_encode_bits(0, - HTT_TCL_META_DATA_VALID_HTT); + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); arsta->ast_hash = peer->ast_hash; arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index d9f1b4615575..84a31b953db8 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4194,6 +4194,10 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); + + if (tg_cfg->is_wds_null_frame_supported) + wmi_cfg->flags2 |= + cpu_to_le32(WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, @@ -4403,6 +4407,9 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) ab->wmi_ab.svc_map)) arg.res_cfg.is_reg_cc_ext_event_supported = true; + if (test_bit(WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT, ab->wmi_ab.svc_map)) + arg.res_cfg.is_wds_null_frame_supported = true; + ab->hw_params->wmi_init(ab, &arg.res_cfg); ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; @@ -7224,7 +7231,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) struct ath12k_wmi_mgmt_rx_arg rx_ev = {}; struct ath12k *ar; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_sta *pubsta = NULL; + struct ath12k_dp_link_peer *peer; struct ieee80211_hdr *hdr; + bool is_4addr_null_pkt; + struct ath12k_dp *dp; u16 fc; struct ieee80211_supported_band *sband; s32 noise_floor; @@ -7299,6 +7310,38 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); + is_4addr_null_pkt = (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) && + ieee80211_has_a4(hdr->frame_control); + + /* + * Add check to drop frames other than 4-address NULL frame. Since + * firmware sends all NULL frames in this path (3-address and 4-address) + */ + if (ieee80211_is_data(hdr->frame_control) && !is_4addr_null_pkt) { + dev_kfree_skb(skb); + goto exit; + } + + if (is_4addr_null_pkt) { + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_pdev_and_addr(dp, ar->pdev_idx, + hdr->addr2); + if (!peer) { + spin_unlock_bh(&dp->dp_lock); + dev_kfree_skb(skb); + goto exit; + } + pubsta = peer->sta; + if (pubsta && pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + spin_unlock_bh(&dp->dp_lock); + goto send_rx; + } + /* Firmware is guaranteed to report all essential management frames via * WMI while it can deliver some extra via HTT. Since there can be * duplicates split the reporting wrt monitor/sniffing. @@ -7322,6 +7365,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (ieee80211_is_beacon(hdr->frame_control)) ath12k_mac_handle_beacon(ar, skb); +send_rx: ath12k_dbg(ab, ATH12K_DBG_MGMT, "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 13d82f706d79..c452e3d57a29 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2278,6 +2278,7 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365, WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410, + WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT = 421, WMI_TLV_SERVICE_IS_TARGET_IPA = 425, WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426, WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429, @@ -2511,6 +2512,7 @@ struct ath12k_wmi_resource_config_arg { u32 ema_max_vap_cnt; u32 ema_max_profile_period; bool is_reg_cc_ext_event_supported; + bool is_wds_null_frame_supported; }; struct ath12k_wmi_init_cmd_arg { @@ -2568,6 +2570,7 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) #define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) +#define WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT BIT(22) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { -- cgit v1.2.3 From f818260ac66b2971a2a587ea08b171b135a2c1e6 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:41 +0530 Subject: wifi: ath12k: Add support for 4-address frame notification mac80211 currently relies on receiving 4-address frames from connected stations to trigger AP_VLAN interface creation. However, when ethernet encapsulation offload is enabled, mac80211 only receives 802.3 frames and cannot differentiate between 3-address and 4-address formats, preventing AP_VLAN creation. Enable mac80211 to detect 4-address traffic by converting 802.3 frames back into 802.11 frames in the driver and setting the FROM_DS and TO_DS bits using the RX_MSDU_END_INFO5_FROM_DS and RX_MSDU_END_INFO5_TO_DS fields. This restores 4-address frame visibility to mac80211 and allows it to trigger AP_VLAN interface creation. Skip this frame conversion once the AP_VLAN interface is created and the station is attached to it. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-6-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.h | 2 ++ drivers/net/wireless/ath/ath12k/dp_rx.c | 10 +++++- drivers/net/wireless/ath/ath12k/dp_rx.h | 3 +- drivers/net/wireless/ath/ath12k/hal.h | 4 ++- drivers/net/wireless/ath/ath12k/mac.c | 1 + drivers/net/wireless/ath/ath12k/peer.c | 3 ++ drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 38 +++++++++++++++++----- .../net/wireless/ath/ath12k/wifi7/hal_qcc2072.c | 16 +++++++++ .../net/wireless/ath/ath12k/wifi7/hal_qcn9274.c | 16 +++++++++ .../net/wireless/ath/ath12k/wifi7/hal_wcn7850.c | 16 +++++++++ 10 files changed, 97 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 113b8040010f..f5067e66f1e1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -143,6 +143,8 @@ struct ath12k_dp_peer { struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + + bool use_4addr; }; struct ath12k_dp_link_peer * diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 8b69afc69ad4..06e74124e57e 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1142,7 +1142,8 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer) { enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type; struct ethhdr *ehdr; @@ -1166,6 +1167,13 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu break; } + if (peer && !peer->use_4addr && + rx_info->is_from_ds && rx_info->is_to_ds) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + /* PN for mcast packets will be validated in mac80211; * remove eth header and add 802.11 header. */ diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 55a31e669b3b..0660db070e92 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -190,7 +190,8 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info); + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer); void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info); diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index bf4f7dbae866..21c551d8b248 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -738,7 +738,9 @@ struct hal_rx_desc_data { addr2_present:1, is_mcbc:1, seq_ctl_valid:1, - fc_valid:1; + fc_valid:1, + is_to_ds:1, + is_from_ds:1; u16 msdu_len; u16 peer_id; u16 seq_no; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2c1eadf548ac..a6e4b660da81 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6630,6 +6630,7 @@ skip_nawds: arsta->addr); if (peer && peer->dp_peer) { peer->dp_peer->ucast_ra_only = true; + peer->dp_peer->use_4addr = true; } else { spin_unlock_bh(&dp->dp_lock); ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index b5a0ba149a28..c222bdaa333c 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -255,6 +255,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ar->hw_link_id); } + if (vif->type == NL80211_IFTYPE_AP && peer->dp_peer) + peer->dp_peer->ucast_ra_only = true; + return ret; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index a5e290edaa89..5c7eaaf200f9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -323,9 +323,9 @@ static void ath12k_wifi7_dp_rx_h_csum_offload(struct sk_buff *msdu, CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, - struct sk_buff *msdu, - struct hal_rx_desc_data *rx_info) +static int ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) { struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; @@ -347,6 +347,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) { + /* + * Drop the 3-address multicast packet from 4-address + * peer To avoid receiving the duplicate multicast packet + * Specifically from AP interface in 3-address format + */ + if (rxcb->is_mcbc && + rx_info->decap_type == DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (peer->use_4addr && + !(rx_info->is_from_ds && rx_info->is_to_ds)) + return -EINVAL; + } + /* resetting mcbc bit because mcbc packets are unicast * packets only for AP as STA sends unicast packets. */ @@ -387,15 +399,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_wifi7_dp_rx_h_csum_offload(msdu, rx_info); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info, + peer); if (!is_decrypted || rx_info->is_mcbc) - return; + return 0; if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } + + return 0; } static int ath12k_wifi7_dp_rx_msdu_coalesce(struct ath12k_hal *hal, @@ -554,7 +569,9 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + goto free_out; rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; @@ -1035,7 +1052,7 @@ mic_fail: ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, - rx_info); + rx_info, NULL); ieee80211_rx(ath12k_pdev_dp_to_hw(dp_pdev), msdu); return -EINVAL; } @@ -1590,6 +1607,7 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, u8 l3pad_bytes = rx_info->l3_pad_bytes; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + int ret; if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ @@ -1634,7 +1652,9 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, return -EINVAL; ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return ret; rxcb->tid = rx_info->tid; @@ -1680,7 +1700,7 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, RX_FLAG_DECRYPTED); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, false, - rx_info); + rx_info, NULL); return false; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c index 1eefb931a853..8cebb229ebed 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c @@ -184,6 +184,20 @@ static u8 ath12k_hal_rx_desc_get_l3_pad_bytes_qcc2072(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static u32 ath12k_hal_rx_desc_get_mpdu_start_tag_qcc2072(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcc2072.mpdu_start_tag, @@ -397,6 +411,8 @@ static void ath12k_hal_extract_rx_desc_data_qcc2072(struct hal_rx_desc_data *rx_ rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcc2072(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcc2072(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcc2072(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcc2072(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcc2072(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_qcc2072(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_qcc2072(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_qcc2072(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c index ba9ce1e718e8..9d5180ef83b4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c @@ -699,6 +699,20 @@ u8 ath12k_hal_rx_desc_get_mpdu_tid_qcn9274(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_TID); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline u16 ath12k_hal_rx_desc_get_mpdu_peer_id_qcn9274(struct hal_rx_desc *desc) { @@ -914,6 +928,8 @@ void ath12k_hal_extract_rx_desc_data_qcn9274(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_ctl_valid = ath12k_hal_rx_desc_get_mpdu_seq_ctl_vld_qcn9274(rx_desc); rx_desc_data->fc_valid = ath12k_hal_rx_desc_get_mpdu_fc_valid_qcn9274(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcn9274(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcn9274(rx_desc); rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcn9274(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcn9274(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcn9274(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c index e64e512cac7d..efbbc1cbd3e4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c @@ -280,6 +280,20 @@ u8 ath12k_hal_rx_desc_get_l3_pad_bytes_wcn7850(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline bool ath12k_hal_rx_desc_encrypt_valid_wcn7850(struct hal_rx_desc *desc) { @@ -599,6 +613,8 @@ void ath12k_hal_extract_rx_desc_data_wcn7850(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_wcn7850(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_wcn7850(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_wcn7850(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_wcn7850(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_wcn7850(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_wcn7850(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_wcn7850(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_wcn7850(rx_desc); -- cgit v1.2.3 From 565257a857690244211d85593b2cd490ce86783a Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:42 +0530 Subject: wifi: ath12k: Handle 4-address EAPOL frames from WBM error path Whenever hardware receives 4-address EAPOL frames from an unauthorized station it is routed through WBM/RXDMA error path with the HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR error code. But, the current driver does not handle the 4-address EAPOL frames in the WBM error path. As a result, these frames are dropped, causing authentication failures and connectivity issues for 4-address stations. Add support to correctly process these frames and forward them to mac80211 for proper handling. This prevents the loss of 4-address EAPOL frames and ensures reliable connectivity for WDS/4-address clients. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Sathishkumar Muruganandam Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-7-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 5c7eaaf200f9..cb9dd8cb28b6 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1704,6 +1704,56 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, return false; } +static bool ath12k_wifi7_dp_rx_h_unauth_wds_err(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) +{ + struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ath12k_dp *dp = dp_pdev->dp; + u32 hdr_len, hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + u8 l3pad_bytes = rx_info->l3_pad_bytes; + struct ath12k_dp_rx_rfc1042_hdr *llc; + u16 msdu_len = rx_info->msdu_len; + struct ath12k_dp_peer *peer; + struct ieee80211_hdr *hdr; + int ret; + + guard(rcu)(); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + if (!peer) { + ath12k_dbg(dp->ab, ATH12K_DBG_DATA, + "failed to find the peer to process unauth wds err handling peer_id %d\n", + rxcb->peer_id); + return true; + } + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return true; + + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); + skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, + rx_info))) + return true; + + ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); + + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return true; + + rxcb->tid = rx_info->tid; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + hdr_len); + if (llc->snap_type != cpu_to_be16(ETH_P_PAE)) + return true; + + return false; +} + static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info) @@ -1715,6 +1765,9 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, dp->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR: + drop = ath12k_wifi7_dp_rx_h_unauth_wds_err(dp_pdev, msdu, rx_info); + break; case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: if (rx_info->err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { -- cgit v1.2.3