From 3d38faef0de1756994b3d95e47b2302842f729e2 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 10 Jan 2022 16:24:13 +0200 Subject: [PATCH 01/28] ath11k: add missing of_node_put() to avoid leak The node pointer is returned by of_find_node_by_type() or of_parse_phandle() with refcount incremented. Calling of_node_put() to aovid the refcount leak. Fixes: 6ac04bdc5edb ("ath11k: Use reserved host DDR addresses from DT for PCI devices") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211221114003.335557-1-yangyingliang@huawei.com --- drivers/net/wireless/ath/ath11k/mhi.c | 1 + drivers/net/wireless/ath/ath11k/qmi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index e4250ba8dfee2..cccaa348cf212 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -332,6 +332,7 @@ static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl) return -ENOENT; ret = of_address_to_resource(np, 0, &res); + of_node_put(np); if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 65d3c6ba35ae6..42c2ad3e36683 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1936,6 +1936,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } ret = of_address_to_resource(hremote_node, 0, &res); + of_node_put(hremote_node); if (ret) { ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi fail to get reg from hremote\n"); From e3fb3d4418fce5484dfe7995fcd94c18b10a431a Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Mon, 10 Jan 2022 16:24:13 +0200 Subject: [PATCH 02/28] ath10k: fix memory overwrite of the WoWLAN wakeup packet pattern In function ath10k_wow_convert_8023_to_80211(), it will do memcpy for the new->pattern, and currently the new->pattern and new->mask is same with the old, then the memcpy of new->pattern will also overwrite the old->pattern, because the header format of new->pattern is 802.11, its length is larger than the old->pattern which is 802.3. Then the operation of "Copy frame body" will copy a mistake value because the body memory has been overwrite when memcpy the new->pattern. Assign another empty value to new_pattern to avoid the overwrite issue. Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00049 Fixes: fa3440fa2fa1 ("ath10k: convert wow pattern from 802.3 to 802.11") Signed-off-by: Wen Gong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211222031347.25463-1-quic_wgong@quicinc.com --- drivers/net/wireless/ath/ath10k/wow.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 7d65c115669fe..20b9aa8ddf7d5 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -337,14 +337,15 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, if (patterns[i].mask[j / 8] & BIT(j % 8)) bitmask[j] = 0xff; old_pattern.mask = bitmask; - new_pattern = old_pattern; if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { - if (patterns[i].pkt_offset < ETH_HLEN) + if (patterns[i].pkt_offset < ETH_HLEN) { ath10k_wow_convert_8023_to_80211(&new_pattern, &old_pattern); - else + } else { + new_pattern = old_pattern; new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; + } } if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) From 9f4ecacf2fa47b8aadd9bca2e88cde01856de028 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Mon, 10 Jan 2022 16:24:14 +0200 Subject: [PATCH 03/28] ath11k: fix workqueue not getting destroyed after rmmod Currently, ath11k_core_alloc() creates a single thread workqueue. This workqueue is not detroyed during clean up when ath11k modules are unloaded from the kernel and is left as it is. If workqueue is not destroyed, it could lead to kernel memory scarcity in a longer run. This could affect self and other drivers workability as well. Add destroy workqueue in ath11k_core_free(). Tested on: IPQ8074 WLAN.HK.2.4.0.1-01746-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1640231787-17408-1-git-send-email-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 293563b3f7842..1ad77bd721b97 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1404,6 +1405,9 @@ EXPORT_SYMBOL(ath11k_core_deinit); void ath11k_core_free(struct ath11k_base *ab) { + flush_workqueue(ab->workqueue); + destroy_workqueue(ab->workqueue); + kfree(ab); } EXPORT_SYMBOL(ath11k_core_free); From fbed57d897f6ea065c45806959337a6f28d2a94d Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Mon, 10 Jan 2022 16:24:14 +0200 Subject: [PATCH 04/28] ath11k: Refactor the fallback routine when peer create fails When there is an error in peer create process from ath11k_peer_find(), the code attempts to handle a fallback for peer create. When this fallback fails, the driver returns the fallback return code rather than actual error code (-ENOENT). So refactor the fallback routine to return the actual error code. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01067-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1640244819-21183-1-git-send-email-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath11k/peer.c | 40 +++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 85471f8b3563c..332886bc6b331 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -252,7 +252,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, { struct ath11k_peer *peer; struct ath11k_sta *arsta; - int ret; + int ret, fbret; lockdep_assert_held(&ar->conf_mutex); @@ -291,22 +291,8 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", param->peer_addr, param->vdev_id); - reinit_completion(&ar->peer_delete_done); - - ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, - param->vdev_id); - if (ret) { - ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", - param->vdev_id, param->peer_addr); - return ret; - } - - ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, - param->peer_addr); - if (ret) - return ret; - - return -ENOENT; + ret = -ENOENT; + goto cleanup; } peer->pdev_idx = ar->pdev_idx; @@ -335,4 +321,24 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, spin_unlock_bh(&ar->ab->base_lock); return 0; + +cleanup: + reinit_completion(&ar->peer_delete_done); + + fbret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, + param->vdev_id); + if (fbret) { + ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", + param->vdev_id, param->peer_addr); + goto exit; + } + + fbret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, + param->peer_addr); + if (fbret) + ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d fallback ret %d\n", + param->peer_addr, param->vdev_id, fbret); + +exit: + return ret; } From b3febdccde3eb086b892485c923cd6b73b2ce9d7 Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Tue, 11 Jan 2022 16:42:52 +0200 Subject: [PATCH 05/28] ath11k: add LDPC FEC type in 802.11 radiotap header LDPC is one the FEC type advertised in msdu_start info2 for HT packet type. Hence, add hardware specific callback for fetching LDPC support from msdu start and enable RX_ENC_FLAG_LDPC flag while passing rx status to mac80211. Tested-on: IPQ8074 WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 Signed-off-by: P Praneesh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1638294648-844-3-git-send-email-quic_ppranees@quicinc.com --- drivers/net/wireless/ath/ath11k/dp_rx.c | 12 +++++++++++- drivers/net/wireless/ath/ath11k/hw.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath11k/hw.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index c212a789421ee..46d8ab71b59f4 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -42,6 +42,13 @@ static inline u8 ath11k_dp_rx_h_msdu_start_decap_type(struct ath11k_base *ab, return ab->hw_params.hw_ops->rx_desc_get_decap_type(desc); } +static inline +bool ath11k_dp_rx_h_msdu_start_ldpc_support(struct ath11k_base *ab, + struct hal_rx_desc *desc) +{ + return ab->hw_params.hw_ops->rx_desc_get_ldpc_support(desc); +} + static inline u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab, struct hal_rx_desc *desc) @@ -2313,7 +2320,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, u8 bw; u8 rate_mcs, nss; u8 sgi; - bool is_cck; + bool is_cck, is_ldpc; pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(ar->ab, rx_desc); bw = ath11k_dp_rx_h_msdu_start_rx_bw(ar->ab, rx_desc); @@ -2355,6 +2362,9 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, if (sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + is_ldpc = ath11k_dp_rx_h_msdu_start_ldpc_support(ar->ab, rx_desc); + if (is_ldpc) + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; break; case RX_MSDU_START_PKT_TYPE_11AX: rx_status->rate_idx = rate_mcs; diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index 3b0fdc1a6b3f4..b7e3b668a9c01 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -273,6 +273,12 @@ static u8 ath11k_hw_ipq8074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); } +static bool ath11k_hw_ipq8074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); +} + static bool ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) { return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, @@ -444,6 +450,12 @@ static u8 ath11k_hw_qcn9074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); } +static bool ath11k_hw_qcn9074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); +} + static bool ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) { return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID, @@ -815,6 +827,7 @@ const struct ath11k_hw_ops ipq8074_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -853,6 +866,7 @@ const struct ath11k_hw_ops ipq6018_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -891,6 +905,7 @@ const struct ath11k_hw_ops qca6390_ops = { .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, @@ -929,6 +944,7 @@ const struct ath11k_hw_ops qcn9074_ops = { .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, .rx_desc_get_mpdu_start_seq_no = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 29934b36c14e4..e06d392be2fbe 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -210,6 +210,7 @@ struct ath11k_hw_ops { u32 (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc); u8 (*rx_desc_get_decap_type)(struct hal_rx_desc *desc); u8 (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc); + bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc); bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc); bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc); u16 (*rx_desc_get_mpdu_start_seq_no)(struct hal_rx_desc *desc); From 212ad7cb7d7592669c067125949e0a8e31ce6a0b Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Tue, 11 Jan 2022 16:42:52 +0200 Subject: [PATCH 06/28] ath11k: free peer for station when disconnect from AP for QCA6390/WCN6855 Commit b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") is to fix firmware crash by changing the WMI command sequence, but actually skip all the peer delete operation, then it lead commit 58595c9874c6 ("ath11k: Fixing dangling pointer issue upon peer delete failure") not take effect, and then happened a use-after-free warning from KASAN. because the peer->sta is not set to NULL and then used later. Change to only skip the WMI_PEER_DELETE_CMDID for QCA6390/WCN6855. log of user-after-free: [ 534.888665] BUG: KASAN: use-after-free in ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] [ 534.888696] Read of size 8 at addr ffff8881396bb1b8 by task rtcwake/2860 [ 534.888705] CPU: 4 PID: 2860 Comm: rtcwake Kdump: loaded Tainted: G W 5.15.0-wt-ath+ #523 [ 534.888712] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021 [ 534.888716] Call Trace: [ 534.888720] [ 534.888726] dump_stack_lvl+0x57/0x7d [ 534.888736] print_address_description.constprop.0+0x1f/0x170 [ 534.888745] ? ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] [ 534.888771] kasan_report.cold+0x83/0xdf [ 534.888783] ? ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] [ 534.888810] ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] [ 534.888840] ath11k_dp_rx_process_mon_status+0x529/0xa70 [ath11k] [ 534.888874] ? ath11k_dp_rx_mon_status_bufs_replenish+0x3f0/0x3f0 [ath11k] [ 534.888897] ? check_prev_add+0x20f0/0x20f0 [ 534.888922] ? __lock_acquire+0xb72/0x1870 [ 534.888937] ? find_held_lock+0x33/0x110 [ 534.888954] ath11k_dp_rx_process_mon_rings+0x297/0x520 [ath11k] [ 534.888981] ? rcu_read_unlock+0x40/0x40 [ 534.888990] ? ath11k_dp_rx_pdev_alloc+0xd90/0xd90 [ath11k] [ 534.889026] ath11k_dp_service_mon_ring+0x67/0xe0 [ath11k] [ 534.889053] ? ath11k_dp_rx_process_mon_rings+0x520/0x520 [ath11k] [ 534.889075] call_timer_fn+0x167/0x4a0 [ 534.889084] ? add_timer_on+0x3b0/0x3b0 [ 534.889103] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 [ 534.889117] __run_timers.part.0+0x539/0x8b0 [ 534.889123] ? ath11k_dp_rx_process_mon_rings+0x520/0x520 [ath11k] [ 534.889157] ? call_timer_fn+0x4a0/0x4a0 [ 534.889164] ? mark_lock_irq+0x1c30/0x1c30 [ 534.889173] ? clockevents_program_event+0xdd/0x280 [ 534.889189] ? mark_held_locks+0xa5/0xe0 [ 534.889203] run_timer_softirq+0x97/0x180 [ 534.889213] __do_softirq+0x276/0x86a [ 534.889230] __irq_exit_rcu+0x11c/0x180 [ 534.889238] irq_exit_rcu+0x5/0x20 [ 534.889244] sysvec_apic_timer_interrupt+0x8e/0xc0 [ 534.889251] [ 534.889254] [ 534.889259] asm_sysvec_apic_timer_interrupt+0x12/0x20 [ 534.889265] RIP: 0010:_raw_spin_unlock_irqrestore+0x38/0x70 [ 534.889271] Code: 74 24 10 e8 ea c2 bf fd 48 89 ef e8 12 53 c0 fd 81 e3 00 02 00 00 75 25 9c 58 f6 c4 02 75 2d 48 85 db 74 01 fb bf 01 00 00 00 13 a7 b5 fd 65 8b 05 cc d9 9c 5e 85 c0 74 0a 5b 5d c3 e8 a0 ee [ 534.889276] RSP: 0018:ffffc90002e5f880 EFLAGS: 00000206 [ 534.889284] RAX: 0000000000000006 RBX: 0000000000000200 RCX: ffffffff9f256f10 [ 534.889289] RDX: 0000000000000000 RSI: ffffffffa1c6e420 RDI: 0000000000000001 [ 534.889293] RBP: ffff8881095e6200 R08: 0000000000000001 R09: ffffffffa40d2b8f [ 534.889298] R10: fffffbfff481a571 R11: 0000000000000001 R12: ffff8881095e6e68 [ 534.889302] R13: ffffc90002e5f908 R14: 0000000000000246 R15: 0000000000000000 [ 534.889316] ? mark_lock+0xd0/0x14a0 [ 534.889332] klist_next+0x1d4/0x450 [ 534.889340] ? dpm_wait_for_subordinate+0x2d0/0x2d0 [ 534.889350] device_for_each_child+0xa8/0x140 [ 534.889360] ? device_remove_class_symlinks+0x1b0/0x1b0 [ 534.889370] ? __lock_release+0x4bd/0x9f0 [ 534.889378] ? dpm_suspend+0x26b/0x3f0 [ 534.889390] dpm_wait_for_subordinate+0x82/0x2d0 [ 534.889400] ? dpm_for_each_dev+0xa0/0xa0 [ 534.889410] ? dpm_suspend+0x233/0x3f0 [ 534.889427] __device_suspend+0xd4/0x10c0 [ 534.889440] ? wait_for_completion_io+0x270/0x270 [ 534.889456] ? async_suspend_late+0xe0/0xe0 [ 534.889463] ? async_schedule_node_domain+0x468/0x640 [ 534.889482] dpm_suspend+0x25a/0x3f0 [ 534.889491] ? dpm_suspend_end+0x1a0/0x1a0 [ 534.889497] ? ktime_get+0x214/0x2f0 [ 534.889502] ? lockdep_hardirqs_on+0x79/0x100 [ 534.889509] ? recalibrate_cpu_khz+0x10/0x10 [ 534.889516] ? ktime_get+0x119/0x2f0 [ 534.889528] dpm_suspend_start+0xab/0xc0 [ 534.889538] suspend_devices_and_enter+0x1ca/0x350 [ 534.889546] ? suspend_enter+0x850/0x850 [ 534.889566] enter_state+0x27c/0x3d7 [ 534.889575] pm_suspend.cold+0x42/0x189 [ 534.889583] state_store+0xab/0x160 [ 534.889595] ? sysfs_file_ops+0x160/0x160 [ 534.889601] kernfs_fop_write_iter+0x2b5/0x450 [ 534.889615] new_sync_write+0x36a/0x600 [ 534.889625] ? new_sync_read+0x600/0x600 [ 534.889639] ? rcu_read_unlock+0x40/0x40 [ 534.889668] vfs_write+0x619/0x910 [ 534.889681] ksys_write+0xf4/0x1d0 [ 534.889689] ? __ia32_sys_read+0xa0/0xa0 [ 534.889699] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 [ 534.889707] ? syscall_enter_from_user_mode+0x1d/0x50 [ 534.889719] do_syscall_64+0x3b/0x90 [ 534.889725] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 534.889731] RIP: 0033:0x7f0b9bc931e7 [ 534.889736] Code: 64 89 02 48 c7 c0 ff ff ff ff eb bb 0f 1f 80 00 00 00 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 [ 534.889741] RSP: 002b:00007ffd9d34cc88 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 534.889749] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f0b9bc931e7 [ 534.889753] RDX: 0000000000000004 RSI: 0000561cd023c5f0 RDI: 0000000000000004 [ 534.889757] RBP: 0000561cd023c5f0 R08: 0000000000000000 R09: 0000000000000004 [ 534.889761] R10: 0000561ccef842a6 R11: 0000000000000246 R12: 0000000000000004 [ 534.889765] R13: 0000561cd0239590 R14: 00007f0b9bd6f4a0 R15: 00007f0b9bd6e8a0 [ 534.889789] [ 534.889796] Allocated by task 2711: [ 534.889800] kasan_save_stack+0x1b/0x40 [ 534.889805] __kasan_kmalloc+0x7c/0x90 [ 534.889810] sta_info_alloc+0x98/0x1ef0 [mac80211] [ 534.889874] ieee80211_prep_connection+0x30b/0x11e0 [mac80211] [ 534.889950] ieee80211_mgd_auth+0x529/0xe00 [mac80211] [ 534.890024] cfg80211_mlme_auth+0x332/0x6f0 [cfg80211] [ 534.890090] nl80211_authenticate+0x839/0xcf0 [cfg80211] [ 534.890147] genl_family_rcv_msg_doit+0x1f4/0x2f0 [ 534.890154] genl_rcv_msg+0x280/0x500 [ 534.890160] netlink_rcv_skb+0x11c/0x340 [ 534.890165] genl_rcv+0x1f/0x30 [ 534.890170] netlink_unicast+0x42b/0x700 [ 534.890176] netlink_sendmsg+0x71b/0xc60 [ 534.890181] sock_sendmsg+0xdf/0x110 [ 534.890187] ____sys_sendmsg+0x5c0/0x850 [ 534.890192] ___sys_sendmsg+0xe4/0x160 [ 534.890197] __sys_sendmsg+0xb2/0x140 [ 534.890202] do_syscall_64+0x3b/0x90 [ 534.890207] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 534.890215] Freed by task 2825: [ 534.890218] kasan_save_stack+0x1b/0x40 [ 534.890223] kasan_set_track+0x1c/0x30 [ 534.890227] kasan_set_free_info+0x20/0x30 [ 534.890232] __kasan_slab_free+0xce/0x100 [ 534.890237] slab_free_freelist_hook+0xf0/0x1a0 [ 534.890242] kfree+0xe5/0x370 [ 534.890248] __sta_info_flush+0x333/0x4b0 [mac80211] [ 534.890308] ieee80211_set_disassoc+0x324/0xd20 [mac80211] [ 534.890382] ieee80211_mgd_deauth+0x537/0xee0 [mac80211] [ 534.890472] cfg80211_mlme_deauth+0x349/0x810 [cfg80211] [ 534.890526] cfg80211_mlme_down+0x1ce/0x270 [cfg80211] [ 534.890578] cfg80211_disconnect+0x4f5/0x7b0 [cfg80211] [ 534.890631] cfg80211_leave+0x24/0x40 [cfg80211] [ 534.890677] wiphy_suspend+0x23d/0x2f0 [cfg80211] [ 534.890723] dpm_run_callback+0xf4/0x1b0 [ 534.890728] __device_suspend+0x648/0x10c0 [ 534.890733] async_suspend+0x16/0xe0 [ 534.890737] async_run_entry_fn+0x90/0x4f0 [ 534.890741] process_one_work+0x866/0x1490 [ 534.890747] worker_thread+0x596/0x1010 [ 534.890751] kthread+0x35d/0x420 [ 534.890756] ret_from_fork+0x22/0x30 [ 534.890763] The buggy address belongs to the object at ffff8881396ba000 which belongs to the cache kmalloc-8k of size 8192 [ 534.890767] The buggy address is located 4536 bytes inside of 8192-byte region [ffff8881396ba000, ffff8881396bc000) [ 534.890772] The buggy address belongs to the page: [ 534.890775] page:ffffea0004e5ae00 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1396b8 [ 534.890780] head:ffffea0004e5ae00 order:3 compound_mapcount:0 compound_pincount:0 [ 534.890784] flags: 0x200000000010200(slab|head|node=0|zone=2) [ 534.890791] raw: 0200000000010200 ffffea000562be08 ffffea0004b04c08 ffff88810004e340 [ 534.890795] raw: 0000000000000000 0000000000010001 00000001ffffffff 0000000000000000 [ 534.890798] page dumped because: kasan: bad access detected [ 534.890804] Memory state around the buggy address: [ 534.890807] ffff8881396bb080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 534.890811] ffff8881396bb100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 534.890814] >ffff8881396bb180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 534.890817] ^ [ 534.890821] ffff8881396bb200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 534.890824] ffff8881396bb280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 534.890827] ================================================================== [ 534.890830] Disabling lock debugging due to kernel taint Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Fixes: b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") Signed-off-by: Wen Gong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211222070431.29595-1-quic_wgong@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 07f499d5ec92b..ac6a192185c19 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4504,24 +4504,30 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr, arvif->vdev_id); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { - ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && + vif->type == NL80211_IFTYPE_STATION; - if (ar->ab->hw_params.vdev_start_delay && - vif->type == NL80211_IFTYPE_STATION) - goto free; + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); - ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); - if (ret) - ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + if (!skip_peer_delete) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, + "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_dbg(ar->ab, + ATH11K_DBG_MAC, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } ath11k_mac_dec_num_stations(arvif, sta); spin_lock_bh(&ar->ab->base_lock); peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); - if (peer && peer->sta == sta) { + if (skip_peer_delete && peer) { + peer->sta = NULL; + } else if (peer && peer->sta == sta) { ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", vif->addr, arvif->vdev_id); peer->sta = NULL; @@ -4531,7 +4537,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, } spin_unlock_bh(&ar->ab->base_lock); -free: kfree(arsta->tx_stats); arsta->tx_stats = NULL; From 564d4eceb97eaf381dd6ef6470b06377bb50c95a Mon Sep 17 00:00:00 2001 From: Zekun Shen Date: Sun, 26 Dec 2021 22:12:13 -0500 Subject: [PATCH 07/28] ath5k: fix OOB in ath5k_eeprom_read_pcal_info_5111 The bug was found during fuzzing. Stacktrace locates it in ath5k_eeprom_convert_pcal_info_5111. When none of the curve is selected in the loop, idx can go up to AR5K_EEPROM_N_PD_CURVES. The line makes pd out of bound. pd = &chinfo[pier].pd_curves[idx]; There are many OOB writes using pd later in the code. So I added a sanity check for idx. Checks for other loops involving AR5K_EEPROM_N_PD_CURVES are not needed as the loop index is not used outside the loops. The patch is NOT tested with real device. The following is the fuzzing report BUG: KASAN: slab-out-of-bounds in ath5k_eeprom_read_pcal_info_5111+0x126a/0x1390 [ath5k] Write of size 1 at addr ffff8880174a4d60 by task modprobe/214 CPU: 0 PID: 214 Comm: modprobe Not tainted 5.6.0 #1 Call Trace: dump_stack+0x76/0xa0 print_address_description.constprop.0+0x16/0x200 ? ath5k_eeprom_read_pcal_info_5111+0x126a/0x1390 [ath5k] ? ath5k_eeprom_read_pcal_info_5111+0x126a/0x1390 [ath5k] __kasan_report.cold+0x37/0x7c ? ath5k_eeprom_read_pcal_info_5111+0x126a/0x1390 [ath5k] kasan_report+0xe/0x20 ath5k_eeprom_read_pcal_info_5111+0x126a/0x1390 [ath5k] ? apic_timer_interrupt+0xa/0x20 ? ath5k_eeprom_init_11a_pcal_freq+0xbc0/0xbc0 [ath5k] ? ath5k_pci_eeprom_read+0x228/0x3c0 [ath5k] ath5k_eeprom_init+0x2513/0x6290 [ath5k] ? ath5k_eeprom_init_11a_pcal_freq+0xbc0/0xbc0 [ath5k] ? usleep_range+0xb8/0x100 ? apic_timer_interrupt+0xa/0x20 ? ath5k_eeprom_read_pcal_info_2413+0x2f20/0x2f20 [ath5k] ath5k_hw_init+0xb60/0x1970 [ath5k] ath5k_init_ah+0x6fe/0x2530 [ath5k] ? kasprintf+0xa6/0xe0 ? ath5k_stop+0x140/0x140 [ath5k] ? _dev_notice+0xf6/0xf6 ? apic_timer_interrupt+0xa/0x20 ath5k_pci_probe.cold+0x29a/0x3d6 [ath5k] ? ath5k_pci_eeprom_read+0x3c0/0x3c0 [ath5k] ? mutex_lock+0x89/0xd0 ? ath5k_pci_eeprom_read+0x3c0/0x3c0 [ath5k] local_pci_probe+0xd3/0x160 pci_device_probe+0x23f/0x3e0 ? pci_device_remove+0x280/0x280 ? pci_device_remove+0x280/0x280 really_probe+0x209/0x5d0 Reported-by: Brendan Dolan-Gavitt Signed-off-by: Zekun Shen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/YckvDdj3mtCkDRIt@a-10-27-26-18.dynapool.vpn.nyu.edu --- drivers/net/wireless/ath/ath5k/eeprom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 1fbc2c19848f2..d444b3d70ba2e 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -746,6 +746,9 @@ ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, } } + if (idx == AR5K_EEPROM_N_PD_CURVES) + goto err_out; + ee->ee_pd_gains[mode] = 1; pd = &chinfo[pier].pd_curves[idx]; From 67a9d399fcb03177152a8797a855cbd4c995c2de Mon Sep 17 00:00:00 2001 From: Miles Hu Date: Wed, 12 Jan 2022 10:15:10 +0200 Subject: [PATCH 08/28] ath11k: enable RX PPDU stats in monitor co-exist mode RX PPDU statistics collection is missing when monitor mode co-exists with other modes. This commit combines the processing of the destination ring with the status ring to fix the issue. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 Signed-off-by: Miles Hu Signed-off-by: Aloka Dixit Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220111032224.14093-1-quic_alokad@quicinc.com --- drivers/net/wireless/ath/ath11k/debugfs.c | 6 ++ drivers/net/wireless/ath/ath11k/dp_rx.c | 84 +++++++---------------- drivers/net/wireless/ath/ath11k/hal_rx.h | 8 +-- 3 files changed, 34 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 198ade90b725c..215b6014c0efe 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -666,6 +666,12 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, goto exit; } + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { + ar->debug.extd_rx_stats = enable; + ret = count; + goto exit; + } + if (enable) { rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 46d8ab71b59f4..259e9709985b7 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -20,6 +20,9 @@ #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) +static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, + u32 quota, struct napi_struct *napi); + static inline u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) { @@ -3097,12 +3100,14 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, enum hal_rx_mon_status hal_status; struct sk_buff *skb; struct sk_buff_head skb_list; - struct hal_rx_mon_ppdu_info ppdu_info; struct ath11k_peer *peer; struct ath11k_sta *arsta; int num_buffs_reaped = 0; u32 rx_buf_sz; u16 log_type = 0; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; + struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; __skb_queue_head_init(&skb_list); @@ -3111,8 +3116,8 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, if (!num_buffs_reaped) goto exit; - memset(&ppdu_info, 0, sizeof(ppdu_info)); - ppdu_info.peer_id = HAL_INVALID_PEERID; + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; while ((skb = __skb_dequeue(&skb_list))) { if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { @@ -3126,9 +3131,18 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, if (log_type) trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); - hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); - if (ppdu_info.peer_id == HAL_INVALID_PEERID || + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + + if (ppdu_info->peer_id == HAL_INVALID_PEERID || hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { dev_kfree_skb_any(skb); continue; @@ -3136,17 +3150,17 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, rcu_read_lock(); spin_lock_bh(&ab->base_lock); - peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); + peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); if (!peer || !peer->sta) { ath11k_dbg(ab, ATH11K_DBG_DATA, "failed to find the peer with peer_id %d\n", - ppdu_info.peer_id); + ppdu_info->peer_id); goto next_skb; } arsta = (struct ath11k_sta *)peer->sta->drv_priv; - ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); + ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); @@ -3156,8 +3170,8 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, rcu_read_unlock(); dev_kfree_skb_any(skb); - memset(&ppdu_info, 0, sizeof(ppdu_info)); - ppdu_info.peer_id = HAL_INVALID_PEERID; + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; } exit: return num_buffs_reaped; @@ -5116,38 +5130,6 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, } } -static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, - int mac_id, u32 quota, - struct napi_struct *napi) -{ - struct ath11k_pdev_dp *dp = &ar->dp; - struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; - struct hal_rx_mon_ppdu_info *ppdu_info; - struct sk_buff *status_skb; - u32 tlv_status = HAL_TLV_STATUS_BUF_DONE; - struct ath11k_pdev_mon_stats *rx_mon_stats; - - ppdu_info = &pmon->mon_ppdu_info; - rx_mon_stats = &pmon->rx_mon_stats; - - if (pmon->mon_ppdu_status != DP_PPDU_STATUS_START) - return; - - while (!skb_queue_empty(&pmon->rx_status_q)) { - status_skb = skb_dequeue(&pmon->rx_status_q); - - tlv_status = ath11k_hal_rx_parse_mon_status(ar->ab, ppdu_info, - status_skb); - if (tlv_status == HAL_TLV_STATUS_PPDU_DONE) { - rx_mon_stats->status_ppdu_done++; - pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath11k_dp_rx_mon_dest_process(ar, mac_id, quota, napi); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; - } - dev_kfree_skb_any(status_skb); - } -} - static u32 ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, void *ring_entry, struct sk_buff **head_msdu, @@ -5499,22 +5481,6 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, return quota; } -static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, int budget) -{ - struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); - struct ath11k_pdev_dp *dp = &ar->dp; - struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; - int num_buffs_reaped = 0; - - num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ar->ab, mac_id, &budget, - &pmon->rx_status_q); - if (num_buffs_reaped) - ath11k_dp_rx_mon_status_process_tlv(ar, mac_id, budget, napi); - - return num_buffs_reaped; -} - int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, struct napi_struct *napi, int budget) { @@ -5524,8 +5490,6 @@ int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ab->hw_params.full_monitor_mode) ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); - else if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) - ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); else ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 571054c6d7f87..7bba4f0411e19 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -65,10 +65,6 @@ enum hal_rx_reception_type { HAL_RX_RECEPTION_TYPE_MAX, }; -#define HAL_TLV_STATUS_PPDU_NOT_DONE 0 -#define HAL_TLV_STATUS_PPDU_DONE 1 -#define HAL_TLV_STATUS_BUF_DONE 2 -#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3 #define HAL_RX_FCS_LEN 4 enum hal_rx_mon_status { @@ -77,6 +73,10 @@ enum hal_rx_mon_status { HAL_RX_MON_STATUS_BUF_DONE, }; +#define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE +#define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE +#define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE + struct hal_sw_mon_ring_entries { dma_addr_t mon_dst_paddr; dma_addr_t mon_status_paddr; From 3cd04a438754aaa4297a5561ad9149eda73ce14d Mon Sep 17 00:00:00 2001 From: Aloka Dixit Date: Wed, 12 Jan 2022 10:15:10 +0200 Subject: [PATCH 09/28] ath11k: move function ath11k_dp_rx_process_mon_status Move the function below ath11k_dp_rx_mon_dest_process() and remove the forward declaration. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aloka Dixit Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220111032224.14093-2-quic_alokad@quicinc.com --- drivers/net/wireless/ath/ath11k/dp_rx.c | 171 ++++++++++++------------ 1 file changed, 84 insertions(+), 87 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 259e9709985b7..89b77b94bb203 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -20,9 +20,6 @@ #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) -static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, - u32 quota, struct napi_struct *napi); - static inline u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) { @@ -3093,90 +3090,6 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, return num_buffs_reaped; } -int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, - struct napi_struct *napi, int budget) -{ - struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); - enum hal_rx_mon_status hal_status; - struct sk_buff *skb; - struct sk_buff_head skb_list; - struct ath11k_peer *peer; - struct ath11k_sta *arsta; - int num_buffs_reaped = 0; - u32 rx_buf_sz; - u16 log_type = 0; - struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; - struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; - struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - - __skb_queue_head_init(&skb_list); - - num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, - &skb_list); - if (!num_buffs_reaped) - goto exit; - - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; - - while ((skb = __skb_dequeue(&skb_list))) { - if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { - log_type = ATH11K_PKTLOG_TYPE_LITE_RX; - rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; - } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { - log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; - rx_buf_sz = DP_RX_BUFFER_SIZE; - } - - if (log_type) - trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); - - hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); - - if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && - pmon->mon_ppdu_status == DP_PPDU_STATUS_START && - hal_status == HAL_TLV_STATUS_PPDU_DONE) { - rx_mon_stats->status_ppdu_done++; - pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; - } - - 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 = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); - - if (!peer || !peer->sta) { - ath11k_dbg(ab, ATH11K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - goto next_skb; - } - - arsta = (struct ath11k_sta *)peer->sta->drv_priv; - ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); - - if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) - trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); - -next_skb: - 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; - } -exit: - return num_buffs_reaped; -} - static void ath11k_dp_rx_frag_timer(struct timer_list *timer) { struct dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); @@ -5130,6 +5043,90 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, } } +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); + enum hal_rx_mon_status hal_status; + struct sk_buff *skb; + struct sk_buff_head skb_list; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + u32 rx_buf_sz; + u16 log_type = 0; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; + struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; + + __skb_queue_head_init(&skb_list); + + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, + &skb_list); + if (!num_buffs_reaped) + goto exit; + + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + + while ((skb = __skb_dequeue(&skb_list))) { + if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_LITE_RX; + rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; + } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; + rx_buf_sz = DP_RX_BUFFER_SIZE; + } + + if (log_type) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); + + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + + 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 = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); + + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + +next_skb: + 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; + } +exit: + return num_buffs_reaped; +} + static u32 ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, void *ring_entry, struct sk_buff **head_msdu, From c9b41832dc080fa59bad597de94865b3ea2d5bab Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 10/28] ath11k: fix error code in ath11k_qmi_assign_target_mem_chunk() The "ret" vairable is not set at this point. It could be uninitialized or zero. The correct thing to return is -ENODEV. Fixes: 6ac04bdc5edb ("ath11k: Use reserved host DDR addresses from DT for PCI devices") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220111071445.GA11243@kili --- drivers/net/wireless/ath/ath11k/qmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 42c2ad3e36683..d0701e8eca9c0 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1932,7 +1932,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) if (!hremote_node) { ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi fail to get hremote_node\n"); - return ret; + return -ENODEV; } ret = of_address_to_resource(hremote_node, 0, &res); From 5b747459477b4d7fc9c36dc8b8134859d905e52d Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 11/28] ath5k: remove unused ah_txq_isr_qtrig member from struct ath5k_hw Remove unused ah_txq_isr_qtrig member from struct ath5k_hw (set in ath5k_hw_get_isr() but never used anywhere). Signed-off-by: Peter Seiderer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220110223021.17655-1-ps.report@gmx.net --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 - drivers/net/wireless/ath/ath5k/dma.c | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 234ea939d3162..db6ba433c05d7 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1398,7 +1398,6 @@ struct ath5k_hw { u32 ah_txq_isr_txurn; u32 ah_txq_isr_qcborn; u32 ah_txq_isr_qcburn; - u32 ah_txq_isr_qtrig; u32 *ah_rf_banks; size_t ah_rf_banks_size; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index e6c52f7c26e73..78b87372da95a 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -723,11 +723,8 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) } /* A queue got triggered */ - if (unlikely(pisr & (AR5K_ISR_QTRIG))) { + if (unlikely(pisr & (AR5K_ISR_QTRIG))) *interrupt_mask |= AR5K_INT_QTRIG; - ah->ah_txq_isr_qtrig |= AR5K_REG_MS(sisr4, - AR5K_SISR4_QTRIG); - } data = pisr; } From bcc08e05041ebdd6506dff5c88c4e523d68b1d96 Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 12/28] ath5k: remove unused ah_txq_isr_qcburn member from struct ath5k_hw Remove unused ah_txq_isr_qcburn member from struct ath5k_hw (set in ath5k_hw_get_isr() but never used anywhere). Signed-off-by: Peter Seiderer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220110223021.17655-2-ps.report@gmx.net --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 - drivers/net/wireless/ath/ath5k/dma.c | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index db6ba433c05d7..72ba36a048371 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1397,7 +1397,6 @@ struct ath5k_hw { u32 ah_txq_isr_txok_all; u32 ah_txq_isr_txurn; u32 ah_txq_isr_qcborn; - u32 ah_txq_isr_qcburn; u32 *ah_rf_banks; size_t ah_rf_banks_size; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 78b87372da95a..faea3262368f8 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -716,11 +716,8 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) } /* A queue got CBR underrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRURN))) { + if (unlikely(pisr & (AR5K_ISR_QCBRURN))) *interrupt_mask |= AR5K_INT_QCBRURN; - ah->ah_txq_isr_qcburn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRURN); - } /* A queue got triggered */ if (unlikely(pisr & (AR5K_ISR_QTRIG))) From 0feb4052ad47aefa07a163beb6b25343d355e2eb Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 13/28] ath5k: remove unused ah_txq_isr_qcborn member from struct ath5k_hw Remove unused ah_txq_isr_qcborn member from struct ath5k_hw (set in ath5k_hw_get_isr() but never used anywhere). Signed-off-by: Peter Seiderer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220110223021.17655-3-ps.report@gmx.net --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 - drivers/net/wireless/ath/ath5k/dma.c | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 72ba36a048371..ac3211b1eb0cb 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1396,7 +1396,6 @@ struct ath5k_hw { u32 ah_txq_isr_txok_all; u32 ah_txq_isr_txurn; - u32 ah_txq_isr_qcborn; u32 *ah_rf_banks; size_t ah_rf_banks_size; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index faea3262368f8..0ee46f8118bb0 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -709,11 +709,8 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) *interrupt_mask |= AR5K_INT_BNR; /* A queue got CBR overrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRORN))) { + if (unlikely(pisr & (AR5K_ISR_QCBRORN))) *interrupt_mask |= AR5K_INT_QCBRORN; - ah->ah_txq_isr_qcborn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRORN); - } /* A queue got CBR underrun */ if (unlikely(pisr & (AR5K_ISR_QCBRURN))) From 3296fe1a833980629b4345a571778447e8ad4f2f Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 14/28] ath5k: remove unused ah_txq_isr_txurn member from struct ath5k_hw Remove unused ah_txq_isr_txurn member from struct ath5k_hw (set in ath5k_hw_get_isr() but never used anywhere). Signed-off-by: Peter Seiderer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220110223021.17655-4-ps.report@gmx.net --- drivers/net/wireless/ath/ath5k/ath5k.h | 1 - drivers/net/wireless/ath/ath5k/dma.c | 7 ------- 2 files changed, 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index ac3211b1eb0cb..f595204f493d1 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1395,7 +1395,6 @@ struct ath5k_hw { u32 ah_txq_imr_nofrm; u32 ah_txq_isr_txok_all; - u32 ah_txq_isr_txurn; u32 *ah_rf_banks; size_t ah_rf_banks_size; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 0ee46f8118bb0..2b135a6284a09 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -670,13 +670,6 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, AR5K_SISR1_QCU_TXEOL); - /* Currently this is not much useful since we treat - * all queues the same way if we get a TXURN (update - * tx trigger level) but we might need it later on*/ - if (pisr & AR5K_ISR_TXURN) - ah->ah_txq_isr_txurn |= AR5K_REG_MS(sisr2, - AR5K_SISR2_QCU_TXURN); - /* Misc Beacon related interrupts */ /* For AR5211 */ From dff39ad93de8fad33bba798df2370ac0655311f0 Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 15/28] ath5k: fix ah_txq_isr_txok_all setting The struct ath5k_hw member ah_txq_isr_txok_all is never reset/assigned outside of ath5k_hw_get_isr() and with the used bitwise-or in the interrupt handling accumulates all ever set interrupt flags. Fix this by clearing ah_txq_isr_txok_all before assigning. Patch tested with Senao NMP-8602 card Qualcomm Atheros AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (rev 01) ath5k: phy6: Atheros AR5413 chip found (MAC: 0xa4, PHY: 0x61) running IBSS mode against Atheros (ath9k) card using ping and iperf traffic. Signed-off-by: Peter Seiderer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220110223021.17655-5-ps.report@gmx.net --- drivers/net/wireless/ath/ath5k/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 2b135a6284a09..d9e376eb040e7 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -650,6 +650,7 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) */ *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; + ah->ah_txq_isr_txok_all = 0; /* We treat TXOK,TXDESC, TXERR and TXEOL * the same way (schedule the tx tasklet) From f14c3f4db9cb098db70651434588ea519930f5d6 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 24 Dec 2021 19:26:25 +0000 Subject: [PATCH 16/28] ath10k: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypasses the hierarchical setup and messes up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Lad Prabhakar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211224192626.15843-8-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/net/wireless/ath/ath10k/snoc.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 9513ab696fff1..681e1abe74400 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1306,13 +1306,10 @@ static int ath10k_snoc_resource_init(struct ath10k *ar) } for (i = 0; i < CE_COUNT; i++) { - res = platform_get_resource(ar_snoc->dev, IORESOURCE_IRQ, i); - if (!res) { - ath10k_err(ar, "failed to get IRQ%d\n", i); - ret = -ENODEV; - goto out; - } - ar_snoc->ce_irqs[i].irq_line = res->start; + ret = platform_get_irq(ar_snoc->dev, i); + if (ret < 0) + return ret; + ar_snoc->ce_irqs[i].irq_line = ret; } ret = device_property_read_u32(&pdev->dev, "qcom,xo-cal-data", @@ -1323,10 +1320,8 @@ static int ath10k_snoc_resource_init(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_SNOC, "xo cal data %x\n", ar_snoc->xo_cal_data); } - ret = 0; -out: - return ret; + return 0; } static void ath10k_snoc_quirks_init(struct ath10k *ar) From a063b650ce5d72585d780d653a123f9f273b7877 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 27 Dec 2021 17:58:10 +0100 Subject: [PATCH 17/28] ath: dfs_pattern_detector: Avoid open coded arithmetic in memory allocation kmalloc_array()/kcalloc() should be used to avoid potential overflow when a multiplication is needed to compute the size of the requested memory. kmalloc_array() can be used here instead of kcalloc() because the array is fully initialized in the next 'for' loop. Finally, 'cd->detectors' is defined as 'struct pri_detector **detectors;'. So 'cd->detectors' and '*cd->detectors' are both some pointer. So use a more logical 'sizeof(*cd->detectors)'. Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/0fbcd32a0384ac1f87c5a3549e505e4becc60226.1640624216.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/ath/dfs_pattern_detector.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 75cb53a3ec15e..27f4d74a41c80 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -197,7 +197,7 @@ static void channel_detector_exit(struct dfs_pattern_detector *dpd, static struct channel_detector * channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { - u32 sz, i; + u32 i; struct channel_detector *cd; cd = kmalloc(sizeof(*cd), GFP_ATOMIC); @@ -206,8 +206,8 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) INIT_LIST_HEAD(&cd->head); cd->freq = freq; - sz = sizeof(cd->detectors) * dpd->num_radar_types; - cd->detectors = kzalloc(sz, GFP_ATOMIC); + cd->detectors = kmalloc_array(dpd->num_radar_types, + sizeof(*cd->detectors), GFP_ATOMIC); if (cd->detectors == NULL) goto fail; From dc7ff75690ea7e1920be08b2b9cc89647667bb90 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 12 Jan 2022 10:54:00 +0800 Subject: [PATCH 18/28] ath11k: Reconfigure hardware rate for WCN6855 after vdev is started There is an issue that WCN6855 tries to connect to an AP using a hardware rate of 1Mb/s , even though the AP has announced expected rates as [24, 36, 48, 54] in Probe Response frame. The reason is that WCN6855 firmware clears hardware rate info of management frames when vdev starts and uses 1Mb/s as default. To solve it, reconfigure the rate after vdev is started. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220112025400.2222-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/mac.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 9e88ccca5ca75..4eda15c56b04b 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -603,6 +603,7 @@ struct ath11k { struct completion finish_11d_ch_list; bool pending_11d; bool regdom_set_by_user; + int hw_rate_code; }; struct ath11k_band_cap { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index ac6a192185c19..02228c4da4d0c 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2862,6 +2862,11 @@ static void ath11k_recalculate_mgmt_rate(struct ath11k *ar, if (ret) ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + /* For WCN6855, firmware will clear this param when vdev starts, hence + * cache it here so that we can reconfigure it once vdev starts. + */ + ar->hw_rate_code = hw_rate_code; + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, hw_rate_code); @@ -6960,6 +6965,19 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, return ret; } + /* Reconfigure hardware rate code since it is cleared by firmware. + */ + if (ar->hw_rate_code > 0) { + u32 vdev_param = WMI_VDEV_PARAM_MGMT_RATE; + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + ar->hw_rate_code); + if (ret) { + ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + return ret; + } + } + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, 0, ar->mac_addr); if (ret) { From 76d7b996aab822c80a6f18c5e2bba1d4de46eab5 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Wed, 12 Jan 2022 08:07:51 +0000 Subject: [PATCH 19/28] ath9k: remove redundant status variable Return value directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: CGEL ZTE Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220112080751.667316-1-chi.minghao@zte.com.cn --- drivers/net/wireless/ath/ath9k/eeprom.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index e6b3cd49ea18b..efb7889142d47 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -670,8 +670,6 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, int ath9k_hw_eeprom_init(struct ath_hw *ah) { - int status; - if (AR_SREV_9300_20_OR_LATER(ah)) ah->eep_ops = &eep_ar9300_ops; else if (AR_SREV_9287(ah)) { @@ -685,7 +683,5 @@ int ath9k_hw_eeprom_init(struct ath_hw *ah) if (!ah->eep_ops->fill_eeprom(ah)) return -EIO; - status = ah->eep_ops->check_eeprom(ah); - - return status; + return ah->eep_ops->check_eeprom(ah); } From d17efe4f80fc729416b5a467b4c60448cee64bfb Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 24 Dec 2021 19:26:24 +0000 Subject: [PATCH 20/28] wcn36xx: Use platform_get_irq_byname() to get the interrupt platform_get_resource_byname(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypasses the hierarchical setup and messes up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq_byname(). Signed-off-by: Lad Prabhakar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211224192626.15843-7-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/net/wireless/ath/wcn36xx/main.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 9575d7373bf27..bd334a3020576 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1446,25 +1446,20 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, { struct device_node *mmio_node; struct device_node *iris_node; - struct resource *res; int index; int ret; /* Set TX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx"); - if (!res) { - wcn36xx_err("failed to get tx_irq\n"); - return -ENOENT; - } - wcn->tx_irq = res->start; + ret = platform_get_irq_byname(pdev, "tx"); + if (ret < 0) + return ret; + wcn->tx_irq = ret; /* Set RX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx"); - if (!res) { - wcn36xx_err("failed to get rx_irq\n"); - return -ENOENT; - } - wcn->rx_irq = res->start; + ret = platform_get_irq_byname(pdev, "rx"); + if (ret < 0) + return ret; + wcn->rx_irq = ret; /* Acquire SMSM tx enable handle */ wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev, From 6bae9de622d3ef4805aba40e763eb4b0975c4f6d Mon Sep 17 00:00:00 2001 From: Francesco Magliocca Date: Wed, 12 Jan 2022 10:15:11 +0200 Subject: [PATCH 21/28] ath10k: abstract htt_rx_desc structure QCA6174 card often hangs with the current htt_rx_desc memory layout in some circumstances, because its firmware fails to handle length differences. Therefore we must abstract the htt_rx_desc structure and operations on it, to allow different wireless cards to use different, unrelated rx descriptor structures. Define a base htt_rx_desc structure and htt_rx_desc_v1 for use with the QCA family of ath10k supported cards and htt_rx_desc_v2 for use with the WCN3990 card. Define htt_rx_desc_ops which contains the abstract operations to access the generic htt_rx_desc, give implementations for each card and update htt_rx.c to use the defined abstract interface to rx descriptors. Fixes: e3def6f7ddf8 ("ath10k: Update rx descriptor for WCN3990 target") Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00157-QCARMSWPZ-1 Co-developed-by: Enrico Lumetti Signed-off-by: Enrico Lumetti Signed-off-by: Francesco Magliocca Link: https://lore.kernel.org/ath10k/CAH4F6usFu8-A6k5Z7rU9__iENcSC6Zr-NtRhh_aypR74UvN1uQ@mail.gmail.com/ Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211216151823.68878-1-franciman12@gmail.com --- drivers/net/wireless/ath/ath10k/core.c | 16 ++ drivers/net/wireless/ath/ath10k/htt.c | 153 ++++++++++ drivers/net/wireless/ath/ath10k/htt.h | 296 +++++++++++++++++-- drivers/net/wireless/ath/ath10k/htt_rx.c | 331 +++++++++++++++------- drivers/net/wireless/ath/ath10k/htt_tx.c | 36 +-- drivers/net/wireless/ath/ath10k/hw.c | 15 +- drivers/net/wireless/ath/ath10k/hw.h | 27 +- drivers/net/wireless/ath/ath10k/rx_desc.h | 40 ++- 8 files changed, 722 insertions(+), 192 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 8f5b8eb368fab..9e1f483e1362a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -75,6 +75,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -111,6 +112,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA988X_BOARD_DATA_SZ, .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -148,6 +150,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9887_BOARD_DATA_SZ, .board_ext_size = QCA9887_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -184,6 +187,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_sdio_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -216,6 +220,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -252,6 +257,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -288,6 +294,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -325,6 +332,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -370,6 +378,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 4, @@ -415,6 +424,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .ext_board_size = QCA99X0_EXT_BOARD_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 12, @@ -461,6 +471,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 12, @@ -501,6 +512,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, .spectral_bin_discard = 0, @@ -537,6 +549,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -575,6 +588,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, + .rx_desc_ops = &qca988x_rx_desc_ops, .hw_ops = &qca6174_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, @@ -611,6 +625,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &qca99x0_rx_desc_ops, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, .spectral_bin_discard = 4, @@ -643,6 +658,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .dir = WCN3990_HW_1_0_FW_DIR, }, .sw_decrypt_mcast_mgmt = true, + .rx_desc_ops = &wcn3990_rx_desc_ops, .hw_ops = &wcn3990_ops, .decap_align_bytes = 1, .num_peers = TARGET_HL_TLV_NUM_PEERS, diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 127b4e4980ef6..907e1e13871ac 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -131,6 +131,159 @@ static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = { HTT_T2H_MSG_TYPE_PEER_STATS, }; +const struct ath10k_htt_rx_desc_ops qca988x_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v1), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload) +}; + +static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc = container_of(rxd, + struct htt_rx_desc_v1, + base); + + return MS(__le32_to_cpu(rx_desc->msdu_end.qca99x0.info1), + RX_MSDU_END_INFO1_L3_HDR_PAD); +} + +static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc = container_of(rxd, + struct htt_rx_desc_v1, + base); + + return !!(rx_desc->msdu_end.common.info0 & + __cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR)); +} + +const struct ath10k_htt_rx_desc_ops qca99x0_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v1), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload), + + .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, + .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error, +}; + +static void ath10k_rx_desc_wcn3990_get_offsets(struct htt_rx_ring_rx_desc_offsets *off) +{ +#define desc_offset(x) (offsetof(struct htt_rx_desc_v2, x) / 4) + off->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + off->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + off->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + off->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + off->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + off->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + off->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + off->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + off->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + off->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); +#undef desc_offset +} + +static struct htt_rx_desc * +ath10k_rx_desc_wcn3990_from_raw_buffer(void *buff) +{ + return &((struct htt_rx_desc_v2 *)buff)->base; +} + +static struct rx_attention * +ath10k_rx_desc_wcn3990_get_attention(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->attention; +} + +static struct rx_frag_info_common * +ath10k_rx_desc_wcn3990_get_frag_info(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->frag_info.common; +} + +static struct rx_mpdu_start * +ath10k_rx_desc_wcn3990_get_mpdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->mpdu_start; +} + +static struct rx_mpdu_end * +ath10k_rx_desc_wcn3990_get_mpdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->mpdu_end; +} + +static struct rx_msdu_start_common * +ath10k_rx_desc_wcn3990_get_msdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->msdu_start.common; +} + +static struct rx_msdu_end_common * +ath10k_rx_desc_wcn3990_get_msdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->msdu_end.common; +} + +static struct rx_ppdu_start * +ath10k_rx_desc_wcn3990_get_ppdu_start(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->ppdu_start; +} + +static struct rx_ppdu_end_common * +ath10k_rx_desc_wcn3990_get_ppdu_end(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return &rx_desc->ppdu_end.common; +} + +static u8 * +ath10k_rx_desc_wcn3990_get_rx_hdr_status(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return rx_desc->rx_hdr_status; +} + +static u8 * +ath10k_rx_desc_wcn3990_get_msdu_payload(struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base); + + return rx_desc->msdu_payload; +} + +const struct ath10k_htt_rx_desc_ops wcn3990_rx_desc_ops = { + .rx_desc_size = sizeof(struct htt_rx_desc_v2), + .rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v2, msdu_payload), + + .rx_desc_from_raw_buffer = ath10k_rx_desc_wcn3990_from_raw_buffer, + .rx_desc_get_offsets = ath10k_rx_desc_wcn3990_get_offsets, + .rx_desc_get_attention = ath10k_rx_desc_wcn3990_get_attention, + .rx_desc_get_frag_info = ath10k_rx_desc_wcn3990_get_frag_info, + .rx_desc_get_mpdu_start = ath10k_rx_desc_wcn3990_get_mpdu_start, + .rx_desc_get_mpdu_end = ath10k_rx_desc_wcn3990_get_mpdu_end, + .rx_desc_get_msdu_start = ath10k_rx_desc_wcn3990_get_msdu_start, + .rx_desc_get_msdu_end = ath10k_rx_desc_wcn3990_get_msdu_end, + .rx_desc_get_ppdu_start = ath10k_rx_desc_wcn3990_get_ppdu_start, + .rx_desc_get_ppdu_end = ath10k_rx_desc_wcn3990_get_ppdu_end, + .rx_desc_get_rx_hdr_status = ath10k_rx_desc_wcn3990_get_rx_hdr_status, + .rx_desc_get_msdu_payload = ath10k_rx_desc_wcn3990_get_msdu_payload, +}; + int ath10k_htt_connect(struct ath10k_htt *htt) { struct ath10k_htc_svc_conn_req conn_req; diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 9a3a8907389bb..f06cf39204e2c 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -240,14 +240,7 @@ enum htt_rx_ring_flags { #define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1) #define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1) -struct htt_rx_ring_setup_ring32 { - __le32 fw_idx_shadow_reg_paddr; - __le32 rx_ring_base_paddr; - __le16 rx_ring_len; /* in 4-byte words */ - __le16 rx_ring_bufsize; /* rx skb size - in bytes */ - __le16 flags; /* %HTT_RX_RING_FLAGS_ */ - __le16 fw_idx_init_val; - +struct htt_rx_ring_rx_desc_offsets { /* the following offsets are in 4-byte units */ __le16 mac80211_hdr_offset; __le16 msdu_payload_offset; @@ -261,6 +254,17 @@ struct htt_rx_ring_setup_ring32 { __le16 frag_info_offset; } __packed; +struct htt_rx_ring_setup_ring32 { + __le32 fw_idx_shadow_reg_paddr; + __le32 rx_ring_base_paddr; + __le16 rx_ring_len; /* in 4-byte words */ + __le16 rx_ring_bufsize; /* rx skb size - in bytes */ + __le16 flags; /* %HTT_RX_RING_FLAGS_ */ + __le16 fw_idx_init_val; + + struct htt_rx_ring_rx_desc_offsets offsets; +} __packed; + struct htt_rx_ring_setup_ring64 { __le64 fw_idx_shadow_reg_paddr; __le64 rx_ring_base_paddr; @@ -269,17 +273,7 @@ struct htt_rx_ring_setup_ring64 { __le16 flags; /* %HTT_RX_RING_FLAGS_ */ __le16 fw_idx_init_val; - /* the following offsets are in 4-byte units */ - __le16 mac80211_hdr_offset; - __le16 msdu_payload_offset; - __le16 ppdu_start_offset; - __le16 ppdu_end_offset; - __le16 mpdu_start_offset; - __le16 mpdu_end_offset; - __le16 msdu_start_offset; - __le16 msdu_end_offset; - __le16 rx_attention_offset; - __le16 frag_info_offset; + struct htt_rx_ring_rx_desc_offsets offsets; } __packed; struct htt_rx_ring_setup_hdr { @@ -2075,12 +2069,22 @@ static inline bool ath10k_htt_rx_proc_rx_frag_ind(struct ath10k_htt *htt, return htt->rx_ops->htt_rx_proc_rx_frag_ind(htt, rx, skb); } +/* the driver strongly assumes that the rx header status be 64 bytes long, + * so all possible rx_desc structures must respect this assumption. + */ #define RX_HTT_HDR_STATUS_LEN 64 -/* This structure layout is programmed via rx ring setup +/* The rx descriptor structure layout is programmed via rx ring setup * so that FW knows how to transfer the rx descriptor to the host. - * Buffers like this are placed on the rx ring. + * Unfortunately, though, QCA6174's firmware doesn't currently behave correctly + * when modifying the structure layout of the rx descriptor beyond what it expects + * (even if it correctly programmed during the rx ring setup). + * Therefore we must keep two different memory layouts, abstract the rx descriptor + * representation and use ath10k_rx_desc_ops + * for correctly accessing rx descriptor data. */ + +/* base struct used for abstracting the rx descritor representation */ struct htt_rx_desc { union { /* This field is filled on the host using the msdu buffer @@ -2089,6 +2093,13 @@ struct htt_rx_desc { struct fw_rx_desc_base fw_desc; u32 pad; } __packed; +} __packed; + +/* rx descriptor for wcn3990 and possibly extensible for newer cards + * Buffers like this are placed on the rx ring. + */ +struct htt_rx_desc_v2 { + struct htt_rx_desc base; struct { struct rx_attention attention; struct rx_frag_info frag_info; @@ -2103,6 +2114,240 @@ struct htt_rx_desc { u8 msdu_payload[]; }; +/* QCA6174, QCA988x, QCA99x0 dedicated rx descriptor to make sure their firmware + * works correctly. We keep a single rx descriptor for all these three + * families of cards because from tests it seems to be the most stable solution, + * e.g. having a rx descriptor only for QCA6174 seldom caused firmware crashes + * during some tests. + * Buffers like this are placed on the rx ring. + */ +struct htt_rx_desc_v1 { + struct htt_rx_desc base; + struct { + struct rx_attention attention; + struct rx_frag_info_v1 frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start_v1 msdu_start; + struct rx_msdu_end_v1 msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end_v1 ppdu_end; + } __packed; + u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; + u8 msdu_payload[]; +}; + +/* rx_desc abstraction */ +struct ath10k_htt_rx_desc_ops { + /* These fields are mandatory, they must be specified in any instance */ + + /* sizeof() of the rx_desc structure used by this hw */ + size_t rx_desc_size; + + /* offset of msdu_payload inside the rx_desc structure used by this hw */ + size_t rx_desc_msdu_payload_offset; + + /* These fields are options. + * When a field is not provided the default implementation gets used + * (see the ath10k_rx_desc_* operations below for more info about the defaults) + */ + bool (*rx_desc_get_msdu_limit_error)(struct htt_rx_desc *rxd); + int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); + + /* Safely cast from a void* buffer containing an rx descriptor + * to the proper rx_desc structure + */ + struct htt_rx_desc *(*rx_desc_from_raw_buffer)(void *buff); + + void (*rx_desc_get_offsets)(struct htt_rx_ring_rx_desc_offsets *offs); + struct rx_attention *(*rx_desc_get_attention)(struct htt_rx_desc *rxd); + struct rx_frag_info_common *(*rx_desc_get_frag_info)(struct htt_rx_desc *rxd); + struct rx_mpdu_start *(*rx_desc_get_mpdu_start)(struct htt_rx_desc *rxd); + struct rx_mpdu_end *(*rx_desc_get_mpdu_end)(struct htt_rx_desc *rxd); + struct rx_msdu_start_common *(*rx_desc_get_msdu_start)(struct htt_rx_desc *rxd); + struct rx_msdu_end_common *(*rx_desc_get_msdu_end)(struct htt_rx_desc *rxd); + struct rx_ppdu_start *(*rx_desc_get_ppdu_start)(struct htt_rx_desc *rxd); + struct rx_ppdu_end_common *(*rx_desc_get_ppdu_end)(struct htt_rx_desc *rxd); + u8 *(*rx_desc_get_rx_hdr_status)(struct htt_rx_desc *rxd); + u8 *(*rx_desc_get_msdu_payload)(struct htt_rx_desc *rxd); +}; + +extern const struct ath10k_htt_rx_desc_ops qca988x_rx_desc_ops; +extern const struct ath10k_htt_rx_desc_ops qca99x0_rx_desc_ops; +extern const struct ath10k_htt_rx_desc_ops wcn3990_rx_desc_ops; + +static inline int +ath10k_htt_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + if (hw->rx_desc_ops->rx_desc_get_l3_pad_bytes) + return hw->rx_desc_ops->rx_desc_get_l3_pad_bytes(rxd); + return 0; +} + +static inline bool +ath10k_htt_rx_desc_msdu_limit_error(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + if (hw->rx_desc_ops->rx_desc_get_msdu_limit_error) + return hw->rx_desc_ops->rx_desc_get_msdu_limit_error(rxd); + return false; +} + +/* The default implementation of all these getters is using the old rx_desc, + * so that it is easier to define the ath10k_htt_rx_desc_ops instances. + * But probably, if new wireless cards must be supported, it would be better + * to switch the default implementation to the new rx_desc, since this would + * make the extension easier . + */ +static inline struct htt_rx_desc * +ath10k_htt_rx_desc_from_raw_buffer(struct ath10k_hw_params *hw, void *buff) +{ + if (hw->rx_desc_ops->rx_desc_from_raw_buffer) + return hw->rx_desc_ops->rx_desc_from_raw_buffer(buff); + return &((struct htt_rx_desc_v1 *)buff)->base; +} + +static inline void +ath10k_htt_rx_desc_get_offsets(struct ath10k_hw_params *hw, + struct htt_rx_ring_rx_desc_offsets *off) +{ + if (hw->rx_desc_ops->rx_desc_get_offsets) { + hw->rx_desc_ops->rx_desc_get_offsets(off); + } else { +#define desc_offset(x) (offsetof(struct htt_rx_desc_v1, x) / 4) + off->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + off->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + off->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + off->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + off->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + off->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + off->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + off->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + off->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + off->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); +#undef desc_offset + } +} + +static inline struct rx_attention * +ath10k_htt_rx_desc_get_attention(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_attention) + return hw->rx_desc_ops->rx_desc_get_attention(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->attention; +} + +static inline struct rx_frag_info_common * +ath10k_htt_rx_desc_get_frag_info(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_frag_info) + return hw->rx_desc_ops->rx_desc_get_frag_info(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->frag_info.common; +} + +static inline struct rx_mpdu_start * +ath10k_htt_rx_desc_get_mpdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_mpdu_start) + return hw->rx_desc_ops->rx_desc_get_mpdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->mpdu_start; +} + +static inline struct rx_mpdu_end * +ath10k_htt_rx_desc_get_mpdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_mpdu_end) + return hw->rx_desc_ops->rx_desc_get_mpdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->mpdu_end; +} + +static inline struct rx_msdu_start_common * +ath10k_htt_rx_desc_get_msdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_start) + return hw->rx_desc_ops->rx_desc_get_msdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->msdu_start.common; +} + +static inline struct rx_msdu_end_common * +ath10k_htt_rx_desc_get_msdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_end) + return hw->rx_desc_ops->rx_desc_get_msdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->msdu_end.common; +} + +static inline struct rx_ppdu_start * +ath10k_htt_rx_desc_get_ppdu_start(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_ppdu_start) + return hw->rx_desc_ops->rx_desc_get_ppdu_start(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->ppdu_start; +} + +static inline struct rx_ppdu_end_common * +ath10k_htt_rx_desc_get_ppdu_end(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_ppdu_end) + return hw->rx_desc_ops->rx_desc_get_ppdu_end(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return &rx_desc->ppdu_end.common; +} + +static inline u8 * +ath10k_htt_rx_desc_get_rx_hdr_status(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_rx_hdr_status) + return hw->rx_desc_ops->rx_desc_get_rx_hdr_status(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return rx_desc->rx_hdr_status; +} + +static inline u8 * +ath10k_htt_rx_desc_get_msdu_payload(struct ath10k_hw_params *hw, struct htt_rx_desc *rxd) +{ + struct htt_rx_desc_v1 *rx_desc; + + if (hw->rx_desc_ops->rx_desc_get_msdu_payload) + return hw->rx_desc_ops->rx_desc_get_msdu_payload(rxd); + + rx_desc = container_of(rxd, struct htt_rx_desc_v1, base); + return rx_desc->msdu_payload; +} + #define HTT_RX_DESC_HL_INFO_SEQ_NUM_MASK 0x00000fff #define HTT_RX_DESC_HL_INFO_SEQ_NUM_LSB 0 #define HTT_RX_DESC_HL_INFO_ENCRYPTED_MASK 0x00001000 @@ -2136,7 +2381,14 @@ struct htt_rx_chan_info { * rounded up to a cache line size. */ #define HTT_RX_BUF_SIZE 2048 -#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) + +/* The HTT_RX_MSDU_SIZE can't be statically computed anymore, + * because it depends on the underlying device rx_desc representation + */ +static inline int ath10k_htt_rx_msdu_size(struct ath10k_hw_params *hw) +{ + return HTT_RX_BUF_SIZE - (int)hw->rx_desc_ops->rx_desc_size; +} /* Refill a bunch of RX buffers for each refill round so that FW/HW can handle * aggregated traffic more nicely. diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index adbaeb67eedf1..9ad64ca84beb7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -21,7 +21,10 @@ #define HTT_RX_RING_REFILL_RESCHED_MS 5 -static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb); +/* shortcut to interpret a raw memory buffer as a rx descriptor */ +#define HTT_RX_BUF_TO_RX_DESC(hw, buf) ath10k_htt_rx_desc_from_raw_buffer(hw, buf) + +static int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb); static struct sk_buff * ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u64 paddr) @@ -128,6 +131,7 @@ static void *ath10k_htt_get_vaddr_ring_64(struct ath10k_htt *htt) static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) { + struct ath10k_hw_params *hw = &htt->ar->hw_params; struct htt_rx_desc *rx_desc; struct ath10k_skb_rxcb *rxcb; struct sk_buff *skb; @@ -163,8 +167,8 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) skb->data); /* Clear rx_desc attention word before posting to Rx ring */ - rx_desc = (struct htt_rx_desc *)skb->data; - rx_desc->attention.flags = __cpu_to_le32(0); + rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, skb->data); + ath10k_htt_rx_desc_get_attention(hw, rx_desc)->flags = __cpu_to_le32(0); paddr = dma_map_single(htt->ar->dev, skb->data, skb->len + skb_tailroom(skb), @@ -343,9 +347,14 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, struct sk_buff_head *amsdu) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; + struct rx_attention *rx_desc_attention; + struct rx_frag_info_common *rx_desc_frag_info_common; + struct rx_msdu_start_common *rx_desc_msdu_start_common; + struct rx_msdu_end_common *rx_desc_msdu_end_common; lockdep_assert_held(&htt->rx_ring.lock); @@ -360,13 +369,18 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, __skb_queue_tail(amsdu, msdu); - rx_desc = (struct htt_rx_desc *)msdu->data; + rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rx_desc_attention = ath10k_htt_rx_desc_get_attention(hw, rx_desc); + rx_desc_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, + rx_desc); + rx_desc_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rx_desc); + rx_desc_frag_info_common = ath10k_htt_rx_desc_get_frag_info(hw, rx_desc); /* FIXME: we must report msdu payload since this is what caller * expects now */ - skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload)); - skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset); /* * Sanity check - confirm the HW is finished filling in the @@ -376,24 +390,24 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, * To prevent the case that we handle a stale Rx descriptor, * just assert for now until we have a way to recover. */ - if (!(__le32_to_cpu(rx_desc->attention.flags) + if (!(__le32_to_cpu(rx_desc_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { __skb_queue_purge(amsdu); return -EIO; } - msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags) + msdu_len_invalid = !!(__le32_to_cpu(rx_desc_attention->flags) & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR | RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR)); - msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.common.info0), + msdu_len = MS(__le32_to_cpu(rx_desc_msdu_start_common->info0), RX_MSDU_START_INFO0_MSDU_LENGTH); - msdu_chained = rx_desc->frag_info.ring2_more_count; + msdu_chained = rx_desc_frag_info_common->ring2_more_count; if (msdu_len_invalid) msdu_len = 0; skb_trim(msdu, 0); - skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, min(msdu_len, ath10k_htt_rx_msdu_size(hw))); msdu_len -= msdu->len; /* Note: Chained buffers do not contain rx descriptor */ @@ -411,11 +425,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_chaining = 1; } - last_msdu = __le32_to_cpu(rx_desc->msdu_end.common.info0) & + last_msdu = __le32_to_cpu(rx_desc_msdu_end_common->info0) & RX_MSDU_END_INFO0_LAST_MSDU; - trace_ath10k_htt_rx_desc(ar, &rx_desc->attention, - sizeof(*rx_desc) - sizeof(u32)); + /* FIXME: why are we skipping the first part of the rx_desc? */ + trace_ath10k_htt_rx_desc(ar, rx_desc + sizeof(u32), + hw->rx_desc_ops->rx_desc_size - sizeof(u32)); if (last_msdu) break; @@ -480,6 +495,7 @@ static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt, struct htt_rx_in_ord_msdu_desc **msdu_desc) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; u32 paddr; struct sk_buff *frag_buf; struct sk_buff *prev_frag_buf; @@ -488,12 +504,12 @@ static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt, struct htt_rx_desc *rxd; int amsdu_len = __le16_to_cpu(ind_desc->msdu_len); - rxd = (void *)msdu->data; - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(struct htt_rx_desc)); - skb_pull(msdu, sizeof(struct htt_rx_desc)); - skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); + skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw))); amsdu_len -= msdu->len; last_frag = ind_desc->reserved; @@ -556,6 +572,7 @@ ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt, struct htt_rx_in_ord_msdu_desc_ext **msdu_desc) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; u64 paddr; struct sk_buff *frag_buf; struct sk_buff *prev_frag_buf; @@ -564,12 +581,12 @@ ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt, struct htt_rx_desc *rxd; int amsdu_len = __le16_to_cpu(ind_desc->msdu_len); - rxd = (void *)msdu->data; - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(struct htt_rx_desc)); - skb_pull(msdu, sizeof(struct htt_rx_desc)); - skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); + skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw))); amsdu_len -= msdu->len; last_frag = ind_desc->reserved; @@ -631,8 +648,10 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, struct sk_buff_head *list) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs32; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; struct sk_buff *msdu; int msdu_count, ret; bool is_offload; @@ -667,15 +686,16 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, __skb_queue_tail(list, msdu); if (!is_offload) { - rxd = (void *)msdu->data; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(*rxd)); - skb_pull(msdu, sizeof(*rxd)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len)); - if (!(__le32_to_cpu(rxd->attention.flags) & + if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); return -EIO; @@ -693,8 +713,10 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, struct sk_buff_head *list) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_in_ord_msdu_desc_ext *msdu_desc = ev->msdu_descs64; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; struct sk_buff *msdu; int msdu_count, ret; bool is_offload; @@ -728,15 +750,16 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, __skb_queue_tail(list, msdu); if (!is_offload) { - rxd = (void *)msdu->data; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); - trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size); - skb_put(msdu, sizeof(*rxd)); - skb_pull(msdu, sizeof(*rxd)); + skb_put(msdu, hw->rx_desc_ops->rx_desc_size); + skb_pull(msdu, hw->rx_desc_ops->rx_desc_size); skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len)); - if (!(__le32_to_cpu(rxd->attention.flags) & + if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); return -EIO; @@ -944,16 +967,32 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_attention *rxd_attention; + struct rx_mpdu_start *rxd_mpdu_start; + struct rx_mpdu_end *rxd_mpdu_end; + struct rx_msdu_start_common *rxd_msdu_start_common; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_ppdu_start *rxd_ppdu_start; struct ieee80211_supported_band *sband; u8 cck, rate, bw, sgi, mcs, nss; + u8 *rxd_msdu_payload; u8 preamble = 0; u8 group_id; u32 info1, info2, info3; u32 stbc, nsts_su; - info1 = __le32_to_cpu(rxd->ppdu_start.info1); - info2 = __le32_to_cpu(rxd->ppdu_start.info2); - info3 = __le32_to_cpu(rxd->ppdu_start.info3); + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + rxd_mpdu_end = ath10k_htt_rx_desc_get_mpdu_end(hw, rxd); + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd); + rxd_msdu_payload = ath10k_htt_rx_desc_get_msdu_payload(hw, rxd); + + info1 = __le32_to_cpu(rxd_ppdu_start->info1); + info2 = __le32_to_cpu(rxd_ppdu_start->info2); + info3 = __le32_to_cpu(rxd_ppdu_start->info3); preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE); @@ -1022,24 +1061,24 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, if (mcs > 0x09) { ath10k_warn(ar, "invalid MCS received %u\n", mcs); ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n", - __le32_to_cpu(rxd->attention.flags), - __le32_to_cpu(rxd->mpdu_start.info0), - __le32_to_cpu(rxd->mpdu_start.info1), - __le32_to_cpu(rxd->msdu_start.common.info0), - __le32_to_cpu(rxd->msdu_start.common.info1), - rxd->ppdu_start.info0, - __le32_to_cpu(rxd->ppdu_start.info1), - __le32_to_cpu(rxd->ppdu_start.info2), - __le32_to_cpu(rxd->ppdu_start.info3), - __le32_to_cpu(rxd->ppdu_start.info4)); + __le32_to_cpu(rxd_attention->flags), + __le32_to_cpu(rxd_mpdu_start->info0), + __le32_to_cpu(rxd_mpdu_start->info1), + __le32_to_cpu(rxd_msdu_start_common->info0), + __le32_to_cpu(rxd_msdu_start_common->info1), + rxd_ppdu_start->info0, + __le32_to_cpu(rxd_ppdu_start->info1), + __le32_to_cpu(rxd_ppdu_start->info2), + __le32_to_cpu(rxd_ppdu_start->info3), + __le32_to_cpu(rxd_ppdu_start->info4)); ath10k_warn(ar, "msdu end %08x mpdu end %08x\n", - __le32_to_cpu(rxd->msdu_end.common.info0), - __le32_to_cpu(rxd->mpdu_end.info0)); + __le32_to_cpu(rxd_msdu_end_common->info0), + __le32_to_cpu(rxd_mpdu_end->info0)); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx desc msdu payload: ", - rxd->msdu_payload, 50); + rxd_msdu_payload, 50); } status->rate_idx = mcs; @@ -1059,6 +1098,10 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, static struct ieee80211_channel * ath10k_htt_rx_h_peer_channel(struct ath10k *ar, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_attention *rxd_attention; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_mpdu_start *rxd_mpdu_start; struct ath10k_peer *peer; struct ath10k_vif *arvif; struct cfg80211_chan_def def; @@ -1069,15 +1112,19 @@ ath10k_htt_rx_h_peer_channel(struct ath10k *ar, struct htt_rx_desc *rxd) if (!rxd) return NULL; - if (rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + + if (rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_PEER_IDX_INVALID)) return NULL; - if (!(rxd->msdu_end.common.info0 & + if (!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU))) return NULL; - peer_id = MS(__le32_to_cpu(rxd->mpdu_start.info0), + peer_id = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_PEER_IDX); peer = ath10k_peer_find_by_id(ar, peer_id); @@ -1167,14 +1214,16 @@ static void ath10k_htt_rx_h_signal(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_ppdu_start *rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd); int i; for (i = 0; i < IEEE80211_MAX_CHAINS ; i++) { status->chains &= ~BIT(i); - if (rxd->ppdu_start.rssi_chains[i].pri20_mhz != 0x80) { + if (rxd_ppdu_start->rssi_chains[i].pri20_mhz != 0x80) { status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR + - rxd->ppdu_start.rssi_chains[i].pri20_mhz; + rxd_ppdu_start->rssi_chains[i].pri20_mhz; status->chains |= BIT(i); } @@ -1182,7 +1231,7 @@ static void ath10k_htt_rx_h_signal(struct ath10k *ar, /* FIXME: Get real NF */ status->signal = ATH10K_DEFAULT_NOISE_FLOOR + - rxd->ppdu_start.rssi_comb; + rxd_ppdu_start->rssi_comb; status->flag &= ~RX_FLAG_NO_SIGNAL_VAL; } @@ -1190,13 +1239,18 @@ static void ath10k_htt_rx_h_mactime(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) { + struct ath10k_hw_params *hw = &ar->hw_params; + struct rx_ppdu_end_common *rxd_ppdu_end_common; + + rxd_ppdu_end_common = ath10k_htt_rx_desc_get_ppdu_end(hw, rxd); + /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This * means all prior MSDUs in a PPDU are reported to mac80211 without the * TSF. Is it worth holding frames until end of PPDU is known? * * FIXME: Can we get/compute 64bit TSF? */ - status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp); + status->mactime = __le32_to_cpu(rxd_ppdu_end_common->tsf_timestamp); status->flag |= RX_FLAG_MACTIME_END; } @@ -1206,7 +1260,9 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, u32 vdev_id) { struct sk_buff *first; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; bool is_first_ppdu; bool is_last_ppdu; @@ -1214,11 +1270,14 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, return; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); - is_first_ppdu = !!(rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + + is_first_ppdu = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU)); - is_last_ppdu = !!(rxd->attention.flags & + is_last_ppdu = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU)); if (is_first_ppdu) { @@ -1357,7 +1416,9 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, const u8 first_hdr[64]) { struct ieee80211_hdr *hdr; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; size_t hdr_len; size_t crypto_len; bool is_first; @@ -1366,10 +1427,13 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, int bytes_aligned = ar->hw_params.decap_align_bytes; u8 *qos; - rxd = (void *)msdu->data - sizeof(*rxd); - is_first = !!(rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); /* Delivered decapped frame: @@ -1387,7 +1451,7 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, * error packets. If limit exceeds, hw sends all remaining MSDUs as * a single last MSDU with this msdu limit error set. */ - msdu_limit_err = ath10k_rx_desc_msdu_limit_error(&ar->hw_params, rxd); + msdu_limit_err = ath10k_htt_rx_desc_msdu_limit_error(hw, rxd); /* If MSDU limit error happens, then don't warn on, the partial raw MSDU * without first MSDU is expected in that case, and handled later here. @@ -1479,6 +1543,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; struct htt_rx_desc *rxd; size_t hdr_len; @@ -1499,9 +1564,10 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, */ /* pull decapped header and copy SA & DA */ - rxd = (void *)msdu->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, (void *)msdu->data - + hw->rx_desc_ops->rx_desc_size); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); hdr = (struct ieee80211_hdr *)(msdu->data + l3_pad_bytes); @@ -1537,18 +1603,25 @@ static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar, enum htt_rx_mpdu_encrypt_type enctype) { struct ieee80211_hdr *hdr; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; + u8 *rxd_rx_hdr_status; size_t hdr_len, crypto_len; void *rfc1042; bool is_first, is_last, is_amsdu; int bytes_aligned = ar->hw_params.decap_align_bytes; - rxd = (void *)msdu->data - sizeof(*rxd); - hdr = (void *)rxd->rx_hdr_status; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_rx_hdr_status = ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); + hdr = (void *)rxd_rx_hdr_status; - is_first = !!(rxd->msdu_end.common.info0 & + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); is_amsdu = !(is_first && is_last); @@ -1574,6 +1647,7 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; struct ethhdr *eth; size_t hdr_len; @@ -1593,8 +1667,10 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar, if (WARN_ON_ONCE(!rfc1042)) return; - rxd = (void *)msdu->data - sizeof(*rxd); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); skb_pull(msdu, l3_pad_bytes); @@ -1635,6 +1711,7 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar, const u8 first_hdr[64], enum htt_rx_mpdu_encrypt_type enctype) { + struct ath10k_hw_params *hw = &ar->hw_params; struct ieee80211_hdr *hdr; size_t hdr_len; int l3_pad_bytes; @@ -1647,8 +1724,10 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar, * [payload] */ - rxd = (void *)msdu->data - sizeof(*rxd); - l3_pad_bytes = ath10k_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd); skb_put(msdu, l3_pad_bytes); skb_pull(msdu, sizeof(struct amsdu_subframe_hdr) + l3_pad_bytes); @@ -1673,7 +1752,9 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, enum htt_rx_mpdu_encrypt_type enctype, bool is_decrypted) { + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_start_common *rxd_msdu_start_common; enum rx_msdu_decap_format decap; /* First msdu's decapped header: @@ -1687,8 +1768,11 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, * [rfc1042/llc] */ - rxd = (void *)msdu->data - sizeof(*rxd); - decap = MS(__le32_to_cpu(rxd->msdu_start.common.info1), + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1), RX_MSDU_START_INFO1_DECAP_FORMAT); switch (decap) { @@ -1710,17 +1794,23 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar, } } -static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) +static int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb) { struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; + struct rx_msdu_start_common *rxd_msdu_start_common; u32 flags, info; bool is_ip4, is_ip6; bool is_tcp, is_udp; bool ip_csum_ok, tcpudp_csum_ok; - rxd = (void *)skb->data - sizeof(*rxd); - flags = __le32_to_cpu(rxd->attention.flags); - info = __le32_to_cpu(rxd->msdu_start.common.info1); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)skb->data - hw->rx_desc_ops->rx_desc_size); + + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + flags = __le32_to_cpu(rxd_attention->flags); + info = __le32_to_cpu(rxd_msdu_start_common->info1); is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO); is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO); @@ -1741,9 +1831,10 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) return CHECKSUM_UNNECESSARY; } -static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu) +static void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw, + struct sk_buff *msdu) { - msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu); + msdu->ip_summed = ath10k_htt_rx_get_csum_state(hw, msdu); } static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, @@ -1835,7 +1926,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, struct sk_buff *first; struct sk_buff *last; struct sk_buff *msdu, *temp; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_attention *rxd_attention; + struct rx_mpdu_start *rxd_mpdu_start; + struct ieee80211_hdr *hdr; enum htt_rx_mpdu_encrypt_type enctype; u8 first_hdr[64]; @@ -1853,18 +1948,22 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, return; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); - is_mgmt = !!(rxd->attention.flags & + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + + is_mgmt = !!(rxd_attention->flags & __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE)); - enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11 * decapped header. It'll be used for undecapping of each MSDU. */ - hdr = (void *)rxd->rx_hdr_status; + hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN); if (rx_hdr) @@ -1882,8 +1981,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, /* Some attention flags are valid only in the last MSDU. */ last = skb_peek_tail(amsdu); - rxd = (void *)last->data - sizeof(*rxd); - attention = __le32_to_cpu(rxd->attention.flags); + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)last->data - hw->rx_desc_ops->rx_desc_size); + + rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd); + attention = __le32_to_cpu(rxd_attention->flags); has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR); has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR); @@ -1971,7 +2073,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, continue; } - ath10k_htt_rx_h_csum_offload(msdu); + ath10k_htt_rx_h_csum_offload(&ar->hw_params, msdu); if (frag && !fill_crypt_header && enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) @@ -2083,12 +2185,19 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, unsigned long *unchain_cnt) { struct sk_buff *first; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_start_common *rxd_msdu_start_common; + struct rx_frag_info_common *rxd_frag_info; enum rx_msdu_decap_format decap; first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); - decap = MS(__le32_to_cpu(rxd->msdu_start.common.info1), + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd); + rxd_frag_info = ath10k_htt_rx_desc_get_frag_info(hw, rxd); + decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1), RX_MSDU_START_INFO1_DECAP_FORMAT); /* FIXME: Current unchaining logic can only handle simple case of raw @@ -2097,7 +2206,7 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, * try re-constructing such frames - it'll be pretty much garbage. */ if (decap != RX_MSDU_DECAP_RAW || - skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) { + skb_queue_len(amsdu) != 1 + rxd_frag_info->ring2_more_count) { *drop_cnt += skb_queue_len(amsdu); __skb_queue_purge(amsdu); return; @@ -2112,7 +2221,10 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, u8 *subframe_hdr; struct sk_buff *first; bool is_first, is_last; + struct ath10k_hw_params *hw = &ar->hw_params; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; + struct rx_mpdu_start *rxd_mpdu_start; struct ieee80211_hdr *hdr; size_t hdr_len, crypto_len; enum htt_rx_mpdu_encrypt_type enctype; @@ -2120,12 +2232,16 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, first = skb_peek(amsdu); - rxd = (void *)first->data - sizeof(*rxd); - hdr = (void *)rxd->rx_hdr_status; + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)first->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd); + hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd); - is_first = !!(rxd->msdu_end.common.info0 & + is_first = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)); - is_last = !!(rxd->msdu_end.common.info0 & + is_last = !!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)); /* Return in case of non-aggregated msdu */ @@ -2136,7 +2252,7 @@ static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar, if (!is_first) return false; - enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); hdr_len = ieee80211_hdrlen(hdr->frame_control); @@ -3028,11 +3144,13 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) spin_unlock_bh(&ar->data_lock); } -static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, +static int ath10k_htt_rx_extract_amsdu(struct ath10k_hw_params *hw, + struct sk_buff_head *list, struct sk_buff_head *amsdu) { struct sk_buff *msdu; struct htt_rx_desc *rxd; + struct rx_msdu_end_common *rxd_msdu_end_common; if (skb_queue_empty(list)) return -ENOBUFS; @@ -3043,15 +3161,22 @@ static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, while ((msdu = __skb_dequeue(list))) { __skb_queue_tail(amsdu, msdu); - rxd = (void *)msdu->data - sizeof(*rxd); - if (rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - + hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + if (rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)) break; } msdu = skb_peek_tail(amsdu); - rxd = (void *)msdu->data - sizeof(*rxd); - if (!(rxd->msdu_end.common.info0 & + rxd = HTT_RX_BUF_TO_RX_DESC(hw, + (void *)msdu->data - hw->rx_desc_ops->rx_desc_size); + + rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd); + if (!(rxd_msdu_end_common->info0 & __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) { skb_queue_splice_init(amsdu, list); return -EAGAIN; @@ -3194,7 +3319,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) while (!skb_queue_empty(&list)) { __skb_queue_head_init(&amsdu); - ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu); + ret = ath10k_htt_rx_extract_amsdu(&ar->hw_params, &list, &amsdu); switch (ret) { case 0: /* Note: The in-order indication may report interleaved diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index b793eac2cfac8..9842a4b2f78f7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -796,47 +796,26 @@ static int ath10k_htt_send_frag_desc_bank_cfg_64(struct ath10k_htt *htt) return 0; } -static void ath10k_htt_fill_rx_desc_offset_32(void *rx_ring) +static void ath10k_htt_fill_rx_desc_offset_32(struct ath10k_hw_params *hw, void *rx_ring) { struct htt_rx_ring_setup_ring32 *ring = (struct htt_rx_ring_setup_ring32 *)rx_ring; -#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) - ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); - ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); - ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); - ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); - ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); - ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); - ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); - ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); - ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); - ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); -#undef desc_offset + ath10k_htt_rx_desc_get_offsets(hw, &ring->offsets); } -static void ath10k_htt_fill_rx_desc_offset_64(void *rx_ring) +static void ath10k_htt_fill_rx_desc_offset_64(struct ath10k_hw_params *hw, void *rx_ring) { struct htt_rx_ring_setup_ring64 *ring = (struct htt_rx_ring_setup_ring64 *)rx_ring; -#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) - ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); - ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); - ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); - ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); - ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); - ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); - ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); - ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); - ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); - ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); -#undef desc_offset + ath10k_htt_rx_desc_get_offsets(hw, &ring->offsets); } static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct sk_buff *skb; struct htt_cmd *cmd; struct htt_rx_ring_setup_ring32 *ring; @@ -896,7 +875,7 @@ static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) ring->flags = __cpu_to_le16(flags); ring->fw_idx_init_val = __cpu_to_le16(fw_idx); - ath10k_htt_fill_rx_desc_offset_32(ring); + ath10k_htt_fill_rx_desc_offset_32(hw, ring); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); @@ -909,6 +888,7 @@ static int ath10k_htt_send_rx_ring_cfg_32(struct ath10k_htt *htt) static int ath10k_htt_send_rx_ring_cfg_64(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; + struct ath10k_hw_params *hw = &ar->hw_params; struct sk_buff *skb; struct htt_cmd *cmd; struct htt_rx_ring_setup_ring64 *ring; @@ -965,7 +945,7 @@ static int ath10k_htt_send_rx_ring_cfg_64(struct ath10k_htt *htt) ring->flags = __cpu_to_le16(flags); ring->fw_idx_init_val = __cpu_to_le16(fw_idx); - ath10k_htt_fill_rx_desc_offset_64(ring); + ath10k_htt_fill_rx_desc_offset_64(hw, ring); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 57c58af64a575..e52e41a703210 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -11,6 +11,7 @@ #include "hif.h" #include "wmi-ops.h" #include "bmi.h" +#include "rx_desc.h" const struct ath10k_hw_regs qca988x_regs = { .rtc_soc_base_address = 0x00004000, @@ -1134,21 +1135,7 @@ const struct ath10k_hw_ops qca988x_ops = { .is_rssi_enable = ath10k_htt_tx_rssi_enable, }; -static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) -{ - return MS(__le32_to_cpu(rxd->msdu_end.qca99x0.info1), - RX_MSDU_END_INFO1_L3_HDR_PAD); -} - -static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd) -{ - return !!(rxd->msdu_end.common.info0 & - __cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR)); -} - const struct ath10k_hw_ops qca99x0_ops = { - .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, - .rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error, .is_rssi_enable = ath10k_htt_tx_rssi_enable, }; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 591ef7416b613..5215a6816d718 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -510,6 +510,8 @@ struct ath10k_hw_clk_params { u32 outdiv; }; +struct htt_rx_desc_ops; + struct ath10k_hw_params { u32 id; u16 dev_id; @@ -562,6 +564,9 @@ struct ath10k_hw_params { */ bool sw_decrypt_mcast_mgmt; + /* Rx descriptor abstraction */ + const struct ath10k_htt_rx_desc_ops *rx_desc_ops; + const struct ath10k_hw_ops *hw_ops; /* Number of bytes used for alignment in rx_hdr_status of rx desc. */ @@ -630,16 +635,14 @@ struct ath10k_hw_params { bool dynamic_sar_support; }; -struct htt_rx_desc; struct htt_resp; struct htt_data_tx_completion_ext; +struct htt_rx_ring_rx_desc_offsets; /* Defines needed for Rx descriptor abstraction */ struct ath10k_hw_ops { - int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); void (*set_coverage_class)(struct ath10k *ar, s16 value); int (*enable_pll_clk)(struct ath10k *ar); - bool (*rx_desc_get_msdu_limit_error)(struct htt_rx_desc *rxd); int (*tx_data_rssi_pad_bytes)(struct htt_resp *htt); int (*is_rssi_enable)(struct htt_resp *resp); }; @@ -652,24 +655,6 @@ extern const struct ath10k_hw_ops wcn3990_ops; extern const struct ath10k_hw_clk_params qca6174_clk[]; -static inline int -ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, - struct htt_rx_desc *rxd) -{ - if (hw->hw_ops->rx_desc_get_l3_pad_bytes) - return hw->hw_ops->rx_desc_get_l3_pad_bytes(rxd); - return 0; -} - -static inline bool -ath10k_rx_desc_msdu_limit_error(struct ath10k_hw_params *hw, - struct htt_rx_desc *rxd) -{ - if (hw->hw_ops->rx_desc_get_msdu_limit_error) - return hw->hw_ops->rx_desc_get_msdu_limit_error(rxd); - return false; -} - static inline int ath10k_tx_data_rssi_get_pad_bytes(struct ath10k_hw_params *hw, struct htt_resp *htt) diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index 705b6295e4663..6ce2a8b1060d9 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -196,17 +196,31 @@ struct rx_attention { * descriptor. */ -struct rx_frag_info { +struct rx_frag_info_common { u8 ring0_more_count; u8 ring1_more_count; u8 ring2_more_count; u8 ring3_more_count; +} __packed; + +struct rx_frag_info_wcn3990 { u8 ring4_more_count; u8 ring5_more_count; u8 ring6_more_count; u8 ring7_more_count; } __packed; +struct rx_frag_info { + struct rx_frag_info_common common; + union { + struct rx_frag_info_wcn3990 wcn3990; + } __packed; +} __packed; + +struct rx_frag_info_v1 { + struct rx_frag_info_common common; +} __packed; + /* * ring0_more_count * Indicates the number of more buffers associated with RX DMA @@ -474,11 +488,17 @@ struct rx_msdu_start_wcn3990 { struct rx_msdu_start { struct rx_msdu_start_common common; union { - struct rx_msdu_start_qca99x0 qca99x0; struct rx_msdu_start_wcn3990 wcn3990; } __packed; } __packed; +struct rx_msdu_start_v1 { + struct rx_msdu_start_common common; + union { + struct rx_msdu_start_qca99x0 qca99x0; + } __packed; +} __packed; + /* * msdu_length * MSDU length in bytes after decapsulation. This field is @@ -612,11 +632,17 @@ struct rx_msdu_end_wcn3990 { struct rx_msdu_end { struct rx_msdu_end_common common; union { - struct rx_msdu_end_qca99x0 qca99x0; struct rx_msdu_end_wcn3990 wcn3990; } __packed; } __packed; +struct rx_msdu_end_v1 { + struct rx_msdu_end_common common; + union { + struct rx_msdu_end_qca99x0 qca99x0; + } __packed; +} __packed; + /* *ip_hdr_chksum * This can include the IP header checksum or the pseudo header @@ -1134,13 +1160,19 @@ struct rx_ppdu_end_wcn3990 { } __packed; struct rx_ppdu_end { + struct rx_ppdu_end_common common; + union { + struct rx_ppdu_end_wcn3990 wcn3990; + } __packed; +} __packed; + +struct rx_ppdu_end_v1 { struct rx_ppdu_end_common common; union { struct rx_ppdu_end_qca988x qca988x; struct rx_ppdu_end_qca6174 qca6174; struct rx_ppdu_end_qca99x0 qca99x0; struct rx_ppdu_end_qca9984 qca9984; - struct rx_ppdu_end_wcn3990 wcn3990; } __packed; } __packed; From 039d5d4db4bccbc299ce3daa2d30e933c4a2719b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sat, 15 Jan 2022 00:16:43 +0000 Subject: [PATCH 22/28] wcn36xx: Implement get_snr() The wcn36xx BD phy descriptor returns both Received Signal Strength Information (RSSI) and Signal To Noise Ratio (SNR) with each delivered BD. The macro to extract this data is a simple-one liner, easily imported from prima driver. This data will be useful to us when implementing mac80211-ops->get_survey(). Signed-off-by: Bryan O'Donoghue Reviewed-by: Loic Poulain Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220115001646.3981501-2-bryan.odonoghue@linaro.org --- drivers/net/wireless/ath/wcn36xx/txrx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index dd58dde8c8363..425d388b4a4d0 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -23,6 +23,11 @@ static inline int get_rssi0(struct wcn36xx_rx_bd *bd) return 100 - ((bd->phy_stat0 >> 24) & 0xff); } +static inline int get_snr(struct wcn36xx_rx_bd *bd) +{ + return ((bd->phy_stat1 >> 24) & 0xff); +} + struct wcn36xx_rate { u16 bitrate; u16 mcs_or_legacy_index; From d6f2746691cb10e484f15ab9e44e2cc2ccf9bd86 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sat, 15 Jan 2022 00:16:44 +0000 Subject: [PATCH 23/28] wcn36xx: Track the band and channel we are tuned to Track the band and channel we are currently tuned to by way of pointers to the standard structures that describe them both embedded within the driver. Tracking of the pair makes it much easier when implementing ieee80211_ops->get_survey to return quickly captured metrics for the currently tuned channel. Signed-off-by: Bryan O'Donoghue Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220115001646.3981501-3-bryan.odonoghue@linaro.org --- drivers/net/wireless/ath/wcn36xx/main.c | 27 ++++++++++++++++++++++ drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index bd334a3020576..bb674b7258733 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -392,11 +392,38 @@ static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) { struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *tmp; + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + int i, j; + + for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) { + band = wcn->hw->wiphy->bands[i]; + if (!band) + break; + for (j = 0; j < band->n_channels; j++) { + if (HW_VALUE_CHANNEL(band->channels[j].hw_value) == ch) { + channel = &band->channels[j]; + break; + } + } + if (channel) + break; + } + + if (!channel) { + wcn36xx_err("Cannot tune to channel %d\n", ch); + return; + } + + wcn->band = band; + wcn->channel = channel; list_for_each_entry(tmp, &wcn->vif_list, list) { vif = wcn36xx_priv_to_vif(tmp); wcn36xx_smd_switch_channel(wcn, vif, ch); } + + return; } static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index fbd0558c2c196..dd2570e468084 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -281,6 +281,9 @@ struct wcn36xx { /* Debug file system entry */ struct wcn36xx_dfs_entry dfs; #endif /* CONFIG_WCN36XX_DEBUGFS */ + + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; }; static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, From 29696e0aa413b9d56558731aae3806d7cff48d36 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sat, 15 Jan 2022 00:16:45 +0000 Subject: [PATCH 24/28] wcn36xx: Track SNR and RSSI for each RX frame The BDs for each RX frame contain both the RSSI and SNR for the received frame. If we track and store this information it can be useful to us in get_survey() and potentially elsewhere. Reported-by: kernel test robot Signed-off-by: Bryan O'Donoghue Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220115001646.3981501-4-bryan.odonoghue@linaro.org --- drivers/net/wireless/ath/wcn36xx/main.c | 12 +++++++++ drivers/net/wireless/ath/wcn36xx/txrx.c | 31 ++++++++++++++++++++++ drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 10 +++++++ 3 files changed, 53 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index bb674b7258733..62e075f62099a 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -331,6 +331,7 @@ static int wcn36xx_start(struct ieee80211_hw *hw) INIT_LIST_HEAD(&wcn->vif_list); spin_lock_init(&wcn->dxe_lock); + spin_lock_init(&wcn->survey_lock); return 0; @@ -394,6 +395,7 @@ static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) struct wcn36xx_vif *tmp; struct ieee80211_supported_band *band; struct ieee80211_channel *channel; + unsigned long flags; int i, j; for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) { @@ -415,8 +417,10 @@ static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) return; } + spin_lock_irqsave(&wcn->survey_lock, flags); wcn->band = band; wcn->channel = channel; + spin_unlock_irqrestore(&wcn->survey_lock, flags); list_for_each_entry(tmp, &wcn->vif_list, list) { vif = wcn36xx_priv_to_vif(tmp); @@ -1557,6 +1561,7 @@ static int wcn36xx_probe(struct platform_device *pdev) void *wcnss; int ret; const u8 *addr; + int n_channels; wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); @@ -1584,6 +1589,13 @@ static int wcn36xx_probe(struct platform_device *pdev) goto out_wq; } + n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; + wcn->chan_survey = devm_kmalloc(wcn->dev, n_channels, GFP_KERNEL); + if (!wcn->chan_survey) { + ret = -ENOMEM; + goto out_wq; + } + ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32)); if (ret < 0) { wcn36xx_err("failed to set DMA mask: %d\n", ret); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index 425d388b4a4d0..c04983718d022 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -271,6 +271,34 @@ static void __skb_queue_purge_irq(struct sk_buff_head *list) dev_kfree_skb_irq(skb); } +static void wcn36xx_update_survey(struct wcn36xx *wcn, int rssi, int snr, + int band, int freq) +{ + static struct ieee80211_channel *channel; + struct ieee80211_supported_band *sband; + int idx; + int i; + + idx = 0; + if (band == NL80211_BAND_5GHZ) + idx = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels; + + sband = wcn->hw->wiphy->bands[band]; + channel = sband->channels; + + for (i = 0; i < sband->n_channels; i++, channel++) { + if (channel->center_freq == freq) { + idx += i; + break; + } + } + + spin_lock(&wcn->survey_lock); + wcn->chan_survey[idx].rssi = rssi; + wcn->chan_survey[idx].snr = snr; + spin_unlock(&wcn->survey_lock); +} + int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) { struct ieee80211_rx_status status; @@ -348,6 +376,9 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) status.freq = WCN36XX_CENTER_FREQ(wcn); } + wcn36xx_update_survey(wcn, status.signal, get_snr(bd), + status.band, status.freq); + if (bd->rate_id < ARRAY_SIZE(wcn36xx_rate_table)) { rate = &wcn36xx_rate_table[bd->rate_id]; status.encoding = rate->encoding; diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index dd2570e468084..81eaa74601d0f 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -194,7 +194,14 @@ struct wcn36xx_sta { enum wcn36xx_ampdu_state ampdu_state[16]; int non_agg_frame_ct; }; + struct wcn36xx_dxe_ch; + +struct wcn36xx_chan_survey { + s8 rssi; + u8 snr; +}; + struct wcn36xx { struct ieee80211_hw *hw; struct device *dev; @@ -284,6 +291,9 @@ struct wcn36xx { struct ieee80211_supported_band *band; struct ieee80211_channel *channel; + + spinlock_t survey_lock; /* protects chan_survey */ + struct wcn36xx_chan_survey *chan_survey; }; static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, From 51395cf204f27a466fd74c1d328fce1d3283166c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sat, 15 Jan 2022 00:16:46 +0000 Subject: [PATCH 25/28] wcn36xx: Add SNR reporting via get_survey() Add support for get_survey() reporting. Current channel and noise-floor are reported, other parameters such as scan, busy, TX and RX time are not immediately available. Noise is a useful metric to report, so bring it out now. Reported-by: kernel test robot Signed-off-by: Bryan O'Donoghue Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220115001646.3981501-5-bryan.odonoghue@linaro.org --- drivers/net/wireless/ath/wcn36xx/main.c | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 62e075f62099a..75661d4497127 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1357,6 +1357,49 @@ static void wcn36xx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } } +static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct wcn36xx *wcn = hw->priv; + struct ieee80211_supported_band *sband; + struct wcn36xx_chan_survey *chan_survey; + int band_idx; + unsigned long flags; + + sband = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]; + band_idx = idx; + if (band_idx >= sband->n_channels) { + band_idx -= sband->n_channels; + sband = wcn->hw->wiphy->bands[NL80211_BAND_5GHZ]; + } + + if (!sband || band_idx >= sband->n_channels) + return -ENOENT; + + spin_lock_irqsave(&wcn->survey_lock, flags); + + chan_survey = &wcn->chan_survey[idx]; + survey->channel = &sband->channels[band_idx]; + survey->noise = chan_survey->rssi - chan_survey->snr; + survey->filled = 0; + + if (chan_survey->rssi > -100 && chan_survey->rssi < 0) + survey->filled |= SURVEY_INFO_NOISE_DBM; + + if (survey->channel == wcn->channel) + survey->filled |= SURVEY_INFO_IN_USE; + + spin_unlock_irqrestore(&wcn->survey_lock, flags); + + wcn36xx_dbg(WCN36XX_DBG_MAC, + "ch %d rssi %d snr %d noise %d filled %x freq %d\n", + HW_VALUE_CHANNEL(survey->channel->hw_value), + chan_survey->rssi, chan_survey->snr, survey->noise, + survey->filled, survey->channel->center_freq); + + return 0; +} + static const struct ieee80211_ops wcn36xx_ops = { .start = wcn36xx_start, .stop = wcn36xx_stop, @@ -1385,6 +1428,7 @@ static const struct ieee80211_ops wcn36xx_ops = { .ipv6_addr_change = wcn36xx_ipv6_addr_change, #endif .flush = wcn36xx_flush, + .get_survey = wcn36xx_get_survey, CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd) }; From d1e0df1c57bd30871dd1c855742a7c346dbca853 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Wed, 19 Jan 2022 10:52:37 +0200 Subject: [PATCH 26/28] ath9k_htc: fix uninit value bugs Syzbot reported 2 KMSAN bugs in ath9k. All of them are caused by missing field initialization. In htc_connect_service() svc_meta_len and pad are not initialized. Based on code it looks like in current skb there is no service data, so simply initialize svc_meta_len to 0. htc_issue_send() does not initialize htc_frame_hdr::control array. Based on firmware code, it will initialize it by itself, so simply zero whole array to make KMSAN happy Fail logs: BUG: KMSAN: kernel-usb-infoleak in usb_submit_urb+0x6c1/0x2aa0 drivers/usb/core/urb.c:430 usb_submit_urb+0x6c1/0x2aa0 drivers/usb/core/urb.c:430 hif_usb_send_regout drivers/net/wireless/ath/ath9k/hif_usb.c:127 [inline] hif_usb_send+0x5f0/0x16f0 drivers/net/wireless/ath/ath9k/hif_usb.c:479 htc_issue_send drivers/net/wireless/ath/ath9k/htc_hst.c:34 [inline] htc_connect_service+0x143e/0x1960 drivers/net/wireless/ath/ath9k/htc_hst.c:275 ... Uninit was created at: slab_post_alloc_hook mm/slab.h:524 [inline] slab_alloc_node mm/slub.c:3251 [inline] __kmalloc_node_track_caller+0xe0c/0x1510 mm/slub.c:4974 kmalloc_reserve net/core/skbuff.c:354 [inline] __alloc_skb+0x545/0xf90 net/core/skbuff.c:426 alloc_skb include/linux/skbuff.h:1126 [inline] htc_connect_service+0x1029/0x1960 drivers/net/wireless/ath/ath9k/htc_hst.c:258 ... Bytes 4-7 of 18 are uninitialized Memory access of size 18 starts at ffff888027377e00 BUG: KMSAN: kernel-usb-infoleak in usb_submit_urb+0x6c1/0x2aa0 drivers/usb/core/urb.c:430 usb_submit_urb+0x6c1/0x2aa0 drivers/usb/core/urb.c:430 hif_usb_send_regout drivers/net/wireless/ath/ath9k/hif_usb.c:127 [inline] hif_usb_send+0x5f0/0x16f0 drivers/net/wireless/ath/ath9k/hif_usb.c:479 htc_issue_send drivers/net/wireless/ath/ath9k/htc_hst.c:34 [inline] htc_connect_service+0x143e/0x1960 drivers/net/wireless/ath/ath9k/htc_hst.c:275 ... Uninit was created at: slab_post_alloc_hook mm/slab.h:524 [inline] slab_alloc_node mm/slub.c:3251 [inline] __kmalloc_node_track_caller+0xe0c/0x1510 mm/slub.c:4974 kmalloc_reserve net/core/skbuff.c:354 [inline] __alloc_skb+0x545/0xf90 net/core/skbuff.c:426 alloc_skb include/linux/skbuff.h:1126 [inline] htc_connect_service+0x1029/0x1960 drivers/net/wireless/ath/ath9k/htc_hst.c:258 ... Bytes 16-17 of 18 are uninitialized Memory access of size 18 starts at ffff888027377e00 Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") Reported-by: syzbot+f83a1df1ed4f67e8d8ad@syzkaller.appspotmail.com Signed-off-by: Pavel Skripkin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220115122733.11160-1-paskripkin@gmail.com --- drivers/net/wireless/ath/ath9k/htc_hst.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 510e61e97dbcb..994ec48b2f669 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -30,6 +30,7 @@ static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, hdr->endpoint_id = epid; hdr->flags = flags; hdr->payload_len = cpu_to_be16(len); + memset(hdr->control, 0, sizeof(hdr->control)); status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb); @@ -272,6 +273,10 @@ int htc_connect_service(struct htc_target *target, conn_msg->dl_pipeid = endpoint->dl_pipeid; conn_msg->ul_pipeid = endpoint->ul_pipeid; + /* To prevent infoleak */ + conn_msg->svc_meta_len = 0; + conn_msg->pad = 0; + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); if (ret) goto err; From 1cb747192de2edb7e55920af8c458e4792908486 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Tue, 18 Jan 2022 22:42:11 -0500 Subject: [PATCH 27/28] ath11k: set WMI_PEER_40MHZ while peer assoc for 6 GHz When station connect to AP of 6 GHz with 40 MHz bandwidth, the TX is always stay 20 MHz, it is because the flag WMI_PEER_40MHZ is not set while peer assoc. Add the flag if remote peer is 40 MHz bandwidth. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 Fixes: c3a7d7eb4c98 ("ath11k: add 6 GHz params in peer assoc command") Signed-off-by: Wen Gong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220119034211.28622-1-quic_wgong@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 02228c4da4d0c..90fcd6adf2d5d 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2319,6 +2319,9 @@ static void ath11k_peer_assoc_h_he_6ghz(struct ath11k *ar, if (!arg->he_flag || band != NL80211_BAND_6GHZ || !sta->he_6ghz_capa.capa) return; + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + arg->bw_40 = true; + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->bw_80 = true; From 0d6e997b76216ca104167a2a0fb79823a7fa9e97 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Tue, 18 Jan 2022 23:13:55 -0500 Subject: [PATCH 28/28] ath11k: avoid firmware crash when reg set for QCA6390/WCN6855 For the NL80211_REGDOM_SET_BY_USER hint from cfg80211, it set the new alpha2 code to ath11k, then ath11k send WMI_SET_INIT_COUNTRY_CMDID to firmware for all chips currently. When test with WCN6855/QCA6390 chips, this WMI CMD leads firmware crash. For AP based chips(ipq8074/qcn9074), WMI_SET_INIT_COUNTRY_CMDID is used to send to firmware, for STATION based chips(WCN6855/QCA6390), it need to use another WMI CMD WMI_SET_CURRENT_COUNTRY_CMDID. Add flag current_cc_support in hardware parameters, it is used to distinguish AP/STA platform. After that, the firmware will work normal and the regulatory feature works well for QCA6390/WCN6855. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 Signed-off-by: Wen Gong Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220119041355.32014-1-quic_wgong@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ drivers/net/wireless/ath/ath11k/hw.h | 1 + drivers/net/wireless/ath/ath11k/reg.c | 25 +++++++++++++++++-------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 1ad77bd721b97..7c508e9baa6d1 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -98,6 +98,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -162,6 +163,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, }, { .name = "qca6390 hw2.0", @@ -225,6 +227,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, }, { .name = "qcn9074 hw1.0", @@ -288,6 +291,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = false, .supports_rssi_stats = false, .fw_wmi_diag_event = false, + .current_cc_support = false, }, { .name = "wcn6855 hw2.0", @@ -351,6 +355,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, }, { .name = "wcn6855 hw2.1", @@ -413,6 +418,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .wakeup_mhi = true, .supports_rssi_stats = true, .fw_wmi_diag_event = true, + .current_cc_support = true, }, }; diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index e06d392be2fbe..c10e1a01fb027 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -192,6 +192,7 @@ struct ath11k_hw_params { bool wakeup_mhi; bool supports_rssi_stats; bool fw_wmi_diag_event; + bool current_cc_support; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index d6575feca5a26..81e11cde31d7b 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -48,6 +48,7 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wmi_init_country_params init_country_param; + struct wmi_set_current_country_params set_current_param = {}; struct ath11k *ar = hw->priv; int ret; @@ -76,18 +77,26 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) return; } - /* Set the country code to the firmware and wait for + /* Set the country code to the firmware and will receive * the WMI_REG_CHAN_LIST_CC EVENT for updating the * reg info */ - init_country_param.flags = ALPHA_IS_SET; - memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); - init_country_param.cc_info.alpha2[2] = 0; + if (ar->ab->hw_params.current_cc_support) { + memcpy(&set_current_param.alpha2, request->alpha2, 2); + ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + if (ret) + ath11k_warn(ar->ab, + "failed set current country code: %d\n", ret); + } else { + init_country_param.flags = ALPHA_IS_SET; + memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); + init_country_param.cc_info.alpha2[2] = 0; - ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); - if (ret) - ath11k_warn(ar->ab, - "INIT Country code set to fw failed : %d\n", ret); + ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); + if (ret) + ath11k_warn(ar->ab, + "INIT Country code set to fw failed : %d\n", ret); + } ath11k_mac_11d_scan_stop(ar); ar->regdom_set_by_user = true;