From 4504f0e5b5714d9d26b1a80bf1fc133c95830588 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 19 Apr 2019 10:28:45 +0300 Subject: [PATCH 01/52] ath10k: sdio: workaround firmware UART pin configuration bug On QCA6174 SDIO devices the SDIO interrupt will fail if UART is disabled from ath10k. SDIO firmware enables UART printouts by default. If ath10k will try to enable UART again the firmware will configure it's GPIO line incorrectly and SDIO interrupts won't work anymore. The workaround is to set UART pin again (19 for QCA6174 SDIO) if uart_print is 0. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 10 +++++++++- drivers/net/wireless/ath/ath10k/hw.h | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 835b8de92d55..cfd7bb29a1ec 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2065,8 +2065,16 @@ static int ath10k_init_uart(struct ath10k *ar) return ret; } - if (!uart_print) + if (!uart_print && ar->hw_params.uart_pin_workaround) { + ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, + ar->hw_params.uart_pin); + if (ret) { + ath10k_warn(ar, "failed to set UART TX pin: %d", ret); + return ret; + } + return 0; + } ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 71314999aa24..a92fce1bb6f0 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -606,6 +606,11 @@ struct ath10k_hw_params { /* target supporting fw download via diag ce */ bool fw_diag_ce_download; + + /* need to set uart pin if disable uart print, workaround for a + * firmware bug + */ + bool uart_pin_workaround; }; struct htt_rx_desc; From 70736b9753fdebccf68eb84d8be0f8d9c5a7680e Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 19 Apr 2019 10:28:47 +0300 Subject: [PATCH 02/52] ath10k: don't disable interrupts in ath10k_sdio_remove() Disabling interrupts this early meant WMI communication was not working anymore when the SDIO device was removed. But we call ath10k_core_unregister() that will eventually call ath10k_sdio_hif_stop(), which disables the interrupts. So there's actually no need to disable interrupts here. Also remove cancel_work_sync() as it's also called in ath10k_sdio_hif_stop(). Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index fae56c67766f..d573d24624c0 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2073,8 +2073,6 @@ static void ath10k_sdio_remove(struct sdio_func *func) "sdio removed func %d vendor 0x%x device 0x%x\n", func->num, func->vendor, func->device); - (void)ath10k_sdio_hif_disable_intrs(ar); - cancel_work_sync(&ar_sdio->wr_async_work); ath10k_core_unregister(ar); ath10k_core_destroy(ar); } From e2a6b711282a371c5153239e0468a48254f17ca6 Mon Sep 17 00:00:00 2001 From: Alagu Sankar Date: Fri, 19 Apr 2019 10:28:49 +0300 Subject: [PATCH 03/52] ath10k: htt: don't use txdone_fifo with SDIO HTT High Latency (ATH10K_DEV_TYPE_HL) does not use txdone_fifo at all, we don't even initialise it by skipping ath10k_htt_tx_alloc_buf() in ath10k_htt_tx_start(). Because of this using QCA6174 SDIO ath10k_htt_rx_tx_compl_ind() will crash when it accesses unitialised txdone_fifo. So skip txdone_fifo when using High Latency mode. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Co-developed-by: Wen Gong Signed-off-by: Alagu Sankar Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 1acc622d2183..f22840bbc389 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2277,7 +2277,9 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ - if (!kfifo_put(&htt->txdone_fifo, tx_done)) { + if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) { + ath10k_txrx_tx_unref(htt, &tx_done); + } else if (!kfifo_put(&htt->txdone_fifo, tx_done)) { ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n", tx_done.msdu_id, tx_done.status); ath10k_txrx_tx_unref(htt, &tx_done); From 6d084ac27ab4b2cc814b142459393e73c3cb47b9 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 19 Apr 2019 10:28:52 +0300 Subject: [PATCH 04/52] ath10k: initialise struct ath10k_bus params to zero This way we don't need to set every variable and give them to default, which is zero. This is also safer in case we forgot to initalise a new field in some of the bus modules. Compile tested only. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 2 +- drivers/net/wireless/ath/ath10k/pci.c | 2 +- drivers/net/wireless/ath/ath10k/sdio.c | 2 +- drivers/net/wireless/ath/ath10k/snoc.c | 2 +- drivers/net/wireless/ath/ath10k/usb.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index 0bf726c55736..f80854180e21 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -740,7 +740,7 @@ static int ath10k_ahb_probe(struct platform_device *pdev) enum ath10k_hw_rev hw_rev; size_t size; int ret; - struct ath10k_bus_params bus_params; + struct ath10k_bus_params bus_params = {}; of_id = of_match_device(ath10k_ahb_of_match, &pdev->dev); if (!of_id) { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 271f92c24d44..13c24cd977d2 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3478,7 +3478,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, struct ath10k *ar; struct ath10k_pci *ar_pci; enum ath10k_hw_rev hw_rev; - struct ath10k_bus_params bus_params; + struct ath10k_bus_params bus_params = {}; bool pci_ps; int (*pci_soft_reset)(struct ath10k *ar); int (*pci_hard_reset)(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index d573d24624c0..bbfdc4d7cf5f 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1954,7 +1954,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, struct ath10k *ar; enum ath10k_hw_rev hw_rev; u32 dev_id_base; - struct ath10k_bus_params bus_params; + struct ath10k_bus_params bus_params = {}; int ret, i; /* Assumption: All SDIO based chipsets (so far) are QCA6174 based. diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 873cb4ce419b..845914f2eaf8 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1249,7 +1249,7 @@ static int ath10k_snoc_resource_init(struct ath10k *ar) int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); - struct ath10k_bus_params bus_params; + struct ath10k_bus_params bus_params = {}; int ret; if (test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags)) diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 970cf69ac35f..2fb0fb40e86d 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -973,7 +973,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, struct usb_device *dev = interface_to_usbdev(interface); int ret, vendor_id, product_id; enum ath10k_hw_rev hw_rev; - struct ath10k_bus_params bus_params; + struct ath10k_bus_params bus_params = {}; /* Assumption: All USB based chipsets (so far) are QCA9377 based. * If there will be newer chipsets that does not use the hw reg From 8ea51e409eb04fb520c96e098b6c289fc97a2cf4 Mon Sep 17 00:00:00 2001 From: Alagu Sankar Date: Fri, 19 Apr 2019 10:28:54 +0300 Subject: [PATCH 05/52] ath10k: htt: support MSDU ids with SDIO Transmit completion for SDIO is similar to PCIe, modify the high latency path to allow SDIO modules to use the msdu id. kvalo: the original patch from Alagu enabled this only for SDIO but I'm not sure should we also enable this with USB. I'll use bus params to enable this for so that it's easy to enable also for USB later. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Co-developed-by: Wen Gong Signed-off-by: Alagu Sankar Signed-off-by: Wen Gong . Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/htt_tx.c | 13 ++++++++++++- drivers/net/wireless/ath/ath10k/sdio.c | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index e08a17b01e03..f22cb3f49034 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -919,6 +919,7 @@ struct ath10k_bus_params { u32 chip_id; enum ath10k_dev_type dev_type; bool link_can_suspend; + bool hl_msdu_ids; }; struct ath10k { diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index d8e9cc0bb772..88f880b601d8 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1244,6 +1244,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth); u8 flags0 = 0; u16 flags1 = 0; + u16 msdu_id = 0; data_len = msdu->len; @@ -1291,6 +1292,16 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm } } + if (ar->bus_param.hl_msdu_ids) { + flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED; + res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); + if (res < 0) { + ath10k_err(ar, "msdu_id allocation failed %d\n", res); + goto out; + } + msdu_id = res; + } + skb_push(msdu, sizeof(*cmd_hdr)); skb_push(msdu, sizeof(*tx_desc)); cmd_hdr = (struct htt_cmd_hdr *)msdu->data; @@ -1300,7 +1311,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm tx_desc->flags0 = flags0; tx_desc->flags1 = __cpu_to_le16(flags1); tx_desc->len = __cpu_to_le16(data_len); - tx_desc->id = 0; + tx_desc->id = __cpu_to_le16(msdu_id); tx_desc->frags_paddr = 0; /* always zero */ /* Initialize peer_id to INVALID_PEER because this is NOT * Reinjection path diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index bbfdc4d7cf5f..d5073fac9509 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2045,6 +2045,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, bus_params.dev_type = ATH10K_DEV_TYPE_HL; /* TODO: don't know yet how to get chip_id with SDIO */ bus_params.chip_id = 0; + bus_params.hl_msdu_ids = true; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); From 30382dd1cf3a141bfaa568ee183c1892090fa79a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 19 Apr 2019 10:28:56 +0300 Subject: [PATCH 06/52] ath10k: fix use-after-free on SDIO data frames With SDIO there's a use after free after a data frame is transfered, call stack below. This happens because ath10k_htt_tx_hl() directly transmits the skb provided by mac80211 using ath10k_htc_send(), all other HTT functions use separate skb created with ath10k_htc_alloc_skb() to transmit the HTC packet. After the packet is transmitted mac80211 frees the skb in ieee80211_tx_status() but HTT layer expects that it still owns the skb, and frees it in ath10k_htt_htc_tx_complete(). To fix this take a reference of skb before sending it to HTC layer to make sure we still own the skb. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. ath10k_htt_tx_hl() is only used by SDIO and USB so other busses (PCI, AHB and SNOC) should be unaffected. call stack of use-after-free: dump_backtrace+0x0/0x2d8 show_stack+0x20/0x2c __dump_stack+0x20/0x28 dump_stack+0xc8/0xec print_address_description+0x74/0x240 kasan_report+0x258/0x274 __asan_report_load4_noabort+0x20/0x28 skb_pull+0xbc/0x114 ath10k_htc_notify_tx_completion+0x190/0x2a4 [ath10k_core] ath10k_sdio_write_async_work+0x1e4/0x2c4 [ath10k_sdio] process_one_work+0x3d8/0x8b0 worker_thread+0x518/0x7e0 kthread+0x260/0x278 ret_from_fork+0x10/0x18 Allocated by one task: kasan_kmalloc+0xa0/0x13c kasan_slab_alloc+0x14/0x1c kmem_cache_alloc+0x144/0x208 __alloc_skb+0xec/0x394 alloc_skb_with_frags+0x8c/0x374 sock_alloc_send_pskb+0x520/0x5d4 sock_alloc_send_skb+0x40/0x50 __ip_append_data+0xf5c/0x1858 ip_make_skb+0x194/0x1d4 udp_sendmsg+0xf24/0x1ab8 inet_sendmsg+0x1b0/0x2e0 sock_sendmsg+0x88/0xa0 __sys_sendto+0x220/0x3a8 __arm64_sys_sendto+0x78/0x80 el0_svc_common+0x120/0x1e0 el0_svc_compat_handler+0x64/0x80 el0_svc_compat+0x8/0x18 Freed by another task: __kasan_slab_free+0x120/0x1d4 kasan_slab_free+0x10/0x1c kmem_cache_free+0x74/0x504 kfree_skbmem+0x88/0xc8 __kfree_skb+0x24/0x2c consume_skb+0x114/0x18c __ieee80211_tx_status+0xb7c/0xf60 [mac80211] ieee80211_tx_status+0x224/0x270 [mac80211] ath10k_txrx_tx_unref+0x564/0x950 [ath10k_core] ath10k_htt_t2h_msg_handler+0x178c/0x2a38 [ath10k_core] ath10k_htt_htc_t2h_msg_handler+0x20/0x30 [ath10k_core] ath10k_sdio_irq_handler+0xcc0/0x1654 [ath10k_sdio] process_sdio_pending_irqs+0xec/0x358 sdio_run_irqs+0x68/0xe4 sdio_irq_work+0x1c/0x28 process_one_work+0x3d8/0x8b0 worker_thread+0x518/0x7e0 kthread+0x260/0x278 ret_from_fork+0x10/0x18 Reported-by: Wen Gong Tested-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 88f880b601d8..78f07b7a3b21 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1302,6 +1302,13 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm msdu_id = res; } + /* As msdu is freed by mac80211 (in ieee80211_tx_status()) and by + * ath10k (in ath10k_htt_htc_tx_complete()) we have to increase + * reference by one to avoid a use-after-free case and a double + * free. + */ + skb_get(msdu); + skb_push(msdu, sizeof(*cmd_hdr)); skb_push(msdu, sizeof(*tx_desc)); cmd_hdr = (struct htt_cmd_hdr *)msdu->data; From fbd428a5b828bf3fd3627e1526dd470f64f90d2c Mon Sep 17 00:00:00 2001 From: Alagu Sankar Date: Fri, 19 Apr 2019 10:28:59 +0300 Subject: [PATCH 07/52] ath10k: add initialization of HTC header HTC header carries junk values that may be interpreted by the firmware differently. Enable credit update only if flow control is enabled for the corresponding endpoint. Signed-off-by: Alagu Sankar Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 805a7f8a04f2..1d4d1a1992fe 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -73,6 +73,7 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, struct ath10k_htc_hdr *hdr; hdr = (struct ath10k_htc_hdr *)skb->data; + memset(hdr, 0, sizeof(struct ath10k_htc_hdr)); hdr->eid = ep->eid; hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); From f91b63b0e3b22f3688fd6b352128c3e0e40d02b4 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Fri, 19 Apr 2019 10:29:06 +0300 Subject: [PATCH 08/52] ath10k: sdio: add missing error check Although not likely, the bundle allocation might fail. Add proper error check and warning print. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index d5073fac9509..30102090e282 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -584,6 +584,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, act_len, &bndl_cnt); + if (ret) { + ath10k_warn(ar, "alloc_bundle error %d\n", ret); + goto err; + } + n_lookaheads += bndl_cnt; i += bndl_cnt; /*Next buffer will be the last in the bundle */ From e1bddde9737ac4687ca6e2fe6c95f67a9bec353b Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 26 Apr 2019 09:41:07 +0300 Subject: [PATCH 09/52] ath10k: add struct for high latency PN replay protection Add the struct for PN replay protection and fragment packet handler. Also fix the bitmask of HTT_RX_DESC_HL_INFO_MCAST_BCAST to match what's currently used by SDIO firmware. The defines are not used yet so it's safe to modify them. Remove the conflicting HTT_RX_DESC_HL_INFO_FRAGMENT as it's not either used in ath10k. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 8 +++++ drivers/net/wireless/ath/ath10k/htt.h | 43 +++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index f22cb3f49034..02e53fb85814 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -400,6 +400,14 @@ struct ath10k_peer { /* protected by ar->data_lock */ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + union htt_rx_pn_t tids_last_pn[ATH10K_TXRX_NUM_EXT_TIDS]; + bool tids_last_pn_valid[ATH10K_TXRX_NUM_EXT_TIDS]; + union htt_rx_pn_t frag_tids_last_pn[ATH10K_TXRX_NUM_EXT_TIDS]; + u32 frag_tids_seq[ATH10K_TXRX_NUM_EXT_TIDS]; + struct { + enum htt_security_types sec_type; + int pn_len; + } rx_pn[ATH10K_HTT_TXRX_PEER_SECURITY_MAX]; }; struct ath10k_txq { diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 4cee5492abc8..fb5cb9169073 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -733,6 +733,20 @@ struct htt_rx_indication_hl { struct htt_rx_indication_mpdu_range mpdu_ranges[0]; } __packed; +struct htt_hl_rx_desc { + __le32 info; + __le32 pn_31_0; + union { + struct { + __le16 pn_47_32; + __le16 pn_63_48; + } pn16; + __le32 pn_63_32; + } u0; + __le32 pn_95_64; + __le32 pn_127_96; +} __packed; + static inline struct htt_rx_indication_mpdu_range * htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind) { @@ -790,6 +804,21 @@ struct htt_rx_peer_unmap { __le16 peer_id; } __packed; +enum htt_txrx_sec_cast_type { + HTT_TXRX_SEC_MCAST = 0, + HTT_TXRX_SEC_UCAST +}; + +enum htt_rx_pn_check_type { + HTT_RX_NON_PN_CHECK = 0, + HTT_RX_PN_CHECK +}; + +enum htt_rx_tkip_demic_type { + HTT_RX_NON_TKIP_MIC = 0, + HTT_RX_TKIP_MIC +}; + enum htt_security_types { HTT_SECURITY_NONE, HTT_SECURITY_WEP128, @@ -803,6 +832,9 @@ enum htt_security_types { HTT_NUM_SECURITY_TYPES /* keep this last! */ }; +#define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2 +#define ATH10K_TXRX_NUM_EXT_TIDS 19 + enum htt_security_flags { #define HTT_SECURITY_TYPE_MASK 0x7F #define HTT_SECURITY_TYPE_LSB 0 @@ -1010,6 +1042,11 @@ struct htt_rx_fragment_indication { u8 fw_msdu_rx_desc[0]; } __packed; +#define ATH10K_IEEE80211_EXTIV BIT(5) +#define ATH10K_IEEE80211_TKIP_MICLEN 8 /* trailing MIC */ + +#define HTT_RX_FRAG_IND_INFO0_HEADER_LEN 16 + #define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK 0x1F #define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB 0 #define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20 @@ -2128,10 +2165,8 @@ struct htt_rx_desc { #define HTT_RX_DESC_HL_INFO_ENCRYPTED_LSB 12 #define HTT_RX_DESC_HL_INFO_CHAN_INFO_PRESENT_MASK 0x00002000 #define HTT_RX_DESC_HL_INFO_CHAN_INFO_PRESENT_LSB 13 -#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_MASK 0x00008000 -#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_LSB 15 -#define HTT_RX_DESC_HL_INFO_FRAGMENT_MASK 0x00010000 -#define HTT_RX_DESC_HL_INFO_FRAGMENT_LSB 16 +#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_MASK 0x00010000 +#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_LSB 16 #define HTT_RX_DESC_HL_INFO_KEY_ID_OCT_MASK 0x01fe0000 #define HTT_RX_DESC_HL_INFO_KEY_ID_OCT_LSB 17 From 28ce53b6cfda35d25ca3bbf2665e84be38a5f9dd Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 26 Apr 2019 09:41:08 +0300 Subject: [PATCH 10/52] ath10k: add handler for HTT_T2H_MSG_TYPE_SEC_IND event Add the handler for HTT_T2H_MSG_TYPE_SEC_IND event from firmware, which stores PN for replay check implemented in the following patch. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index f22840bbc389..cc2875d73bf0 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -3267,6 +3267,51 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data) rcu_read_unlock(); } +static int ath10k_htt_rx_pn_len(enum htt_security_types sec_type) +{ + switch (sec_type) { + case HTT_SECURITY_TKIP: + case HTT_SECURITY_TKIP_NOMIC: + case HTT_SECURITY_AES_CCMP: + return 48; + default: + return 0; + } +} + +static void ath10k_htt_rx_sec_ind_handler(struct ath10k *ar, + struct htt_security_indication *ev) +{ + enum htt_txrx_sec_cast_type sec_index; + enum htt_security_types sec_type; + struct ath10k_peer *peer; + + spin_lock_bh(&ar->data_lock); + + peer = ath10k_peer_find_by_id(ar, __le16_to_cpu(ev->peer_id)); + if (!peer) { + ath10k_warn(ar, "failed to find peer id %d for security indication", + __le16_to_cpu(ev->peer_id)); + goto out; + } + + sec_type = MS(ev->flags, HTT_SECURITY_TYPE); + + if (ev->flags & HTT_SECURITY_IS_UNICAST) + sec_index = HTT_TXRX_SEC_UCAST; + else + sec_index = HTT_TXRX_SEC_MCAST; + + peer->rx_pn[sec_index].sec_type = sec_type; + peer->rx_pn[sec_index].pn_len = ath10k_htt_rx_pn_len(sec_type); + + memset(peer->tids_last_pn_valid, 0, sizeof(peer->tids_last_pn_valid)); + memset(peer->tids_last_pn, 0, sizeof(peer->tids_last_pn)); + +out: + spin_unlock_bh(&ar->data_lock); +} + bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htt *htt = &ar->htt; @@ -3360,6 +3405,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) struct ath10k *ar = htt->ar; struct htt_security_indication *ev = &resp->security_indication; + ath10k_htt_rx_sec_ind_handler(ar, ev); ath10k_dbg(ar, ATH10K_DBG_HTT, "sec ind peer_id %d unicast %d type %d\n", __le16_to_cpu(ev->peer_id), From 130c77495708c61f0aa839e436a8a3a973824b19 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 26 Apr 2019 09:41:09 +0300 Subject: [PATCH 11/52] ath10k: add PN replay protection for high latency devices On high latency devices (SDIO, USB) ath10k did not do PN replay check, a data frame with an invalid PN number was not discard as it should have been. So this patch implements PN replay in ath10k. PN replay check for fragmented frames is implemented in followup patch. With low latency devices (PCI, AHB) hardware can store the data frames's content to host memory directly and the firmware can fully reorder data frames, and do PN replay check at the same time. But for high latency devices all data frames will be received and stored in firmware's memory and it is hard to do full reorder because of the memory size limitations in the firmware. This is why the PN replay protections needs to be implemented in host driver. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 97 +++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index cc2875d73bf0..961e14633320 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2061,9 +2061,91 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) return 0; } +static void ath10k_htt_rx_mpdu_desc_pn_hl(struct htt_hl_rx_desc *rx_desc, + union htt_rx_pn_t *pn, + int pn_len_bits) +{ + switch (pn_len_bits) { + case 48: + pn->pn48 = __le32_to_cpu(rx_desc->pn_31_0) + + ((u64)(__le32_to_cpu(rx_desc->u0.pn_63_32) & 0xFFFF) << 32); + break; + case 24: + pn->pn24 = __le32_to_cpu(rx_desc->pn_31_0); + break; + }; +} + +static bool ath10k_htt_rx_pn_cmp48(union htt_rx_pn_t *new_pn, + union htt_rx_pn_t *old_pn) +{ + return ((new_pn->pn48 & 0xffffffffffffULL) <= + (old_pn->pn48 & 0xffffffffffffULL)); +} + +static bool ath10k_htt_rx_pn_check_replay_hl(struct ath10k *ar, + struct ath10k_peer *peer, + struct htt_rx_indication_hl *rx) +{ + bool last_pn_valid, pn_invalid = false; + enum htt_txrx_sec_cast_type sec_index; + enum htt_security_types sec_type; + union htt_rx_pn_t new_pn = {0}; + struct htt_hl_rx_desc *rx_desc; + union htt_rx_pn_t *last_pn; + u32 rx_desc_info, tid; + int num_mpdu_ranges; + + lockdep_assert_held(&ar->data_lock); + + if (!peer) + return false; + + if (!(rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU)) + return false; + + num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), + HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + + rx_desc = (struct htt_hl_rx_desc *)&rx->mpdu_ranges[num_mpdu_ranges]; + rx_desc_info = __le32_to_cpu(rx_desc->info); + + if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) + return false; + + tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID); + last_pn_valid = peer->tids_last_pn_valid[tid]; + last_pn = &peer->tids_last_pn[tid]; + + if (MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST)) + sec_index = HTT_TXRX_SEC_MCAST; + else + sec_index = HTT_TXRX_SEC_UCAST; + + sec_type = peer->rx_pn[sec_index].sec_type; + ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len); + + if (sec_type != HTT_SECURITY_AES_CCMP && + sec_type != HTT_SECURITY_TKIP && + sec_type != HTT_SECURITY_TKIP_NOMIC) + return false; + + if (last_pn_valid) + pn_invalid = ath10k_htt_rx_pn_cmp48(&new_pn, last_pn); + else + peer->tids_last_pn_valid[tid] = 1; + + if (!pn_invalid) + last_pn->pn48 = new_pn.pn48; + + return pn_invalid; +} + static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, struct htt_rx_indication_hl *rx, - struct sk_buff *skb) + struct sk_buff *skb, + enum htt_rx_pn_check_type check_pn_type, + enum htt_rx_tkip_demic_type tkip_mic_type) { struct ath10k *ar = htt->ar; struct ath10k_peer *peer; @@ -2107,6 +2189,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, goto err; } + if (check_pn_type == HTT_RX_PN_CHECK && + ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx)) + goto err; + /* Strip off all headers before the MAC header before delivery to * mac80211 */ @@ -2114,6 +2200,7 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, sizeof(rx->ppdu) + sizeof(rx->prefix) + sizeof(rx->fw_desc) + sizeof(*mpdu_ranges) * num_mpdu_ranges + rx_desc_len; + skb_pull(skb, tot_hdr_len); hdr = (struct ieee80211_hdr *)skb->data; @@ -2162,6 +2249,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, RX_FLAG_MMIC_STRIPPED; } + if (tkip_mic_type == HTT_RX_TKIP_MIC) + rx_status->flag &= ~RX_FLAG_IV_STRIPPED & + ~RX_FLAG_MMIC_STRIPPED; + ieee80211_rx_ni(ar->hw, skb); /* We have delivered the skb to the upper layers (mac80211) so we @@ -3343,7 +3434,9 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, - skb); + skb, + HTT_RX_PN_CHECK, + HTT_RX_NON_TKIP_MIC); else ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind); break; From 33f9747291ff384aeed7f0c97c19d5dba621f265 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 26 Apr 2019 09:41:10 +0300 Subject: [PATCH 12/52] ath10k: add fragmentation handler for high latency devices On high latency devices (SDIO, USB) ath10k did not handle fragmented frames and all fragmented frames on receive path were lost in ath10k. Even a simple ping test failed with fragmentation. The fragmented packets are decapsulated based on the security mode, then the PN is checked and the fragmented frame is passed to mac80211. mac80211 in ieee80211_rx_h_defragment() will then combine the fragment frames and forward to upper layers. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 13 ++ drivers/net/wireless/ath/ath10k/htt_rx.c | 230 +++++++++++++++++++++++ 2 files changed, 243 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index fb5cb9169073..b28c167cb5b4 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -2092,6 +2092,9 @@ struct ath10k_htt_rx_ops { int idx); void* (*htt_get_vaddr_ring)(struct ath10k_htt *htt); void (*htt_reset_paddrs_ring)(struct ath10k_htt *htt, int idx); + bool (*htt_rx_proc_rx_frag_ind)(struct ath10k_htt *htt, + struct htt_rx_fragment_indication *rx, + struct sk_buff *skb); }; static inline size_t ath10k_htt_get_rx_ring_size(struct ath10k_htt *htt) @@ -2131,6 +2134,16 @@ static inline void ath10k_htt_reset_paddrs_ring(struct ath10k_htt *htt, int idx) htt->rx_ops->htt_reset_paddrs_ring(htt, idx); } +static inline bool ath10k_htt_rx_proc_rx_frag_ind(struct ath10k_htt *htt, + struct htt_rx_fragment_indication *rx, + struct sk_buff *skb) +{ + if (!htt->rx_ops->htt_rx_proc_rx_frag_ind) + return true; + + return htt->rx_ops->htt_rx_proc_rx_frag_ind(htt, rx, skb); +} + #define RX_HTT_HDR_STATUS_LEN 64 /* This structure layout is programmed via rx ring setup diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 961e14633320..45f4eecc3839 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2266,6 +2266,231 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, return true; } +static int ath10k_htt_rx_frag_tkip_decap_nomic(struct sk_buff *skb, + u16 head_len, + u16 hdr_len) +{ + u8 *ivp, *orig_hdr; + + orig_hdr = skb->data; + ivp = orig_hdr + hdr_len + head_len; + + /* the ExtIV bit is always set to 1 for TKIP */ + if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV)) + return -EINVAL; + + memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len); + skb_pull(skb, IEEE80211_TKIP_IV_LEN); + skb_trim(skb, skb->len - ATH10K_IEEE80211_TKIP_MICLEN); + return 0; +} + +static int ath10k_htt_rx_frag_tkip_decap_withmic(struct sk_buff *skb, + u16 head_len, + u16 hdr_len) +{ + u8 *ivp, *orig_hdr; + + orig_hdr = skb->data; + ivp = orig_hdr + hdr_len + head_len; + + /* the ExtIV bit is always set to 1 for TKIP */ + if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV)) + return -EINVAL; + + memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len); + skb_pull(skb, IEEE80211_TKIP_IV_LEN); + skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN); + return 0; +} + +static int ath10k_htt_rx_frag_ccmp_decap(struct sk_buff *skb, + u16 head_len, + u16 hdr_len) +{ + u8 *ivp, *orig_hdr; + + orig_hdr = skb->data; + ivp = orig_hdr + hdr_len + head_len; + + /* the ExtIV bit is always set to 1 for CCMP */ + if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV)) + return -EINVAL; + + skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN); + memmove(orig_hdr + IEEE80211_CCMP_HDR_LEN, orig_hdr, head_len + hdr_len); + skb_pull(skb, IEEE80211_CCMP_HDR_LEN); + return 0; +} + +static int ath10k_htt_rx_frag_wep_decap(struct sk_buff *skb, + u16 head_len, + u16 hdr_len) +{ + u8 *orig_hdr; + + orig_hdr = skb->data; + + memmove(orig_hdr + IEEE80211_WEP_IV_LEN, + orig_hdr, head_len + hdr_len); + skb_pull(skb, IEEE80211_WEP_IV_LEN); + skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN); + return 0; +} + +static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt, + struct htt_rx_fragment_indication *rx, + struct sk_buff *skb) +{ + struct ath10k *ar = htt->ar; + enum htt_rx_tkip_demic_type tkip_mic = HTT_RX_NON_TKIP_MIC; + enum htt_txrx_sec_cast_type sec_index; + struct htt_rx_indication_hl *rx_hl; + enum htt_security_types sec_type; + u32 tid, frag, seq, rx_desc_info; + union htt_rx_pn_t new_pn = {0}; + struct htt_hl_rx_desc *rx_desc; + u16 peer_id, sc, hdr_space; + union htt_rx_pn_t *last_pn; + struct ieee80211_hdr *hdr; + int ret, num_mpdu_ranges; + struct ath10k_peer *peer; + struct htt_resp *resp; + size_t tot_hdr_len; + + resp = (struct htt_resp *)(skb->data + HTT_RX_FRAG_IND_INFO0_HEADER_LEN); + skb_pull(skb, HTT_RX_FRAG_IND_INFO0_HEADER_LEN); + skb_trim(skb, skb->len - FCS_LEN); + + peer_id = __le16_to_cpu(rx->peer_id); + rx_hl = (struct htt_rx_indication_hl *)(&resp->rx_ind_hl); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) { + ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer: %u\n", peer_id); + goto err; + } + + num_mpdu_ranges = MS(__le32_to_cpu(rx_hl->hdr.info1), + HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + + tot_hdr_len = sizeof(struct htt_resp_hdr) + + sizeof(rx_hl->hdr) + + sizeof(rx_hl->ppdu) + + sizeof(rx_hl->prefix) + + sizeof(rx_hl->fw_desc) + + sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges; + + tid = MS(rx_hl->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID); + rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len); + rx_desc_info = __le32_to_cpu(rx_desc->info); + + if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) { + spin_unlock_bh(&ar->data_lock); + return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb, + HTT_RX_NON_PN_CHECK, + HTT_RX_NON_TKIP_MIC); + } + + hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len); + + if (ieee80211_has_retry(hdr->frame_control)) + goto err; + + hdr_space = ieee80211_hdrlen(hdr->frame_control); + sc = __le16_to_cpu(hdr->seq_ctrl); + seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + frag = sc & IEEE80211_SCTL_FRAG; + + sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ? + HTT_TXRX_SEC_MCAST : HTT_TXRX_SEC_UCAST; + sec_type = peer->rx_pn[sec_index].sec_type; + ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len); + + switch (sec_type) { + case HTT_SECURITY_TKIP: + tkip_mic = HTT_RX_TKIP_MIC; + ret = ath10k_htt_rx_frag_tkip_decap_withmic(skb, + tot_hdr_len + + rx_hl->fw_desc.len, + hdr_space); + if (ret) + goto err; + break; + case HTT_SECURITY_TKIP_NOMIC: + ret = ath10k_htt_rx_frag_tkip_decap_nomic(skb, + tot_hdr_len + + rx_hl->fw_desc.len, + hdr_space); + if (ret) + goto err; + break; + case HTT_SECURITY_AES_CCMP: + ret = ath10k_htt_rx_frag_ccmp_decap(skb, + tot_hdr_len + rx_hl->fw_desc.len, + hdr_space); + if (ret) + goto err; + break; + case HTT_SECURITY_WEP128: + case HTT_SECURITY_WEP104: + case HTT_SECURITY_WEP40: + ret = ath10k_htt_rx_frag_wep_decap(skb, + tot_hdr_len + rx_hl->fw_desc.len, + hdr_space); + if (ret) + goto err; + break; + default: + break; + } + + resp = (struct htt_resp *)(skb->data); + + if (sec_type != HTT_SECURITY_AES_CCMP && + sec_type != HTT_SECURITY_TKIP && + sec_type != HTT_SECURITY_TKIP_NOMIC) { + spin_unlock_bh(&ar->data_lock); + return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb, + HTT_RX_NON_PN_CHECK, + HTT_RX_NON_TKIP_MIC); + } + + last_pn = &peer->frag_tids_last_pn[tid]; + + if (frag == 0) { + if (ath10k_htt_rx_pn_check_replay_hl(ar, peer, &resp->rx_ind_hl)) + goto err; + + last_pn->pn48 = new_pn.pn48; + peer->frag_tids_seq[tid] = seq; + } else if (sec_type == HTT_SECURITY_AES_CCMP) { + if (seq != peer->frag_tids_seq[tid]) + goto err; + + if (new_pn.pn48 != last_pn->pn48 + 1) + goto err; + + last_pn->pn48 = new_pn.pn48; + last_pn = &peer->tids_last_pn[tid]; + last_pn->pn48 = new_pn.pn48; + } + + spin_unlock_bh(&ar->data_lock); + + return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb, + HTT_RX_NON_PN_CHECK, tkip_mic); + +err: + spin_unlock_bh(&ar->data_lock); + + /* Tell the caller that it must free the skb since we have not + * consumed it + */ + return true; +} + static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt, struct htt_rx_indication *rx) { @@ -3511,6 +3736,10 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", skb->data, skb->len); atomic_inc(&htt->num_mpdus_ready); + + return ath10k_htt_rx_proc_rx_frag_ind(htt, + &resp->rx_frag_ind, + skb); break; } case HTT_T2H_MSG_TYPE_TEST: @@ -3724,6 +3953,7 @@ static const struct ath10k_htt_rx_ops htt_rx_ops_64 = { }; static const struct ath10k_htt_rx_ops htt_rx_ops_hl = { + .htt_rx_proc_rx_frag_ind = ath10k_htt_rx_proc_rx_frag_ind_hl, }; void ath10k_htt_set_rx_ops(struct ath10k_htt *htt) From a8b10da05cbe2c7466ea72378a4c046dfa47d128 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 26 Apr 2019 09:41:11 +0300 Subject: [PATCH 13/52] ath10k: enable QCA6174 hw3.2 SDIO hardware After implementing PN replay check we can enable SDIO support on QCA6174. Tested with client mode on all security modes, and fragmentation as well. AP mode does not work yet. Also tone down the warning about SDIO being not ready yet. Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. AP mode is not working yet. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 27 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 1 + drivers/net/wireless/ath/ath10k/sdio.c | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index cfd7bb29a1ec..525a7ff87bf4 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -153,6 +153,33 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .hw_filter_reset_required = true, .fw_diag_ce_download = false, }, + { + .id = QCA6174_HW_3_2_VERSION, + .dev_id = QCA6174_3_2_DEVICE_ID, + .bus = ATH10K_BUS_SDIO, + .name = "qca6174 hw3.2 sdio", + .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, + .uart_pin = 19, + .otp_exe_param = 0, + .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, + .cal_data_len = 0, + .fw = { + .dir = QCA6174_HW_3_0_FW_DIR, + .board = QCA6174_HW_3_0_BOARD_DATA_FILE, + .board_size = QCA6174_BOARD_DATA_SZ, + .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, + }, + .hw_ops = &qca6174_ops, + .hw_clk = qca6174_clk, + .target_cpu_freq = 176000000, + .decap_align_bytes = 4, + .n_cipher_suites = 8, + .num_peers = 10, + .ast_skid_limit = 0x10, + .num_wds_entries = 0x20, + .uart_pin_workaround = true, + }, { .id = QCA6174_HW_2_1_VERSION, .dev_id = QCA6164_2_1_DEVICE_ID, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index a92fce1bb6f0..248cfb4e53c3 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -24,6 +24,7 @@ enum ath10k_bus { #define QCA988X_2_0_DEVICE_ID (0x003c) #define QCA6164_2_1_DEVICE_ID (0x0041) #define QCA6174_2_1_DEVICE_ID (0x003e) +#define QCA6174_3_2_DEVICE_ID (0x0042) #define QCA99X0_2_0_DEVICE_ID (0x0040) #define QCA9888_2_0_DEVICE_ID (0x0056) #define QCA9984_1_0_DEVICE_ID (0x0046) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 30102090e282..a2c1bad67b5b 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2059,7 +2059,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, } /* TODO: remove this once SDIO support is fully implemented */ - ath10k_warn(ar, "WARNING: ath10k SDIO support is incomplete, don't expect anything to work!\n"); + ath10k_warn(ar, "WARNING: ath10k SDIO support is work-in-progress, problems may arise!\n"); return 0; From 93ee3d108fc77e19efeac3ec5aa7d5886711bfef Mon Sep 17 00:00:00 2001 From: Pradeep kumar Chitrapu Date: Tue, 23 Apr 2019 16:43:28 +0300 Subject: [PATCH 14/52] ath10k: fix incorrect multicast/broadcast rate setting Invalid rate code is sent to firmware when multicast rate value of 0 is sent to driver indicating disabled case, causing broken mesh path. so fix that. Tested on QCA9984 with firmware 10.4-3.6.1-00827 Sven tested on IPQ4019 with 10.4-3.5.3-00057 and QCA9888 with 10.4-3.5.3-00053 (ath10k-firmware) and 10.4-3.6-00140 (linux-firmware 2018-12-16-211de167). Fixes: cd93b83ad92 ("ath10k: support for multicast rate control") Co-developed-by: Zhi Chen Signed-off-by: Zhi Chen Signed-off-by: Pradeep Kumar Chitrapu Tested-by: Sven Eckelmann Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 41e89db244d2..80760a20a049 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5588,8 +5588,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct cfg80211_chan_def def; u32 vdev_param, pdev_param, slottime, preamble; u16 bitrate, hw_value; - u8 rate, basic_rate_idx; - int rateidx, ret = 0, hw_rate_code; + u8 rate, basic_rate_idx, rateidx; + int ret = 0, hw_rate_code, mcast_rate; enum nl80211_band band; const struct ieee80211_supported_band *sband; @@ -5776,7 +5776,11 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_MCAST_RATE && !WARN_ON(ath10k_mac_vif_chan(arvif->vif, &def))) { band = def.chan->band; - rateidx = vif->bss_conf.mcast_rate[band] - 1; + mcast_rate = vif->bss_conf.mcast_rate[band]; + if (mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(vif->bss_conf.basic_rates) - 1; if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) rateidx += ATH10K_MAC_FIRST_OFDM_RATE_IDX; From df75786b9233be9e4ce9608df2c1cdc8b20e13df Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 3 Apr 2019 10:48:35 -0500 Subject: [PATCH 15/52] ath6kl: debug: Use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, change the following form: sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) to : struct_size(tbl, info, num_entries) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 4e94b22eaada..54337d60f288 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -1132,8 +1132,7 @@ int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, tbl = (const struct wmi_target_roam_tbl *) buf; num_entries = le16_to_cpu(tbl->num_entries); - if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) > - len) + if (struct_size(tbl, info, num_entries) > len) return -EINVAL; if (ar->debug.roam_tbl == NULL || From 83d9562b6478d830c310a21cf99091de44c8f6e2 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 3 Apr 2019 13:49:49 -0500 Subject: [PATCH 16/52] ath6kl: wmi: use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, replace code of the following form: sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) with: struct_size(ev, neighbor, ev->num_neighbors) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 68854c45d0a4..39925a8f5e85 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1295,8 +1295,7 @@ static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap, if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_neighbor_report_event *) datap; - if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) - > len) { + if (struct_size(ev, neighbor, ev->num_neighbors) > len) { ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event (num=%d len=%d)\n", ev->num_neighbors, len); From 5d6751eaff672ea77642e74e92e6c0ac7f9709ab Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Apr 2019 11:56:51 +0300 Subject: [PATCH 17/52] ath6kl: add some bounds checking The "ev->traffic_class" and "reply->ac" variables come from the network and they're used as an offset into the wmi->stream_exist_for_ac[] array. Those variables are u8 so they can be 0-255 but the stream_exist_for_ac[] array only has WMM_NUM_AC (4) elements. We need to add a couple bounds checks to prevent array overflows. I also modified one existing check from "if (traffic_class > 3) {" to "if (traffic_class >= WMM_NUM_AC) {" just to make them all consistent. Fixes: bdcd81707973 (" Add ath6kl cleaned up driver") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 39925a8f5e85..2382c6c46851 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1176,6 +1176,10 @@ static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap, return -EINVAL; ev = (struct wmi_pstream_timeout_event *) datap; + if (ev->traffic_class >= WMM_NUM_AC) { + ath6kl_err("invalid traffic class: %d\n", ev->traffic_class); + return -EINVAL; + } /* * When the pstream (fat pipe == AC) timesout, it means there were @@ -1516,6 +1520,10 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; reply = (struct wmi_cac_event *) datap; + if (reply->ac >= WMM_NUM_AC) { + ath6kl_err("invalid AC: %d\n", reply->ac); + return -EINVAL; + } if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) && (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) { @@ -2632,7 +2640,7 @@ int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, u16 active_tsids = 0; int ret; - if (traffic_class > 3) { + if (traffic_class >= WMM_NUM_AC) { ath6kl_err("invalid traffic class: %d\n", traffic_class); return -EINVAL; } From e643da21e19a62f928fb30f080470ea2e335b7f0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 4 Apr 2019 14:47:23 +0100 Subject: [PATCH 18/52] ath6kl: remove redundant check of status != 0 The check on status not being zero is redundant as previous code paths that set status to an error value break out of the while loop and hence status is never non-zero at the check. Remove this redundant code. Addresses-Coverity: ("Logically dead code") Signed-off-by: Colin Ian King Reviewed-by: Mukesh Ojha Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_pipe.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index 434b66829646..c68848819a52 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -898,9 +898,6 @@ static int htc_process_trailer(struct htc_target *target, u8 *buffer, break; } - if (status != 0) - break; - /* advance buffer past this record for next time around */ buffer += record->len; len -= record->len; From 389b72e58259336c2d56d58b660b79cf4b9e0dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 7 Mar 2019 19:39:44 +0100 Subject: [PATCH 19/52] ath9k: Don't trust TX status TID number when reporting airtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As already noted a comment in ath_tx_complete_aggr(), the hardware will occasionally send a TX status with the wrong tid number. If we trust the value, airtime usage will be reported to the wrong AC, which can cause the deficit on that AC to become very low, blocking subsequent attempts to transmit. To fix this, account airtime usage to the TID number from the original skb, instead of the one in the hardware TX status report. Reported-by: Miguel Catalan Cid Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index b17e1ca40995..3be0aeedb9b5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -668,7 +668,8 @@ static bool bf_is_ampdu_not_probing(struct ath_buf *bf) static void ath_tx_count_airtime(struct ath_softc *sc, struct ieee80211_sta *sta, struct ath_buf *bf, - struct ath_tx_status *ts) + struct ath_tx_status *ts, + u8 tid) { u32 airtime = 0; int i; @@ -679,7 +680,7 @@ static void ath_tx_count_airtime(struct ath_softc *sc, airtime += rate_dur * bf->rates[i].count; } - ieee80211_sta_register_airtime(sta, ts->tid, airtime, 0); + ieee80211_sta_register_airtime(sta, tid, airtime, 0); } static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, @@ -709,7 +710,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, if (sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; tid = ath_get_skb_tid(sc, an, bf->bf_mpdu); - ath_tx_count_airtime(sc, sta, bf, ts); + ath_tx_count_airtime(sc, sta, bf, ts, tid->tidno); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->clear_ps_filter = true; } From 2f90c7e5d09437a4d8d5546feaae9f1cf48cfbe1 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Mon, 18 Mar 2019 20:05:57 +0100 Subject: [PATCH 20/52] ath9k: Check for errors when reading SREV register Right now, if an error is encountered during the SREV register read (i.e. an EIO in ath9k_regread()), that error code gets passed all the way to __ath9k_hw_init(), where it is visible during the "Chip rev not supported" message. ath9k_htc 1-1.4:1.0: ath9k_htc: HTC initialized with 33 credits ath: phy2: Mac Chip Rev 0x0f.3 is not supported by this driver ath: phy2: Unable to initialize hardware; initialization status: -95 ath: phy2: Unable to initialize hardware; initialization status: -95 ath9k_htc: Failed to initialize the device Check for -EIO explicitly in ath9k_hw_read_revisions() and return a boolean based on the success of the operation. Check for that in __ath9k_hw_init() and abort with a more debugging-friendly message if reading the revisions wasn't successful. ath9k_htc 1-1.4:1.0: ath9k_htc: HTC initialized with 33 credits ath: phy2: Failed to read SREV register ath: phy2: Could not read hardware revision ath: phy2: Unable to initialize hardware; initialization status: -95 ath: phy2: Unable to initialize hardware; initialization status: -95 ath9k_htc: Failed to initialize the device This helps when debugging by directly showing the first point of failure and it could prevent possible errors if a 0x0f.3 revision is ever supported. Signed-off-by: Tim Schumacher Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hw.c | 32 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8581d917635a..b6773d613f0c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -252,8 +252,9 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah, /* Chip Revisions */ /******************/ -static void ath9k_hw_read_revisions(struct ath_hw *ah) +static bool ath9k_hw_read_revisions(struct ath_hw *ah) { + u32 srev; u32 val; if (ah->get_mac_revision) @@ -269,25 +270,33 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) val = REG_READ(ah, AR_SREV); ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); } - return; + return true; case AR9300_DEVID_AR9340: ah->hw_version.macVersion = AR_SREV_VERSION_9340; - return; + return true; case AR9300_DEVID_QCA955X: ah->hw_version.macVersion = AR_SREV_VERSION_9550; - return; + return true; case AR9300_DEVID_AR953X: ah->hw_version.macVersion = AR_SREV_VERSION_9531; - return; + return true; case AR9300_DEVID_QCA956X: ah->hw_version.macVersion = AR_SREV_VERSION_9561; - return; + return true; } - val = REG_READ(ah, AR_SREV) & AR_SREV_ID; + srev = REG_READ(ah, AR_SREV); + + if (srev == -EIO) { + ath_err(ath9k_hw_common(ah), + "Failed to read SREV register"); + return false; + } + + val = srev & AR_SREV_ID; if (val == 0xFF) { - val = REG_READ(ah, AR_SREV); + val = srev; ah->hw_version.macVersion = (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); @@ -306,6 +315,8 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) ah->is_pciexpress = true; } + + return true; } /************************************/ @@ -559,7 +570,10 @@ static int __ath9k_hw_init(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); int r = 0; - ath9k_hw_read_revisions(ah); + if (!ath9k_hw_read_revisions(ah)) { + ath_err(common, "Could not read hardware revisions"); + return -EOPNOTSUPP; + } switch (ah->hw_version.macVersion) { case AR_SREV_VERSION_5416_PCI: From b037b107565f329e737ec9ffdb121477a07245b6 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 20 Mar 2019 11:37:23 +0100 Subject: [PATCH 21/52] ath9k: Differentiate between max combined and per chain power The ath9k driver uses as maximum allowed txpower the constant MAX_RATE_POWER. It is used to set a maximum txpower limit for the PHY (which is combined txpower) and also the maximum txpower for per chain rates. Its value 63 is derived from the maximum number the registers can store for the per chain txpower. The max txpower a user can set because of this is 31 dBm (floor(63 / 2)). This also means that a device with multiple tx chains is even limited further: * 1 chain: 31 dBm per chain * 2 chains: 28 dBm per chain * 3 chains: 26 dBm per chain This combined txpower limit of 31 dBm becomes even more problematic when some extra antenna gain is set in the EEPROM. A high power device is then no longer able to reach its potential limits. Instead the code dealing with the combined txpower must use a higher limit than 63 and only the code dealing with the per chain txpower have to use the limit of 63. Since the antenna gain can be quite large and 8 bit variables are often used in ath9k for txpower, a large, divisible by two number like 254 is a good choice for this new limit. Signed-off-by: Sven Eckelmann Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 1 + drivers/net/wireless/ath/ath9k/hw.c | 8 ++++---- drivers/net/wireless/ath/ath9k/hw.h | 1 + drivers/net/wireless/ath/ath9k/init.c | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index 6fbd5559c0c0..c22d457dbc54 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -428,7 +428,7 @@ u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, else power_limit = 0; - return power_limit; + return min_t(u16, power_limit, MAX_RATE_POWER); } void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index b8c0a08066a0..e8c2cc03be0c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -424,6 +424,7 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, ath9k_hw_get_channel_centers(ah, chan, ¢ers); scaledPower = powerLimit - antenna_reduction; + scaledPower = min_t(u16, scaledPower, MAX_RATE_POWER); numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; pCtlMode = ctlModesFor11g; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index b6773d613f0c..052deffb4c9d 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -457,7 +457,7 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); regulatory->country_code = CTRY_DEFAULT; - regulatory->power_limit = MAX_RATE_POWER; + regulatory->power_limit = MAX_COMBINED_POWER; ah->hw_version.magic = AR5416_MAGIC; ah->hw_version.subvendorid = 0; @@ -2966,7 +2966,7 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, ctl = ath9k_regd_get_ctl(reg, chan); channel = chan->chan; - chan_pwr = min_t(int, channel->max_power * 2, MAX_RATE_POWER); + chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER); new_pwr = min_t(int, chan_pwr, reg->power_limit); ah->eep_ops->set_txpower(ah, chan, ctl, @@ -2979,9 +2979,9 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) struct ath9k_channel *chan = ah->curchan; struct ieee80211_channel *channel = chan->chan; - reg->power_limit = min_t(u32, limit, MAX_RATE_POWER); + reg->power_limit = min_t(u32, limit, MAX_COMBINED_POWER); if (test) - channel->max_power = MAX_RATE_POWER / 2; + channel->max_power = MAX_COMBINED_POWER / 2; ath9k_hw_apply_txpower(ah, chan, test); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 68956cdc8c9a..2e4489700a85 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -173,6 +173,7 @@ #define ATH9K_NUM_QUEUES 10 #define MAX_RATE_POWER 63 +#define MAX_COMBINED_POWER 254 /* 128 dBm, chosen to fit in u8 */ #define AH_WAIT_TIMEOUT 100000 /* (us) */ #define AH_TSF_WRITE_TIMEOUT 100 /* (us) */ #define AH_TIME_QUANTUM 10 diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 98141b699c88..043801f773b5 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -805,7 +805,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band) ah->curchan = &ah->channels[chan->hw_value]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); ath9k_cmn_get_channel(sc->hw, ah, &chandef); - ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true); + ath9k_hw_set_txpowerlimit(ah, MAX_COMBINED_POWER, true); } } From 05039f01e63053add589255e4a9aa1d4c3e1f0c0 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 17 Apr 2019 10:57:45 +0800 Subject: [PATCH 22/52] ath9k: Remove some set but not used variables Fixes gcc '-Wunused-but-set-variable' warning: drivers/net/wireless/ath/ath9k/xmit.c: In function 'ath_tx_count_frames': drivers/net/wireless/ath/ath9k/xmit.c:413:25: warning: variable 'fi' set but not used [-Wunused-but-set-variable] drivers/net/wireless/ath/ath9k/xmit.c: In function 'ath_tx_complete_aggr': drivers/net/wireless/ath/ath9k/xmit.c:449:24: warning: variable 'hdr' set but not used [-Wunused-but-set-variable] drivers/net/wireless/ath/ath9k/xmit.c: In function 'ath_tx_start': drivers/net/wireless/ath/ath9k/xmit.c:2274:18: warning: variable 'avp' set but not used [-Wunused-but-set-variable] drivers/net/wireless/ath/ath9k/xmit.c:2269:24: warning: variable 'hdr' set but not used [-Wunused-but-set-variable] These variables are not used any more and can be removed. Signed-off-by: YueHaibing Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3be0aeedb9b5..31e7b108279c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -410,7 +410,6 @@ static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int txok, int *nframes, int *nbad) { - struct ath_frame_info *fi; u16 seq_st = 0; u32 ba[WME_BA_BMP_SIZE >> 5]; int ba_index; @@ -426,7 +425,6 @@ static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf, } while (bf) { - fi = get_frame_info(bf->bf_mpdu); ba_index = ATH_BA_INDEX(seq_st, bf->bf_state.seqno); (*nframes)++; @@ -446,7 +444,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, { struct ath_node *an = NULL; struct sk_buff *skb; - struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info; struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; struct list_head bf_head; @@ -463,8 +460,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, int bar_index = -1; skb = bf->bf_mpdu; - hdr = (struct ieee80211_hdr *)skb->data; - tx_info = IEEE80211_SKB_CB(skb); memcpy(rates, bf->rates, sizeof(rates)); @@ -2270,12 +2265,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl) { - struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; struct ath_frame_info *fi = get_frame_info(skb); - struct ath_vif *avp = NULL; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; @@ -2284,16 +2277,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, bool ps_resp; int q, ret; - if (vif) - avp = (void *)vif->drv_priv; - ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE); ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; - hdr = (struct ieee80211_hdr *) skb->data; /* * At this point, the vif, hw_key and sta pointers in the tx control * info are no longer valid (overwritten by the ath_frame_info data. From b50fd29c526806d6550f4ee773d30e2fe2144a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= Date: Thu, 14 Mar 2019 15:07:49 +0100 Subject: [PATCH 23/52] ath: drop duplicated define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove duplicate NO_CTL that is just 2 lines below. Signed-off-by: Tomislav Požega Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/regd.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h index 75ddaefdd049..8d5a16b558e6 100644 --- a/drivers/net/wireless/ath/regd.h +++ b/drivers/net/wireless/ath/regd.h @@ -28,7 +28,6 @@ enum ctl_group { CTL_ETSI = 0x30, }; -#define NO_CTL 0xff #define SD_NO_CTL 0xE0 #define NO_CTL 0xff #define CTL_11A 0 From d3c2be9022d687cc97c0798c4593382f1a22c068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= Date: Thu, 14 Mar 2019 15:07:50 +0100 Subject: [PATCH 24/52] ath9k: drop redundant code in ar9003_hw_set_channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AR9330, AR9485, AR9531, AR9550, AR9561 and AR9565 all use same channel set register configuration which allows for small code size reduction. Signed-off-by: Tomislav Požega Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 24 +++------------------ 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 98c5f524a360..daf30f9946b4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -157,7 +157,9 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) freq = centers.synth_center; if (freq < 4800) { /* 2 GHz, fractional mode */ - if (AR_SREV_9330(ah)) { + if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || + AR_SREV_9531(ah) || AR_SREV_9550(ah) || + AR_SREV_9561(ah) || AR_SREV_9565(ah)) { if (ah->is_clk_25mhz) div = 75; else @@ -166,16 +168,6 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) channelSel = (freq * 4) / div; chan_frac = (((freq * 4) % div) * 0x20000) / div; channelSel = (channelSel << 17) | chan_frac; - } else if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) { - /* - * freq_ref = 40 / (refdiva >> amoderefsel); - * where refdiva=1 and amoderefsel=0 - * ndiv = ((chan_mhz * 4) / 3) / freq_ref; - * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000 - */ - channelSel = (freq * 4) / 120; - chan_frac = (((freq * 4) % 120) * 0x20000) / 120; - channelSel = (channelSel << 17) | chan_frac; } else if (AR_SREV_9340(ah)) { if (ah->is_clk_25mhz) { channelSel = (freq * 2) / 75; @@ -184,16 +176,6 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) } else { channelSel = CHANSEL_2G(freq) >> 1; } - } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || - AR_SREV_9561(ah)) { - if (ah->is_clk_25mhz) - div = 75; - else - div = 120; - - channelSel = (freq * 4) / div; - chan_frac = (((freq * 4) % div) * 0x20000) / div; - channelSel = (channelSel << 17) | chan_frac; } else { channelSel = CHANSEL_2G(freq); } From d8792393a783158cbb2c39939cb897dc5e5299b6 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 6 Mar 2019 23:06:11 +0530 Subject: [PATCH 25/52] ath: DFS JP domain W56 fixed pulse type 3 RADAR detection Increase pulse width range from 1-2usec to 0-4usec. During data traffic HW occasionally fails detecting radar pulses, so that SW cannot get enough radar reports to achieve the success rate. Tested ath10k hw and fw: * QCA9888(10.4-3.5.1-00052) * QCA4019(10.4-3.2.1.1-00017) * QCA9984(10.4-3.6-00104) * QCA988X(10.2.4-1.0-00041) Tested ath9k hw: AR9300 Tested-by: Tamizh chelvam Signed-off-by: Tamizh chelvam Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/dfs_pattern_detector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index d52b31b45df7..a274eb0d1968 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -111,7 +111,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = { JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18, 29, false), JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18, 29, false), JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18, 50, false), - JP_PATTERN(3, 1, 2, 4000, 4000, 1, 18, 50, false), + JP_PATTERN(3, 0, 4, 4000, 4000, 1, 18, 50, false), JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), From bfabdd6997323adbedccb13a3fed1967fb8cf8f5 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 15 Apr 2019 09:56:46 -0500 Subject: [PATCH 26/52] wil6210: fix potential out-of-bounds read Notice that *rc* can evaluate to up to 5, include/linux/netdevice.h: enum gro_result { GRO_MERGED, GRO_MERGED_FREE, GRO_HELD, GRO_NORMAL, GRO_DROP, GRO_CONSUMED, }; typedef enum gro_result gro_result_t; In case *rc* evaluates to 5, we end up having an out-of-bounds read at drivers/net/wireless/ath/wil6210/txrx.c:821: wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", len, gro_res_str[rc]); Fix this by adding element "GRO_CONSUMED" to array gro_res_str. Addresses-Coverity-ID: 1444666 ("Out-of-bounds read") Fixes: 194b482b5055 ("wil6210: Debug print GRO Rx result") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 4ccfd1404458..d74837cce67f 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -750,6 +750,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) [GRO_HELD] = "GRO_HELD", [GRO_NORMAL] = "GRO_NORMAL", [GRO_DROP] = "GRO_DROP", + [GRO_CONSUMED] = "GRO_CONSUMED", }; wil->txrx_ops.get_netif_rx_params(skb, &cid, &security); From 62acdcfa8b7ae921b70afff888e994df117022a6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Mar 2019 13:43:44 +0100 Subject: [PATCH 27/52] wireless: carl9170: fix clang build warning clang fails to eliminate some dead code with always-taken branches when CONFIG_PROFILE_ANNOTATED_BRANCHES is set, leading to a false-positive warning: drivers/net/wireless/ath/carl9170/mac.c:522:3: error: variable 'power' is used uninitialized whenever 'if' condition is false [-Werror,-Wsometimes-uninitialized] BUG_ON(1); ^~~~~~~~~ Change both instances of BUG_ON(1) in carl9170 to the simpler BUG() to avoid the warning. Signed-off-by: Arnd Bergmann Acked-by: Christian Lamparter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/carl9170/mac.c | 2 +- drivers/net/wireless/ath/carl9170/rx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index 7d4a72dc98db..b2eeb9fd68d2 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -519,7 +519,7 @@ int carl9170_set_mac_tpc(struct ar9170 *ar, struct ieee80211_channel *channel) power = ar->power_5G_leg[0] & 0x3f; break; default: - BUG_ON(1); + BUG(); } power = min_t(unsigned int, power, ar->hw->conf.power_level * 2); diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 8e154f6364a3..23ab8a80c18c 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -795,7 +795,7 @@ static void carl9170_rx_untie_data(struct ar9170 *ar, u8 *buf, int len) break; default: - BUG_ON(1); + BUG(); break; } From e10b0eddd5235aa5aef4e40b970e34e735611a80 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 26 Apr 2019 18:43:29 +0300 Subject: [PATCH 28/52] wil6210: fix spurious interrupts in 3-msi Interrupt is set in ICM (ICR & ~IMV) rising trigger. As the driver masks the IRQ after clearing it, there can be a race where an additional spurious interrupt is triggered when the driver unmask the IRQ. This can happen in case HW triggers an interrupt after the clear and before the mask. To prevent the second spurious interrupt the driver needs to mask the IRQ before reading and clearing it. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/interrupt.c | 65 ++++++++++++-------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 3f5bd177d55f..d161dc930313 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -296,21 +296,24 @@ void wil_configure_interrupt_moderation(struct wil6210_priv *wil) static irqreturn_t wil6210_irq_rx(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - u32 isr = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_RX_ICR) + - offsetof(struct RGF_ICR, ICR)); + u32 isr; bool need_unmask = true; + wil6210_mask_irq_rx(wil); + + isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_rx(isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (unlikely(!isr)) { wil_err_ratelimited(wil, "spurious IRQ: RX\n"); + wil6210_unmask_irq_rx(wil); return IRQ_NONE; } - wil6210_mask_irq_rx(wil); - /* RX_DONE and RX_HTRSH interrupts are the same if interrupt * moderation is not used. Interrupt moderation may cause RX * buffer overflow while RX_DONE is delayed. The required @@ -355,21 +358,24 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - u32 isr = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_INT_GEN_RX_ICR) + - offsetof(struct RGF_ICR, ICR)); + u32 isr; bool need_unmask = true; + wil6210_mask_irq_rx_edma(wil); + + isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_rx(isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (unlikely(!isr)) { wil_err(wil, "spurious IRQ: RX\n"); + wil6210_unmask_irq_rx_edma(wil); return IRQ_NONE; } - wil6210_mask_irq_rx_edma(wil); - if (likely(isr & BIT_RX_STATUS_IRQ)) { wil_dbg_irq(wil, "RX status ring\n"); isr &= ~BIT_RX_STATUS_IRQ; @@ -403,21 +409,24 @@ static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie) static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - u32 isr = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_INT_GEN_TX_ICR) + - offsetof(struct RGF_ICR, ICR)); + u32 isr; bool need_unmask = true; + wil6210_mask_irq_tx_edma(wil); + + isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_tx(isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (unlikely(!isr)) { wil_err(wil, "spurious IRQ: TX\n"); + wil6210_unmask_irq_tx_edma(wil); return IRQ_NONE; } - wil6210_mask_irq_tx_edma(wil); - if (likely(isr & BIT_TX_STATUS_IRQ)) { wil_dbg_irq(wil, "TX status ring\n"); isr &= ~BIT_TX_STATUS_IRQ; @@ -446,21 +455,24 @@ static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie) static irqreturn_t wil6210_irq_tx(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - u32 isr = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_TX_ICR) + - offsetof(struct RGF_ICR, ICR)); + u32 isr; bool need_unmask = true; + wil6210_mask_irq_tx(wil); + + isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_tx(isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (unlikely(!isr)) { wil_err_ratelimited(wil, "spurious IRQ: TX\n"); + wil6210_unmask_irq_tx(wil); return IRQ_NONE; } - wil6210_mask_irq_tx(wil); - if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) { wil_dbg_irq(wil, "TX done\n"); isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; @@ -532,20 +544,23 @@ static bool wil_validate_mbox_regs(struct wil6210_priv *wil) static irqreturn_t wil6210_irq_misc(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - u32 isr = wil_ioread32_and_clear(wil->csr + - HOSTADDR(RGF_DMA_EP_MISC_ICR) + - offsetof(struct RGF_ICR, ICR)); + u32 isr; + + wil6210_mask_irq_misc(wil, false); + + isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); trace_wil6210_irq_misc(isr); wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); if (!isr) { wil_err(wil, "spurious IRQ: MISC\n"); + wil6210_unmask_irq_misc(wil, false); return IRQ_NONE; } - wil6210_mask_irq_misc(wil, false); - if (isr & ISR_MISC_FW_ERROR) { u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr); u32 ucode_assert_code = From 9c6465979276c58d5972a959dd35b4d9aff52749 Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Fri, 26 Apr 2019 18:43:30 +0300 Subject: [PATCH 29/52] wil6210: fix _desc access in __wil_tx_vring_tso _desc is defined in __wil_tx_vring_tso() and may not be set in case len is 0, verify _desc is set. Signed-off-by: Alexei Avshalom Lazar Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index d74837cce67f..c5e16b2145f8 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1761,6 +1761,9 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, } } + if (!_desc) + goto mem_error; + /* first descriptor may also be the last. * in this case d pointer is invalid */ From 0131d185133891b7370b606411427efc260d68a0 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Fri, 26 Apr 2019 18:43:31 +0300 Subject: [PATCH 30/52] wil6210: add printout of platform capabilities Add logging of the platform capabilities after retrieving it from the platform driver. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 3b82d6cfc218..4e2d9221b8e6 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -142,6 +142,8 @@ int wil_set_capabilities(struct wil6210_priv *wil) min(sizeof(wil->platform_capa), sizeof(platform_capa))); } + wil_info(wil, "platform_capa 0x%lx\n", *wil->platform_capa); + /* extract FW capabilities from file without loading the FW */ wil_request_firmware(wil, wil->wil_fw_name, false); wil_refresh_fw_capabilities(wil); From a7feb56f204fd8bcc9a8d7342097f7b3cf87b50e Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 26 Apr 2019 18:43:32 +0300 Subject: [PATCH 31/52] wil6210: add support for multiple sections in brd file Current board file loading procedure assumes that the board file includes only one section. New board files can include multiple sections. Add the ability to read multiple addresses and max size from FW file and load multiple sections from the board file into those addresses. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/fw.h | 11 +- drivers/net/wireless/ath/wil6210/fw_inc.c | 148 +++++++++++++++------ drivers/net/wireless/ath/wil6210/main.c | 3 +- drivers/net/wireless/ath/wil6210/wil6210.h | 9 +- 4 files changed, 126 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h index 3e7a28045cab..fa3164765b20 100644 --- a/drivers/net/wireless/ath/wil6210/fw.h +++ b/drivers/net/wireless/ath/wil6210/fw.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014,2016 Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -109,12 +109,17 @@ struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */ /* brd file info encoded inside a comment record */ #define WIL_BRD_FILE_MAGIC (0xabcddcbb) + +struct brd_info { + __le32 base_addr; + __le32 max_size_bytes; +} __packed; + struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */ /* identifies brd file record */ struct wil_fw_record_comment_hdr hdr; __le32 version; - __le32 base_addr; - __le32 max_size_bytes; + struct brd_info brd_info[0]; } __packed; /* perform action diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index 3ec0f2fab9b7..94ebfa338e3f 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -156,17 +156,52 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_brd_file *rec = data; + u32 max_num_ent, i, ent_size; - if (size < sizeof(*rec)) { - wil_err_fw(wil, "brd_file record too short: %zu\n", size); - return 0; + if (size <= offsetof(struct wil_fw_record_brd_file, brd_info)) { + wil_err(wil, "board record too short, size %zu\n", size); + return -EINVAL; } - wil->brd_file_addr = le32_to_cpu(rec->base_addr); - wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); + ent_size = size - offsetof(struct wil_fw_record_brd_file, brd_info); + max_num_ent = ent_size / sizeof(struct brd_info); - wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", - wil->brd_file_addr, wil->brd_file_max_size); + if (!max_num_ent) { + wil_err(wil, "brd info entries are missing\n"); + return -EINVAL; + } + + wil->brd_info = kcalloc(max_num_ent, sizeof(struct wil_brd_info), + GFP_KERNEL); + if (!wil->brd_info) + return -ENOMEM; + + for (i = 0; i < max_num_ent; i++) { + wil->brd_info[i].file_addr = + le32_to_cpu(rec->brd_info[i].base_addr); + wil->brd_info[i].file_max_size = + le32_to_cpu(rec->brd_info[i].max_size_bytes); + + if (!wil->brd_info[i].file_addr) + break; + + wil_dbg_fw(wil, + "brd info %d: file_addr 0x%x, file_max_size %d\n", + i, wil->brd_info[i].file_addr, + wil->brd_info[i].file_max_size); + } + + wil->num_of_brd_entries = i; + if (wil->num_of_brd_entries == 0) { + kfree(wil->brd_info); + wil->brd_info = NULL; + wil_dbg_fw(wil, + "no valid brd info entries, using brd file addr\n"); + + } else { + wil_dbg_fw(wil, "num of brd info entries %d\n", + wil->num_of_brd_entries); + } return 0; } @@ -634,6 +669,11 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name, } wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size); + /* re-initialize board info params */ + wil->num_of_brd_entries = 0; + kfree(wil->brd_info); + wil->brd_info = NULL; + for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) { rc1 = wil_fw_verify(wil, d, sz); if (rc1 < 0) { @@ -662,11 +702,13 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data, { int rc = 0; const struct wil_fw_record_head *hdr = data; - size_t s, hdr_sz; + size_t s, hdr_sz = 0; u16 type; + int i = 0; - /* Assuming the board file includes only one header record and one data - * record. Each record starts with wil_fw_record_head. + /* Assuming the board file includes only one file header + * and one or several data records. + * Each record starts with wil_fw_record_head. */ if (size < sizeof(*hdr)) return -EINVAL; @@ -674,40 +716,67 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data, if (s > size) return -EINVAL; - /* Skip the header record and handle the data record */ - hdr = (const void *)hdr + s; + /* Skip the header record and handle the data records */ size -= s; - if (size < sizeof(*hdr)) - return -EINVAL; - hdr_sz = le32_to_cpu(hdr->size); - if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size) - return -EINVAL; - if (sizeof(*hdr) + hdr_sz > size) - return -EINVAL; - if (hdr_sz % 4) { - wil_err_fw(wil, "unaligned record size: %zu\n", - hdr_sz); - return -EINVAL; + for (hdr = data + s;; hdr = (const void *)hdr + s, size -= s, i++) { + if (size < sizeof(*hdr)) + break; + + if (i >= wil->num_of_brd_entries) { + wil_err_fw(wil, + "Too many brd records: %d, num of expected entries %d\n", + i, wil->num_of_brd_entries); + break; + } + + hdr_sz = le32_to_cpu(hdr->size); + s = sizeof(*hdr) + hdr_sz; + if (wil->brd_info[i].file_max_size && + hdr_sz > wil->brd_info[i].file_max_size) + return -EINVAL; + if (sizeof(*hdr) + hdr_sz > size) + return -EINVAL; + if (hdr_sz % 4) { + wil_err_fw(wil, "unaligned record size: %zu\n", + hdr_sz); + return -EINVAL; + } + type = le16_to_cpu(hdr->type); + if (type != wil_fw_type_data) { + wil_err_fw(wil, + "invalid record type for board file: %d\n", + type); + return -EINVAL; + } + if (hdr_sz < sizeof(struct wil_fw_record_data)) { + wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); + return -EINVAL; + } + + wil_dbg_fw(wil, + "using info from fw file for record %d: addr[0x%08x], max size %d\n", + i, wil->brd_info[i].file_addr, + wil->brd_info[i].file_max_size); + + rc = __fw_handle_data(wil, &hdr[1], hdr_sz, + cpu_to_le32(wil->brd_info[i].file_addr)); + if (rc) + return rc; } - type = le16_to_cpu(hdr->type); - if (type != wil_fw_type_data) { - wil_err_fw(wil, "invalid record type for board file: %d\n", - type); - return -EINVAL; - } - if (hdr_sz < sizeof(struct wil_fw_record_data)) { - wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); + + if (size) { + wil_err_fw(wil, "unprocessed bytes: %zu\n", size); + if (size >= sizeof(*hdr)) { + wil_err_fw(wil, + "Stop at offset %ld record type %d [%zd bytes]\n", + (long)((const void *)hdr - data), + le16_to_cpu(hdr->type), hdr_sz); + } return -EINVAL; } - wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n", - wil->brd_file_addr); - - rc = __fw_handle_data(wil, &hdr[1], hdr_sz, - cpu_to_le32(wil->brd_file_addr)); - - return rc; + return 0; } /** @@ -738,7 +807,8 @@ int wil_request_board(struct wil6210_priv *wil, const char *name) rc = dlen; goto out; } - /* Process the data record */ + + /* Process the data records */ rc = wil_brd_process(wil, brd->data, dlen); out: diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 9b9c9ec01536..03ca8e5a776b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -838,6 +838,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); + kfree(wil->brd_info); } static void wil_shutdown_bl(struct wil6210_priv *wil) @@ -1709,7 +1710,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) rc = wil_request_firmware(wil, wil->wil_fw_name, true); if (rc) goto out; - if (wil->brd_file_addr) + if (wil->num_of_brd_entries) rc = wil_request_board(wil, board_file); else rc = wil_request_firmware(wil, board_file, true); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 8724d9975606..dc400026a3eb 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -913,6 +913,11 @@ struct wil_fw_stats_global { struct wmi_link_stats_global stats; }; +struct wil_brd_info { + u32 file_addr; + u32 file_max_size; +}; + struct wil6210_priv { struct pci_dev *pdev; u32 bar_size; @@ -927,8 +932,8 @@ struct wil6210_priv { const char *hw_name; const char *wil_fw_name; char *board_file; - u32 brd_file_addr; - u32 brd_file_max_size; + u32 num_of_brd_entries; + struct wil_brd_info *brd_info; DECLARE_BITMAP(hw_capa, hw_capa_last); DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX); DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX); From 9a874d045473f199e1a184a68be1cf4970db6cbf Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Fri, 26 Apr 2019 18:43:33 +0300 Subject: [PATCH 32/52] wil6210: enhancements for descriptor and status ring debugfs Add printing of hwhead as part of desc ring and status ring debugfs. Validate wil->rx_buff_mgmt and ring->ctx arrays before accessing them. Dump Tx descriptor content even when it indicates invalid buff_id. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/debugfs.c | 52 +++++++++++++++++----- drivers/net/wireless/ath/wil6210/wil6210.h | 5 +++ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index df2adff6c33a..42507f530a40 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -63,7 +63,9 @@ static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil, &ring->va[idx].rx.enhanced; u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); - has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + if (wil->rx_buff_mgmt.buff_arr && + wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size)) + has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; seq_printf(s, "%c", (has_skb) ? _h : _s); } else { struct wil_tx_enhanced_desc *d = @@ -71,9 +73,9 @@ static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil, &ring->va[idx].tx.enhanced; num_of_descs = (u8)d->mac.d[2]; - has_skb = ring->ctx[idx].skb; + has_skb = ring->ctx && ring->ctx[idx].skb; if (num_of_descs >= 1) - seq_printf(s, "%c", ring->ctx[idx].skb ? _h : _s); + seq_printf(s, "%c", has_skb ? _h : _s); else /* num_of_descs == 0, it's a frag in a list of descs */ seq_printf(s, "%c", has_skb ? 'h' : _s); @@ -84,7 +86,7 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, const char *name, struct wil_ring *ring, char _s, char _h) { - void __iomem *x = wmi_addr(wil, ring->hwtail); + void __iomem *x; u32 v; seq_printf(s, "RING %s = {\n", name); @@ -96,7 +98,21 @@ static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, else seq_printf(s, " swtail = %d\n", ring->swtail); seq_printf(s, " swhead = %d\n", ring->swhead); + if (wil->use_enhanced_dma_hw) { + int ring_id = ring->is_rx ? + WIL_RX_DESC_RING_ID : ring - wil->ring_tx; + /* SUBQ_CONS is a table of 32 entries, one for each Q pair. + * lower 16bits are for even ring_id and upper 16bits are for + * odd ring_id + */ + x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2)); + v = readl_relaxed(x); + + v = (ring_id % 2 ? (v >> 16) : (v & 0xffff)); + seq_printf(s, " hwhead = %u\n", v); + } seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail); + x = wmi_addr(wil, ring->hwtail); if (x) { v = readl(x); seq_printf(s, "0x%08x = %d\n", v, v); @@ -188,7 +204,7 @@ DEFINE_SHOW_ATTRIBUTE(ring); static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, struct wil_status_ring *sring) { - void __iomem *x = wmi_addr(wil, sring->hwtail); + void __iomem *x; int sring_idx = sring - wil->srings; u32 v; @@ -199,7 +215,19 @@ static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, seq_printf(s, " size = %d\n", sring->size); seq_printf(s, " elem_size = %zu\n", sring->elem_size); seq_printf(s, " swhead = %d\n", sring->swhead); + if (wil->use_enhanced_dma_hw) { + /* COMPQ_PROD is a table of 32 entries, one for each Q pair. + * lower 16bits are for even ring_id and upper 16bits are for + * odd ring_id + */ + x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2)); + v = readl_relaxed(x); + + v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff)); + seq_printf(s, " hwhead = %u\n", v); + } seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail); + x = wmi_addr(wil, sring->hwtail); if (x) { v = readl_relaxed(x); seq_printf(s, "0x%08x = %d\n", v, v); @@ -1091,19 +1119,18 @@ static int txdesc_show(struct seq_file *s, void *data) if (wil->use_enhanced_dma_hw) { if (tx) { - skb = ring->ctx[txdesc_idx].skb; - } else { + skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL; + } else if (wil->rx_buff_mgmt.buff_arr) { struct wil_rx_enhanced_desc *rx_d = (struct wil_rx_enhanced_desc *) &ring->va[txdesc_idx].rx.enhanced; u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); if (!wil_val_in_range(buff_id, 0, - wil->rx_buff_mgmt.size)) { + wil->rx_buff_mgmt.size)) seq_printf(s, "invalid buff_id %d\n", buff_id); - return 0; - } - skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + else + skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; } } else { skb = ring->ctx[txdesc_idx].skb; @@ -1136,7 +1163,7 @@ static int status_msg_show(struct seq_file *s, void *data) struct wil6210_priv *wil = s->private; int sring_idx = dbg_sring_index; struct wil_status_ring *sring; - bool tx = sring_idx == wil->tx_sring_idx ? 1 : 0; + bool tx; u32 status_msg_idx = dbg_status_msg_index; u32 *u; @@ -1146,6 +1173,7 @@ static int status_msg_show(struct seq_file *s, void *data) } sring = &wil->srings[sring_idx]; + tx = !sring->is_rx; if (!sring->va) { seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R'); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index dc400026a3eb..1f0175d8399c 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -335,6 +335,11 @@ struct RGF_ICR { #define BIT_BOOT_FROM_ROM BIT(31) /* eDMA */ +#define RGF_SCM_PTRS_SUBQ_RD_PTR (0x8b4000) +#define RGF_SCM_PTRS_COMPQ_RD_PTR (0x8b4100) +#define RGF_DMA_SCM_SUBQ_CONS (0x8b60ec) +#define RGF_DMA_SCM_COMPQ_PROD (0x8b616c) + #define RGF_INT_COUNT_ON_SPECIAL_EVT (0x8b62d8) #define RGF_INT_CTRL_INT_GEN_CFG_0 (0x8bc000) From ddf7afdde824545680dafba2c91f6b218c45de3a Mon Sep 17 00:00:00 2001 From: Ahmad Masri Date: Fri, 26 Apr 2019 18:43:34 +0300 Subject: [PATCH 33/52] wil6210: fix overwriting max_assoc_sta module param Save the parameter of max_assoc_sta per device. On fw_ready event calculate the max_assoc_sta and save it per device, do not overwrite the max_assoc_sta module param. Signed-off-by: Ahmad Masri Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++-- drivers/net/wireless/ath/wil6210/debugfs.c | 18 +++++++-------- drivers/net/wireless/ath/wil6210/main.c | 9 ++++---- drivers/net/wireless/ath/wil6210/rx_reorder.c | 2 +- drivers/net/wireless/ath/wil6210/txrx.c | 22 +++++++++---------- drivers/net/wireless/ath/wil6210/txrx_edma.c | 7 +++--- drivers/net/wireless/ath/wil6210/wil6210.h | 19 ++++++++-------- drivers/net/wireless/ath/wil6210/wmi.c | 14 ++++++------ 8 files changed, 49 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 218296e319b9..e9780fcde62a 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -395,7 +395,7 @@ static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx) { int i; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].status == wil_sta_unused) continue; if (wil->sta[i].mid != mid) @@ -3020,7 +3020,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX, sector_type, WIL_CID_ALL); if (rc == -EINVAL) { - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].mid != vif->mid) continue; rc = wil_rf_sector_wmi_set_selected( diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 42507f530a40..7a2c3fdeca8c 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -178,7 +178,7 @@ static int ring_show(struct seq_file *s, void *data) snprintf(name, sizeof(name), "tx_%2d", i); - if (cid < max_assoc_sta) + if (cid < wil->max_assoc_sta) seq_printf(s, "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", wil->sta[cid].addr, cid, tid, @@ -839,7 +839,7 @@ static ssize_t wil_write_back(struct file *file, const char __user *buf, "BACK: del_rx require at least 2 params\n"); return -EINVAL; } - if (p1 < 0 || p1 >= max_assoc_sta) { + if (p1 < 0 || p1 >= wil->max_assoc_sta) { wil_err(wil, "BACK: invalid CID %d\n", p1); return -EINVAL; } @@ -1290,7 +1290,7 @@ static int bf_show(struct seq_file *s, void *data) memset(&reply, 0, sizeof(reply)); - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { u32 status; cmd.cid = i; @@ -1387,7 +1387,7 @@ static int link_show(struct seq_file *s, void *data) if (!sinfo) return -ENOMEM; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; struct wil6210_vif *vif; @@ -1589,7 +1589,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) struct wil6210_priv *wil = s->private; int i, tid, mcs; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; u8 aid = 0; @@ -1698,7 +1698,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) struct wil6210_priv *wil = s->private; int i, bin; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; u8 aid = 0; @@ -1787,7 +1787,7 @@ static ssize_t wil_tx_latency_write(struct file *file, const char __user *buf, size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS; wil->tx_latency_res = val; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { struct wil_sta_info *sta = &wil->sta[i]; kfree(sta->tx_latency_bins); @@ -1872,7 +1872,7 @@ static void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif, } seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf); - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].status == wil_sta_unused) continue; if (wil->sta[i].mid != vif->mid) @@ -2488,7 +2488,7 @@ void wil6210_debugfs_remove(struct wil6210_priv *wil) wil->debug = NULL; kfree(wil->dbg_data.data_arr); - for (i = 0; i < max_assoc_sta; i++) + for (i = 0; i < wil->max_assoc_sta; i++) kfree(wil->sta[i].tx_latency_bins); /* free pmc memory without sending command to fw, as it will diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 03ca8e5a776b..efdb6e137c16 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -241,7 +241,7 @@ static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid) { int i; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].mid == mid && wil->sta[i].status == wil_sta_connected) return true; @@ -344,7 +344,7 @@ static void _wil6210_disconnect_complete(struct wil6210_vif *vif, wil_disconnect_cid_complete(vif, cid, reason_code); } else { /* all */ wil_dbg_misc(wil, "Disconnect complete all\n"); - for (cid = 0; cid < max_assoc_sta; cid++) + for (cid = 0; cid < wil->max_assoc_sta; cid++) wil_disconnect_cid_complete(vif, cid, reason_code); } @@ -456,7 +456,7 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, wil_disconnect_cid(vif, cid, reason_code); } else { /* all */ wil_dbg_misc(wil, "Disconnect all\n"); - for (cid = 0; cid < max_assoc_sta; cid++) + for (cid = 0; cid < wil->max_assoc_sta; cid++) wil_disconnect_cid(vif, cid, reason_code); } @@ -753,6 +753,7 @@ int wil_priv_init(struct wil6210_priv *wil) wil->reply_mid = U8_MAX; wil->max_vifs = 1; + wil->max_assoc_sta = max_assoc_sta; /* edma configuration can be updated via debugfs before allocation */ wil->num_rx_status_rings = WIL_DEFAULT_NUM_RX_STATUS_RINGS; @@ -1922,7 +1923,7 @@ int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac) int i; int rc = -ENOENT; - for (i = 0; i < max_assoc_sta; i++) { + for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].mid == mid && wil->sta[i].status != wil_sta_unused && ether_addr_equal(wil->sta[i].addr, mac)) { diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 32b14fc33a59..1c796640d6aa 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -336,7 +336,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) might_sleep(); /* sanity checks */ - if (cid >= max_assoc_sta) { + if (cid >= wil->max_assoc_sta) { wil_err(wil, "BACK: invalid CID %d\n", cid); rc = -EINVAL; goto out; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index c5e16b2145f8..8790e5effa28 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -411,7 +411,7 @@ static int wil_rx_get_cid_by_skb(struct wil6210_priv *wil, struct sk_buff *skb) ta = hdr->addr2; } - if (max_assoc_sta <= WIL6210_RX_DESC_MAX_CID) + if (wil->max_assoc_sta <= WIL6210_RX_DESC_MAX_CID) return cid; /* assuming no concurrency between AP interfaces and STA interfaces. @@ -426,14 +426,14 @@ static int wil_rx_get_cid_by_skb(struct wil6210_priv *wil, struct sk_buff *skb) * to find the real cid, compare transmitter address with the stored * stations mac address in the driver sta array */ - for (i = cid; i < max_assoc_sta; i += WIL6210_RX_DESC_MAX_CID) { + for (i = cid; i < wil->max_assoc_sta; i += WIL6210_RX_DESC_MAX_CID) { if (wil->sta[i].status != wil_sta_unused && ether_addr_equal(wil->sta[i].addr, ta)) { cid = i; break; } } - if (i >= max_assoc_sta) { + if (i >= wil->max_assoc_sta) { wil_err_ratelimited(wil, "Could not find cid for frame with transmit addr = %pM, iftype = %d, frametype = %d, len = %d\n", ta, vif->wdev.iftype, ftype, skb->len); cid = -ENOENT; @@ -1064,7 +1064,7 @@ static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, txdata->enabled = 0; spin_unlock_bh(&txdata->lock); wil_vring_free(wil, vring); - wil->ring2cid_tid[id][0] = max_assoc_sta; + wil->ring2cid_tid[id][0] = wil->max_assoc_sta; wil->ring2cid_tid[id][1] = 0; out: @@ -1149,7 +1149,7 @@ static int wil_tx_vring_modify(struct wil6210_vif *vif, int ring_id, int cid, txdata->dot1x_open = false; txdata->enabled = 0; spin_unlock_bh(&txdata->lock); - wil->ring2cid_tid[ring_id][0] = max_assoc_sta; + wil->ring2cid_tid[ring_id][0] = wil->max_assoc_sta; wil->ring2cid_tid[ring_id][1] = 0; return rc; } @@ -1196,7 +1196,7 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) if (rc) goto out; - wil->ring2cid_tid[id][0] = max_assoc_sta; /* CID */ + wil->ring2cid_tid[id][0] = wil->max_assoc_sta; /* CID */ wil->ring2cid_tid[id][1] = 0; /* TID */ cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); @@ -1244,7 +1244,7 @@ static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, cid = wil_find_cid(wil, vif->mid, da); - if (cid < 0 || cid >= max_assoc_sta) + if (cid < 0 || cid >= wil->max_assoc_sta) return NULL; /* TODO: fix for multiple TID */ @@ -1296,7 +1296,7 @@ static struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil, continue; cid = wil->ring2cid_tid[i][0]; - if (cid >= max_assoc_sta) /* skip BCAST */ + if (cid >= wil->max_assoc_sta) /* skip BCAST */ continue; if (!wil->ring_tx_data[i].dot1x_open && @@ -1374,7 +1374,7 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, continue; cid = wil->ring2cid_tid[i][0]; - if (cid >= max_assoc_sta) /* skip BCAST */ + if (cid >= wil->max_assoc_sta) /* skip BCAST */ continue; if (!wil->ring_tx_data[i].dot1x_open && skb->protocol != cpu_to_be16(ETH_P_PAE)) @@ -1402,7 +1402,7 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, if (!v2->va || txdata2->mid != vif->mid) continue; cid = wil->ring2cid_tid[i][0]; - if (cid >= max_assoc_sta) /* skip BCAST */ + if (cid >= wil->max_assoc_sta) /* skip BCAST */ continue; if (!wil->ring_tx_data[i].dot1x_open && skb->protocol != cpu_to_be16(ETH_P_PAE)) @@ -2258,7 +2258,7 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid) used_before_complete = wil_ring_used_tx(vring); - if (cid < max_assoc_sta) + if (cid < wil->max_assoc_sta) stats = &wil->sta[cid].stats; while (!wil_ring_is_empty(vring)) { diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index f6fce6ff73d9..f37a2056ad21 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -734,7 +734,7 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, txdata->enabled = 0; spin_unlock_bh(&txdata->lock); wil_ring_free_edma(wil, ring); - wil->ring2cid_tid[ring_id][0] = max_assoc_sta; + wil->ring2cid_tid[ring_id][0] = wil->max_assoc_sta; wil->ring2cid_tid[ring_id][1] = 0; out: @@ -944,7 +944,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, eop = wil_rx_status_get_eop(msg); cid = wil_rx_status_get_cid(msg); - if (unlikely(!wil_val_in_range(cid, 0, max_assoc_sta))) { + if (unlikely(!wil_val_in_range(cid, 0, wil->max_assoc_sta))) { wil_err(wil, "Corrupt cid=%d, sring->swhead=%d\n", cid, sring->swhead); rxdata->skipping = true; @@ -1199,7 +1199,8 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, ndev = vif_to_ndev(vif); cid = wil->ring2cid_tid[ring_id][0]; - stats = (cid < max_assoc_sta ? &wil->sta[cid].stats : NULL); + stats = (cid < wil->max_assoc_sta) ? &wil->sta[cid].stats : + NULL; wil_dbg_txrx(wil, "tx_status: completed desc_ring (%d), num_descs (%d)\n", diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 1f0175d8399c..44984031ce54 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -461,15 +461,6 @@ static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid) *tid = (cidxtid >> 4) & 0xf; } -/** - * wil_cid_valid - check cid is valid - * @cid: CID value - */ -static inline bool wil_cid_valid(u8 cid) -{ - return (cid >= 0 && cid < max_assoc_sta); -} - struct wil6210_mbox_ring { u32 base; u16 entry_size; /* max. size of mbox entry, incl. all headers */ @@ -950,6 +941,8 @@ struct wil6210_priv { struct wil6210_vif *vifs[WIL_MAX_VIFS]; struct mutex vif_mutex; /* protects access to VIF entries */ atomic_t connected_vifs; + u32 max_assoc_sta; /* max sta's supported by the driver and the FW */ + /* profile */ struct cfg80211_chan_def monitor_chandef; u32 monitor_flags; @@ -1147,6 +1140,14 @@ static inline void wil_c(struct wil6210_priv *wil, u32 reg, u32 val) wil_w(wil, reg, wil_r(wil, reg) & ~val); } +/** + * wil_cid_valid - check cid is valid + */ +static inline bool wil_cid_valid(struct wil6210_priv *wil, u8 cid) +{ + return (cid >= 0 && cid < wil->max_assoc_sta); +} + void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len); #if defined(CONFIG_DYNAMIC_DEBUG) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index d89cd41e78ac..0a0818f1df28 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -806,8 +806,8 @@ static void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len) } } - max_assoc_sta = min_t(uint, max_assoc_sta, fw_max_assoc_sta); - wil_dbg_wmi(wil, "setting max assoc sta to %d\n", max_assoc_sta); + wil->max_assoc_sta = min_t(uint, max_assoc_sta, fw_max_assoc_sta); + wil_dbg_wmi(wil, "setting max assoc sta to %d\n", wil->max_assoc_sta); wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); @@ -974,7 +974,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) evt->assoc_req_len, evt->assoc_resp_len); return; } - if (evt->cid >= max_assoc_sta) { + if (evt->cid >= wil->max_assoc_sta) { wil_err(wil, "Connect CID invalid : %d\n", evt->cid); return; } @@ -1236,7 +1236,7 @@ static void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) return; cid = wil->ring2cid_tid[vri][0]; - if (!wil_cid_valid(cid)) { + if (!wil_cid_valid(wil, cid)) { wil_err(wil, "invalid cid %d for vring %d\n", cid, vri); return; } @@ -1439,7 +1439,7 @@ static void wil_link_stats_store_basic(struct wil6210_vif *vif, u8 cid = basic->cid; struct wil_sta_info *sta; - if (cid < 0 || cid >= max_assoc_sta) { + if (cid < 0 || cid >= wil->max_assoc_sta) { wil_err(wil, "invalid cid %d\n", cid); return; } @@ -1589,7 +1589,7 @@ static int wil_find_cid_ringid_sta(struct wil6210_priv *wil, continue; lcid = wil->ring2cid_tid[i][0]; - if (lcid >= max_assoc_sta) /* skip BCAST */ + if (lcid >= wil->max_assoc_sta) /* skip BCAST */ continue; wil_dbg_wmi(wil, "find sta -> ringid %d cid %d\n", i, lcid); @@ -2135,7 +2135,7 @@ int wmi_pcp_start(struct wil6210_vif *vif, .network_type = wmi_nettype, .disable_sec_offload = 1, .channel = chan - 1, - .pcp_max_assoc_sta = max_assoc_sta, + .pcp_max_assoc_sta = wil->max_assoc_sta, .hidden_ssid = hidden_ssid, .is_go = is_go, .ap_sme_offload_mode = disable_ap_sme ? From 7441be71ba7e07791fd4fa2b07c932dff14ff4d9 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 26 Apr 2019 18:43:35 +0300 Subject: [PATCH 34/52] wil6210: fix missed MISC mbox interrupt When MISC interrupt is triggered due to HALP bit, in parallel to mbox events handling by the MISC threaded IRQ, new mbox interrupt can be missed in the following scenario: 1. MISC ICR is read in the IRQ handler 2. Threaded IRQ is completed and all MISC interrupts are unmasked 3. mbox interrupt is set by FW 4. HALP is masked The mbox interrupt in step 3 can be missed due to constant high level of ICM. Masking all MISC IRQs instead of masking only HALP bit in step 4 will guarantee that ICM will drop to 0 and interrupt will be triggered once MISC interrupts will be unmasked. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/interrupt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index d161dc930313..b00a13d6d530 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -595,7 +595,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) /* no need to handle HALP ICRs until next vote */ wil->halp.handle_icr = false; wil_dbg_irq(wil, "irq_misc: HALP IRQ invoked\n"); - wil6210_mask_halp(wil); + wil6210_mask_irq_misc(wil, true); complete(&wil->halp.comp); } } From d6a553c0c61b0b0219764e4d4fc14e385085f374 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Fri, 26 Apr 2019 18:43:36 +0300 Subject: [PATCH 35/52] wil6210: check rx_buff_mgmt before accessing it Make sure rx_buff_mgmt is initialized before accessing it. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx_edma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index f37a2056ad21..6140db5960c3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -269,6 +269,9 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, struct list_head *active = &wil->rx_buff_mgmt.active; dma_addr_t pa; + if (!wil->rx_buff_mgmt.buff_arr) + return; + while (!list_empty(active)) { struct wil_rx_buff *rx_buff = list_first_entry(active, struct wil_rx_buff, list); From 3d0aa91984463a922c1c1a3abef6ff117bb5f418 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 26 Apr 2019 18:43:37 +0300 Subject: [PATCH 36/52] wil6210: remove HALP for Talyn devices In Talyn the HW is responsible for power management enter / exit flow, hence the deep sleep exit latency is significantly shorter than in Sparrow. In such a case HALP feature, that is meant to prevent long PCIe blocking accesses, is no longer needed and can be removed for Talyn. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index efdb6e137c16..3c30076efb64 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1940,6 +1940,9 @@ void wil_halp_vote(struct wil6210_priv *wil) unsigned long rc; unsigned long to_jiffies = msecs_to_jiffies(WAIT_FOR_HALP_VOTE_MS); + if (wil->hw_version >= HW_VER_TALYN_MB) + return; + mutex_lock(&wil->halp.lock); wil_dbg_irq(wil, "halp_vote: start, HALP ref_cnt (%d)\n", @@ -1971,6 +1974,9 @@ void wil_halp_vote(struct wil6210_priv *wil) void wil_halp_unvote(struct wil6210_priv *wil) { + if (wil->hw_version >= HW_VER_TALYN_MB) + return; + WARN_ON(wil->halp.ref_cnt == 0); mutex_lock(&wil->halp.lock); From 14bf9217d66dc799da0cda5f98aa49c6d3884120 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Wed, 27 Feb 2019 16:54:41 +0530 Subject: [PATCH 37/52] ath10k: Extended the HTT stats support to retrieve Mu-MIMO related stats Extended the bit mask value of the HTT stats to get the Mu-MIMO related stats via tracing. Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 4 ++-- drivers/net/wireless/ath/ath10k/htt.h | 3 ++- drivers/net/wireless/ath/ath10k/htt_tx.c | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 32d967a31c65..2da8611778a2 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -922,8 +922,8 @@ static ssize_t ath10k_write_htt_stats_mask(struct file *file, if (ret) return ret; - /* max 8 bit masks (for now) */ - if (mask > 0xff) + /* max 17 bit masks (for now) */ + if (mask > HTT_STATS_BIT_MASK) return -E2BIG; mutex_lock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index b28c167cb5b4..10c13c2974c8 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -315,6 +315,7 @@ struct htt_stats_req { } __packed; #define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff +#define HTT_STATS_BIT_MASK GENMASK(16, 0) /* * htt_oob_sync_req - request out-of-band sync @@ -2243,7 +2244,7 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); -int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie); +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u64 cookie); int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 78f07b7a3b21..1f07bed5e6eb 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -580,7 +580,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) return 0; } -int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u64 cookie) { struct ath10k *ar = htt->ar; struct htt_stats_req *req; @@ -603,11 +603,11 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) memset(req, 0, sizeof(*req)); - /* currently we support only max 8 bit masks so no need to worry + /* currently we support only max 24 bit masks so no need to worry * about endian support */ - req->upload_types[0] = mask; - req->reset_types[0] = mask; + memcpy(req->upload_types, &mask, 3); + memcpy(req->reset_types, &mask, 3); req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID; req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff); req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32); From 473a4084e19e3fe4ad11ff7bdb7552d0b41cd6f1 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Wed, 27 Feb 2019 16:54:42 +0530 Subject: [PATCH 38/52] ath10k: Added support to reset HTT stats in debugfs Reset HTT stats helps to get the aggregated HTT stats via tracing and also we can clear the accumulated HTT stats with this debugfs file. Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/debug.c | 43 +++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/htt.h | 3 +- drivers/net/wireless/ath/ath10k/htt_tx.c | 5 +-- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 02e53fb85814..cbb9ff4d1707 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -622,6 +622,7 @@ struct ath10k_debug { bool fw_stats_done; unsigned long htt_stats_mask; + unsigned long reset_htt_stats; struct delayed_work htt_stats_dwork; struct ath10k_dfs_stats dfs_stats; struct ath_dfs_pool_stats dfs_pool_stats; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 2da8611778a2..f828fd810270 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -873,7 +873,7 @@ static int ath10k_debug_htt_stats_req(struct ath10k *ar) cookie = get_jiffies_64(); ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask, - cookie); + ar->debug.reset_htt_stats, cookie); if (ret) { ath10k_warn(ar, "failed to send htt stats request: %d\n", ret); return ret; @@ -2469,6 +2469,44 @@ static const struct file_operations fops_ps_state_enable = { .llseek = default_llseek, }; +static ssize_t ath10k_write_reset_htt_stats(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long reset; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &reset); + if (ret) + return ret; + + if (reset == 0 || reset > 0x1ffff) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ar->debug.reset_htt_stats = reset; + + ret = ath10k_debug_htt_stats_req(ar); + if (ret) + goto out; + + ar->debug.reset_htt_stats = 0; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_reset_htt_stats = { + .write = ath10k_write_reset_htt_stats, + .owner = THIS_MODULE, + .open = simple_open, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); @@ -2609,6 +2647,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_phy, ar, &fops_ps_state_enable); + debugfs_create_file("reset_htt_stats", 0200, ar->debug.debugfs_phy, ar, + &fops_reset_htt_stats); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 10c13c2974c8..19cd0cb5875f 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -2244,7 +2244,8 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); -int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u64 cookie); +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u32 reset_mask, + u64 cookie); int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 1f07bed5e6eb..1f391c739e40 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -580,7 +580,8 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) return 0; } -int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u64 cookie) +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u32 reset_mask, + u64 cookie) { struct ath10k *ar = htt->ar; struct htt_stats_req *req; @@ -607,7 +608,7 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u64 cookie) * about endian support */ memcpy(req->upload_types, &mask, 3); - memcpy(req->reset_types, &mask, 3); + memcpy(req->reset_types, &reset_mask, 3); req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID; req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff); req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32); From 7b612ed94d0a4ce4fa457c070baa0e3a5eb35c18 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Fri, 1 Mar 2019 20:20:00 +0530 Subject: [PATCH 39/52] ath10k: Move board id and fw version logging to info level Board id and fw version is not printed by default in qmi cap response message. Move board id and fw version logging to info level for default logging. [ 34.005399] ath10k_snoc a000000.wifi: qmi chip_id 0x30b chip_family 0x4001 board_id 0xff soc_id 0x40070000 [ 34.005432] ath10k_snoc a000000.wifi: qmi fw_version 0x3106836b fw_build_timestamp 2019-02-13 10:24 fw_build_id QC_IMAGE_VERSION_STRING=WLAN.HL.3.1-00875-QCAHLSWMTPL-1 Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/qmi.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index a7bc2c70d076..2e678780df5d 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -506,6 +506,7 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) struct wlfw_cap_resp_msg_v01 *resp; struct wlfw_cap_req_msg_v01 req = {}; struct ath10k *ar = qmi->ar; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct qmi_txn txn; int ret; @@ -560,13 +561,13 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) strlcpy(qmi->fw_build_id, resp->fw_build_id, MAX_BUILD_ID_LEN + 1); - ath10k_dbg(ar, ATH10K_DBG_QMI, - "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x", - qmi->chip_info.chip_id, qmi->chip_info.chip_family, - qmi->board_info.board_id, qmi->soc_info.soc_id); - ath10k_dbg(ar, ATH10K_DBG_QMI, - "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s", - qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id); + if (!test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) { + ath10k_info(ar, "qmi chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x", + qmi->chip_info.chip_id, qmi->chip_info.chip_family, + qmi->board_info.board_id, qmi->soc_info.soc_id); + ath10k_info(ar, "qmi fw_version 0x%x fw_build_timestamp %s fw_build_id %s", + qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id); + } kfree(resp); return 0; From 889ab61d9dc1042c6707582da1c95b6dae5632ee Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Sat, 2 Mar 2019 10:06:05 +0530 Subject: [PATCH 40/52] ath10k: Modify CE4 src buffer entries to 2048 for WCN3990 CE4 is host to target HTT tx pipe, tx completion are not served on time when CPU is loaded and this cause ce src ring full condition due to less no of src buffer entries. To mitigate the issue increase CE4 src buffer entries to 2048. Testing: Tested on QCS404 platform(WCN3990 HW) Tested FW: WLAN.HL.3.1-00784-QCAHLSWMTPLZ-1 Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/snoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 845914f2eaf8..ca1186ec4129 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -165,7 +165,7 @@ static struct ce_attr host_ce_config_wlan[] = { /* CE4: host->target HTT */ { .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, - .src_nentries = 256, + .src_nentries = 2048, .src_sz_max = 256, .dest_nentries = 0, .send_cb = ath10k_snoc_htt_tx_cb, From 9e0b341a3d664eb95df2f56d180ee9d96786f860 Mon Sep 17 00:00:00 2001 From: Brandon Huang Date: Tue, 12 Mar 2019 12:33:39 +0800 Subject: [PATCH 41/52] ath10k: Fix the tx stats bytes & packets parsing In tx_stats debugfs, txrate->flags may contain multiple bits. For example, RATE_INFO_FLAGS_SHORT_GI could be set, and tx stats bytes and packets will be not updated correctly. Fix this issue by using bit operation to check txrate->flags. Tested HW: QCA9984 Tested Firmware: 10.4-3.9.0.1-00007 Signed-off-by: Brandon Huang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 45f4eecc3839..ba822a33609d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -3256,14 +3256,14 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar, #define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name] - if (txrate->flags == RATE_INFO_FLAGS_VHT_MCS) { + if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) { STATS_OP_FMT(SUCC).vht[0][mcs] += pstats->succ_bytes; STATS_OP_FMT(SUCC).vht[1][mcs] += pstats->succ_pkts; STATS_OP_FMT(FAIL).vht[0][mcs] += pstats->failed_bytes; STATS_OP_FMT(FAIL).vht[1][mcs] += pstats->failed_pkts; STATS_OP_FMT(RETRY).vht[0][mcs] += pstats->retry_bytes; STATS_OP_FMT(RETRY).vht[1][mcs] += pstats->retry_pkts; - } else if (txrate->flags == RATE_INFO_FLAGS_MCS) { + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { STATS_OP_FMT(SUCC).ht[0][ht_idx] += pstats->succ_bytes; STATS_OP_FMT(SUCC).ht[1][ht_idx] += pstats->succ_pkts; STATS_OP_FMT(FAIL).ht[0][ht_idx] += pstats->failed_bytes; @@ -3284,7 +3284,7 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar, if (ATH10K_HW_AMPDU(pstats->flags)) { tx_stats->ba_fails += ATH10K_HW_BA_FAIL(pstats->flags); - if (txrate->flags == RATE_INFO_FLAGS_MCS) { + if (txrate->flags & RATE_INFO_FLAGS_MCS) { STATS_OP_FMT(AMPDU).ht[0][ht_idx] += pstats->succ_bytes + pstats->retry_bytes; STATS_OP_FMT(AMPDU).ht[1][ht_idx] += From 5c51875c09431af01d3bce189c4c47c72cc533c9 Mon Sep 17 00:00:00 2001 From: Balaji Pothunoori Date: Tue, 12 Mar 2019 16:47:50 +0530 Subject: [PATCH 42/52] ath10k: rx_duration update for fw_stats debugfs entry Currently instant rx_duration always fetching as zero in fw_stats debugfs entry if extended peer stats event supports. This patch updates instant rx_duration in fw_stats entry based on extended peer stats and maintaining backward compatibility for 10.2/10.x. Tested HW: QCA9984. Tested FW: 10.4-3.6.0.1-00004. Signed-off-by: Balaji Pothunoori Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 +- drivers/net/wireless/ath/ath10k/debug.c | 3 ++ drivers/net/wireless/ath/ath10k/wmi.c | 37 +++++++++++++++++++++---- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index cbb9ff4d1707..24981d94bbcd 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -196,7 +196,7 @@ struct ath10k_fw_extd_stats_peer { struct list_head list; u8 peer_macaddr[ETH_ALEN]; - u32 rx_duration; + u64 rx_duration; }; struct ath10k_fw_stats_vdev { diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index f828fd810270..740781fe80f8 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -305,6 +305,9 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb) if (is_end) ar->debug.fw_stats_done = true; + if (stats.extended) + ar->debug.fw_stats.extended = true; + is_started = !list_empty(&ar->debug.fw_stats.pdevs); if (is_started && !is_end) { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 98a90e49d666..4f707c6394bb 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -8309,7 +8309,7 @@ ath10k_wmi_fw_vdev_stats_fill(const struct ath10k_fw_stats_vdev *vdev, static void ath10k_wmi_fw_peer_stats_fill(const struct ath10k_fw_stats_peer *peer, - char *buf, u32 *length) + char *buf, u32 *length, bool extended_peer) { u32 len = *length; u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; @@ -8322,13 +8322,27 @@ ath10k_wmi_fw_peer_stats_fill(const struct ath10k_fw_stats_peer *peer, "Peer TX rate", peer->peer_tx_rate); len += scnprintf(buf + len, buf_len - len, "%30s %u\n", "Peer RX rate", peer->peer_rx_rate); - len += scnprintf(buf + len, buf_len - len, "%30s %llu\n", - "Peer RX duration", peer->rx_duration); + if (!extended_peer) + len += scnprintf(buf + len, buf_len - len, "%30s %llu\n", + "Peer RX duration", peer->rx_duration); len += scnprintf(buf + len, buf_len - len, "\n"); *length = len; } +static void +ath10k_wmi_fw_extd_peer_stats_fill(const struct ath10k_fw_extd_stats_peer *peer, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "Peer MAC address", peer->peer_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %llu\n", + "Peer RX duration", peer->rx_duration); +} + void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar, struct ath10k_fw_stats *fw_stats, char *buf) @@ -8374,7 +8388,8 @@ void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar, "================="); list_for_each_entry(peer, &fw_stats->peers, list) { - ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len, + fw_stats->extended); } unlock: @@ -8432,7 +8447,8 @@ void ath10k_wmi_10x_op_fw_stats_fill(struct ath10k *ar, "================="); list_for_each_entry(peer, &fw_stats->peers, list) { - ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len, + fw_stats->extended); } unlock: @@ -8541,6 +8557,7 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, const struct ath10k_fw_stats_pdev *pdev; const struct ath10k_fw_stats_vdev_extd *vdev; const struct ath10k_fw_stats_peer *peer; + const struct ath10k_fw_extd_stats_peer *extd_peer; size_t num_peers; size_t num_vdevs; @@ -8603,7 +8620,15 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, "================="); list_for_each_entry(peer, &fw_stats->peers, list) { - ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len, + fw_stats->extended); + } + + if (fw_stats->extended) { + list_for_each_entry(extd_peer, &fw_stats->peers_extd, list) { + ath10k_wmi_fw_extd_peer_stats_fill(extd_peer, buf, + &len); + } } unlock: From 42f1bc43e6a97b9ddbe976eba9bd05306c990c75 Mon Sep 17 00:00:00 2001 From: Rakesh Pillai Date: Tue, 2 Apr 2019 18:12:50 +0530 Subject: [PATCH 43/52] ath10k: Fix encoding for protected management frames Currently the protected management frames are not appended with the MIC_LEN which results in the protected management frames being encoded incorrectly. Add the extra space at the end of the protected management frames to fix this encoding error for the protected management frames. Tested HW: WCN3990 Tested FW: WLAN.HL.3.1-00784-QCAHLSWMTPLZ-1 Fixes: 1807da49733e ("ath10k: wmi: add management tx by reference support over wmi") Signed-off-by: Rakesh Pillai Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 582fb11f648a..02709fc99034 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2840,8 +2840,10 @@ ath10k_wmi_tlv_op_gen_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_disassoc(hdr->frame_control)) && - ieee80211_has_protected(hdr->frame_control)) + ieee80211_has_protected(hdr->frame_control)) { + skb_put(msdu, IEEE80211_CCMP_MIC_LEN); buf_len += IEEE80211_CCMP_MIC_LEN; + } buf_len = min_t(u32, buf_len, WMI_TLV_MGMT_TX_FRAME_MAX_LEN); buf_len = round_up(buf_len, 4); From 35b50e70df272a4b4d313b7042e386445c98f06d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 3 Apr 2019 12:20:29 -0500 Subject: [PATCH 44/52] ath10k: Use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, change the following form: sizeof(*rx) + (sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges) to : struct_size(rx, mpdu_ranges, num_mpdu_ranges) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index ba822a33609d..0c18c58de71d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2509,9 +2509,7 @@ static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt, mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", - rx, sizeof(*rx) + - (sizeof(struct htt_rx_indication_mpdu_range) * - num_mpdu_ranges)); + rx, struct_size(rx, mpdu_ranges, num_mpdu_ranges)); for (i = 0; i < num_mpdu_ranges; i++) mpdu_count += mpdu_ranges[i].mpdu_count; From 4f735cd73650d8136f8bd78dca8f61f361791622 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 3 Apr 2019 14:06:36 -0500 Subject: [PATCH 45/52] ath10k: coredump: use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, replace code of the following form: sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0]) with: struct_size(ce_hdr, entries, CE_COUNT) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/coredump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c index 33838d9c1cb6..7825b6a1b104 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.c +++ b/drivers/net/wireless/ath/ath10k/coredump.c @@ -1192,8 +1192,8 @@ static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar) if (test_bit(ATH10K_FW_CRASH_DUMP_CE_DATA, &ath10k_coredump_mask)) { dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_CE_DATA); - dump_tlv->tlv_len = cpu_to_le32(sizeof(*ce_hdr) + - CE_COUNT * sizeof(ce_hdr->entries[0])); + dump_tlv->tlv_len = cpu_to_le32(struct_size(ce_hdr, entries, + CE_COUNT)); ce_hdr = (struct ath10k_ce_crash_hdr *)(dump_tlv->tlv_data); ce_hdr->ce_count = cpu_to_le32(CE_COUNT); memset(ce_hdr->reserved, 0, sizeof(ce_hdr->reserved)); From 9280f4fc06f44d0b4dc9e831f72d97b3d7cd35d3 Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Tue, 9 Apr 2019 12:13:13 +0530 Subject: [PATCH 46/52] ath10k: Fix the wrong value of enums for wmi tlv stats id The enum value for WMI_TLV_STAT_PDEV, WMI_TLV_STAT_VDEV and WMI_TLV_STAT_PEER is wrong, due to which the vdev stats are not received from firmware in wmi_update_stats event. Fix the enum values for above stats to receive all stats from firmware in WMI_TLV_UPDATE_STATS_EVENTID. Tested HW: WCN3990 Tested FW: WLAN.HL.3.1-00784-QCAHLSWMTPLZ-1 Fixes: f40a307eb92c ("ath10k: Fill rx duration for each peer in fw_stats for WCN3990) Signed-off-by: Surabhi Vishnoi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index e1c40bb69932..12f57f9adbba 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4535,9 +4535,10 @@ enum wmi_10_4_stats_id { }; enum wmi_tlv_stats_id { - WMI_TLV_STAT_PDEV = BIT(0), - WMI_TLV_STAT_VDEV = BIT(1), - WMI_TLV_STAT_PEER = BIT(2), + WMI_TLV_STAT_PEER = BIT(0), + WMI_TLV_STAT_AP = BIT(1), + WMI_TLV_STAT_PDEV = BIT(2), + WMI_TLV_STAT_VDEV = BIT(3), WMI_TLV_STAT_PEER_EXTD = BIT(10), }; From 8cdee1affe9000df55558fb1cd407b510f7406a6 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 10 Apr 2019 12:30:54 +0800 Subject: [PATCH 47/52] ath10k: change swap mail box config for UTF mode of SDIO For SDIO chip, it does not have HTT connect step in boot phase of UTF mode, so it does not need the swap configuration for UTF mode, otherwise it will trigger UTF load fail. For normal mode, it is swap between HTT and WMI, for UTF mode, it does not have HTT, so it can not swap between HTT and WMI. Disable swap mail box for UTF mode will let UTF mode load success. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 11 ++++++++--- drivers/net/wireless/ath/ath10k/sdio.c | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 525a7ff87bf4..a934e941760a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -656,7 +656,7 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) complete(&ar->target_suspend); } -static void ath10k_init_sdio(struct ath10k *ar) +static void ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode) { u32 param = 0; @@ -673,7 +673,12 @@ static void ath10k_init_sdio(struct ath10k *ar) * not big enough for mac80211 / native wifi frames. disable it */ param &= ~HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE; - param |= HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET; + + if (mode == ATH10K_FIRMWARE_MODE_UTF) + param &= ~HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET; + else + param |= HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET; + ath10k_bmi_write32(ar, hi_acs_flags, param); /* Explicitly set fwlog prints to zero as target may turn it on @@ -2536,7 +2541,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err; if (ar->hif.bus == ATH10K_BUS_SDIO) - ath10k_init_sdio(ar); + ath10k_init_sdio(ar, mode); } ar->htc.htc_ops.target_send_suspend_complete = diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index a2c1bad67b5b..9bbd5b54b8ca 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1642,7 +1642,12 @@ static int ath10k_sdio_hif_swap_mailbox(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio mailbox swap service enabled\n"); ar_sdio->swap_mbox = true; + } else { + ath10k_dbg(ar, ATH10K_DBG_SDIO, + "sdio mailbox swap service disabled\n"); + ar_sdio->swap_mbox = false; } + return 0; } From a7368c395fc3ebd544cf6ceaca715adb4e85eff1 Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Wed, 17 Apr 2019 14:01:45 +0530 Subject: [PATCH 48/52] ath10k: Add wmi tlv vdev subtype for mesh in WCN3990 There is a disparity in wmi and wmi tlv vdev subtype enum for WMI_VDEV_SUBTYPE_MESH_11S. Add different enum for wmi tlv vdev subtype to support vdev of subtype mesh 11s for WCN3990. Tested HW: WCN3990 Signed-off-by: Surabhi Vishnoi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 24 ++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi-tlv.h | 10 ++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 02709fc99034..34e187486c63 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -1905,6 +1905,28 @@ ath10k_wmi_tlv_op_gen_stop_scan(struct ath10k *ar, return skb; } +static int ath10k_wmi_tlv_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype) +{ + switch (subtype) { + case WMI_VDEV_SUBTYPE_NONE: + return WMI_TLV_VDEV_SUBTYPE_NONE; + case WMI_VDEV_SUBTYPE_P2P_DEVICE: + return WMI_TLV_VDEV_SUBTYPE_P2P_DEV; + case WMI_VDEV_SUBTYPE_P2P_CLIENT: + return WMI_TLV_VDEV_SUBTYPE_P2P_CLI; + case WMI_VDEV_SUBTYPE_P2P_GO: + return WMI_TLV_VDEV_SUBTYPE_P2P_GO; + case WMI_VDEV_SUBTYPE_PROXY_STA: + return WMI_TLV_VDEV_SUBTYPE_PROXY_STA; + case WMI_VDEV_SUBTYPE_MESH_11S: + return WMI_TLV_VDEV_SUBTYPE_MESH_11S; + case WMI_VDEV_SUBTYPE_MESH_NON_11S: + return -ENOTSUPP; + } + return -ENOTSUPP; +} + static struct sk_buff * ath10k_wmi_tlv_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id, @@ -4307,7 +4329,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update, .gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs, .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, - .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, + .get_vdev_subtype = ath10k_wmi_tlv_op_get_vdev_subtype, .gen_echo = ath10k_wmi_tlv_op_gen_echo, .gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf, .gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 65e6aa520b06..55da01f8bb5b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -1775,6 +1775,16 @@ struct wmi_tlv_start_scan_cmd { struct wmi_mac_addr mac_mask; } __packed; +enum wmi_tlv_vdev_subtype { + WMI_TLV_VDEV_SUBTYPE_NONE = 0, + WMI_TLV_VDEV_SUBTYPE_P2P_DEV = 1, + WMI_TLV_VDEV_SUBTYPE_P2P_CLI = 2, + WMI_TLV_VDEV_SUBTYPE_P2P_GO = 3, + WMI_TLV_VDEV_SUBTYPE_PROXY_STA = 4, + WMI_TLV_VDEV_SUBTYPE_MESH = 5, + WMI_TLV_VDEV_SUBTYPE_MESH_11S = 6, +}; + struct wmi_tlv_vdev_start_cmd { __le32 vdev_id; __le32 requestor_id; From 97354f2c432788e3163134df6bb144f4b6289d87 Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Wed, 17 Apr 2019 14:01:46 +0530 Subject: [PATCH 49/52] ath10k: Do not send probe response template for mesh Currently mac80211 do not support probe response template for mesh point. When WMI_SERVICE_BEACON_OFFLOAD is enabled, host driver tries to configure probe response template for mesh, but it fails because the interface type is not NL80211_IFTYPE_AP but NL80211_IFTYPE_MESH_POINT. To avoid this failure, skip sending probe response template to firmware for mesh point. Tested HW: WCN3990/QCA6174/QCA9984 Signed-off-by: Surabhi Vishnoi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 80760a20a049..98a7842e09b1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1630,6 +1630,10 @@ static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif) if (arvif->vdev_type != WMI_VDEV_TYPE_AP) return 0; + /* For mesh, probe response and beacon share the same template */ + if (ieee80211_vif_is_mesh(vif)) + return 0; + prb = ieee80211_proberesp_get(hw, vif); if (!prb) { ath10k_warn(ar, "failed to get probe resp template from mac80211\n"); From 7835d8f8792dab0e3704951d5009c2b832d4513d Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Wed, 17 Apr 2019 14:01:47 +0530 Subject: [PATCH 50/52] ath10k: Add wmi tlv service map for mesh 11s WCN3990 firmware advertises the service WMI_TLV_SERVICE_MESH_11S for mesh 11s. Map this wmi tlv service to enable mesh 11s for WCN3990. Tested HW: WCN3990 Signed-off-by: Surabhi Vishnoi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 55da01f8bb5b..bc3198b08844 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -1567,6 +1567,8 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len) WMI_SERVICE_SAP_AUTH_OFFLOAD, len); SVCMAP(WMI_TLV_SERVICE_MGMT_TX_WMI, WMI_SERVICE_MGMT_TX_WMI, len); + SVCMAP(WMI_TLV_SERVICE_MESH_11S, + WMI_SERVICE_MESH_11S, len); } static inline void From 49ed34b835e231aa941257394716bc689bc98d9f Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Mon, 29 Apr 2019 19:17:12 +0800 Subject: [PATCH 51/52] ath10k: add peer id check in ath10k_peer_find_by_id For some SDIO chip, the peer id is 65535 for MPDU with error status, then test_bit will trigger buffer overflow for peer's memory, if kasan enabled, it will report error. Reason is when station is in disconnecting status, firmware do not delete the peer info since it not disconnected completely, meanwhile some AP will still send data packet to station, then hardware will receive the packet and send to firmware, firmware's logic will report peer id of 65535 for MPDU with error status. Add check for overflow the size of peer's peer_ids will avoid the buffer overflow access. Call trace of kasan: dump_backtrace+0x0/0x2ec show_stack+0x20/0x2c __dump_stack+0x20/0x28 dump_stack+0xc8/0xec print_address_description+0x74/0x240 kasan_report+0x250/0x26c __asan_report_load8_noabort+0x20/0x2c ath10k_peer_find_by_id+0x180/0x1e4 [ath10k_core] ath10k_htt_t2h_msg_handler+0x100c/0x2fd4 [ath10k_core] ath10k_htt_htc_t2h_msg_handler+0x20/0x34 [ath10k_core] ath10k_sdio_irq_handler+0xcc8/0x1678 [ath10k_sdio] process_sdio_pending_irqs+0xec/0x370 sdio_run_irqs+0x68/0xe4 sdio_irq_work+0x1c/0x28 process_one_work+0x3d8/0x8b0 worker_thread+0x508/0x7cc kthread+0x24c/0x264 ret_from_fork+0x10/0x18 Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/txrx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index c5818d28f55a..4102df016931 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -150,6 +150,9 @@ struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id) { struct ath10k_peer *peer; + if (peer_id >= BITS_PER_TYPE(peer->peer_ids)) + return NULL; + lockdep_assert_held(&ar->data_lock); list_for_each_entry(peer, &ar->peers, list) From 9e7251fa38978b85108c44743e1436d48e8d0d76 Mon Sep 17 00:00:00 2001 From: Yingying Tang Date: Thu, 2 May 2019 21:36:50 +0800 Subject: [PATCH 52/52] ath10k: Check tx_stats before use it tx_stats will be freed and set to NULL before debugfs_sta node is removed in station disconnetion process. So if read the debugfs_sta node there may be NULL pointer error. Add check for tx_stats before use it to resove this issue. Signed-off-by: Yingying Tang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debugfs_sta.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index c704ae371c4d..42931a669b02 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -663,6 +663,13 @@ static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file, mutex_lock(&ar->conf_mutex); + if (!arsta->tx_stats) { + ath10k_warn(ar, "failed to get tx stats"); + mutex_unlock(&ar->conf_mutex); + kfree(buf); + return 0; + } + spin_lock_bh(&ar->data_lock); for (k = 0; k < ATH10K_STATS_TYPE_MAX; k++) { for (j = 0; j < ATH10K_COUNTER_TYPE_MAX; j++) {