From 3e841fd0a54745c8ad24ecfb0303c505b7460ffb Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 14 May 2014 16:23:31 +0300 Subject: ath10k: fix htt rx ring clean up msdu_payId was read before txrx tasklet was killed so it was possible to end up using an invalid sk_buff pointer leading to a panic. Make sure to sanitize rx ring sk_buff pointers and make the clean up go through all possible entries and not rely on coherent-DMA mapped u32 index which could be (in theory) corrupted by the device as well. Reported-By: Avery Pennarun Reported-By: Ben Greear Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index f85a3cf6da31..db6c8af0c9b1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -225,10 +225,26 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) ath10k_htt_rx_msdu_buff_replenish(htt); } -void ath10k_htt_rx_detach(struct ath10k_htt *htt) +static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt) { - int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; + struct sk_buff *skb; + int i; + + for (i = 0; i < htt->rx_ring.size; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + if (!skb) + continue; + + dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + htt->rx_ring.netbufs_ring[i] = NULL; + } +} +void ath10k_htt_rx_detach(struct ath10k_htt *htt) +{ del_timer_sync(&htt->rx_ring.refill_retry_timer); tasklet_kill(&htt->rx_replenish_task); tasklet_kill(&htt->txrx_compl_task); @@ -236,18 +252,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt) skb_queue_purge(&htt->tx_compl_q); skb_queue_purge(&htt->rx_compl_q); - while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { - struct sk_buff *skb = - htt->rx_ring.netbufs_ring[sw_rd_idx]; - struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); - - dma_unmap_single(htt->ar->dev, cb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]); - sw_rd_idx++; - sw_rd_idx &= htt->rx_ring.size_mask; - } + ath10k_htt_rx_ring_clean_up(htt); dma_free_coherent(htt->ar->dev, (htt->rx_ring.size * @@ -277,6 +282,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) idx = htt->rx_ring.sw_rd_idx.msdu_payld; msdu = htt->rx_ring.netbufs_ring[idx]; + htt->rx_ring.netbufs_ring[idx] = NULL; idx++; idx &= htt->rx_ring.size_mask; @@ -494,7 +500,7 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt); htt->rx_ring.netbufs_ring = - kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *), GFP_KERNEL); if (!htt->rx_ring.netbufs_ring) goto err_netbuf; -- cgit From ede9c8e09192e8a073915ae2d7be56409bb69ddb Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 14 May 2014 16:23:31 +0300 Subject: ath10k: fix handling of wierd MSDU chaining cases Apparently firmware can sometimes report a sequence with the first rx descriptor saying it's not the last MSDU. In that case msdu_chaining value could be overwritten saying it's not a chained MSDU. This in turn led to skb_push panic as the frame could be treated as an A-MSDU instead of a chained MSDU. Reported-By: Avery Pennarun Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index db6c8af0c9b1..ac6a5fe75c87 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -312,6 +312,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; + bool corrupted = false; lockdep_assert_held(&htt->rx_ring.lock); @@ -405,7 +406,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0), RX_MSDU_START_INFO0_MSDU_LENGTH); msdu_chained = rx_desc->frag_info.ring2_more_count; - msdu_chaining = msdu_chained; if (msdu_len_invalid) msdu_len = 0; @@ -433,11 +433,15 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->next = next; msdu = next; + msdu_chaining = 1; } last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & RX_MSDU_END_INFO0_LAST_MSDU; + if (msdu_chaining && !last_msdu) + corrupted = true; + if (last_msdu) { msdu->next = NULL; break; @@ -452,6 +456,20 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, if (*head_msdu == NULL) msdu_chaining = -1; + /* + * Apparently FW sometimes reports weird chained MSDU sequences with + * more than one rx descriptor. This seems like a bug but needs more + * analyzing. For the time being fix it by dropping such sequences to + * avoid blowing up the host system. + */ + if (corrupted) { + ath10k_warn("failed to pop chained msdus, dropping\n"); + ath10k_htt_rx_free_msdu_chain(*head_msdu); + *head_msdu = NULL; + *tail_msdu = NULL; + msdu_chaining = -EINVAL; + } + /* * Don't refill the ring yet. * -- cgit From d0e0a5524cd0179268935b0d4fcb0772265fd5e6 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 14 May 2014 16:56:14 +0300 Subject: ath10k: fix spelling mistake in comments Simple typo fix. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index ae838221af65..e59b245b79b9 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2323,9 +2323,9 @@ struct wmi_pdev_param_map { #define WMI_PDEV_PARAM_UNSUPPORTED 0 enum wmi_pdev_param { - /* TX chian mask */ + /* TX chain mask */ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, - /* RX chian mask */ + /* RX chain mask */ WMI_PDEV_PARAM_RX_CHAIN_MASK, /* TX power limit for 2G Radio */ WMI_PDEV_PARAM_TXPOWER_LIMIT2G, -- cgit From de01357b36f89ae918b12a81faf448fffe8288fc Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 14 May 2014 16:56:16 +0300 Subject: ath10k: improve warm reset reliability Warm reset is now able to recover after device crashes which required a cold reset before. This should greatly reduce chances of getting data bus errors or host system freezes due to buggy cold reset on some chips. kvalo: use ath10k_pci_soc_*() Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 66b1f3017f2b..02dc4083882d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1802,6 +1802,26 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } +/* this function effectively clears target memory controller assert line */ +static void ath10k_pci_warm_reset_si0(struct ath10k *ar) +{ + u32 val; + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val & ~SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); +} + static int ath10k_pci_warm_reset(struct ath10k *ar) { int ret = 0; @@ -1860,6 +1880,8 @@ static int ath10k_pci_warm_reset(struct ath10k *ar) SOC_RESET_CONTROL_ADDRESS); msleep(10); + ath10k_pci_warm_reset_si0(ar); + /* debug */ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CAUSE_ADDRESS); -- cgit From 61c95cea66ae78502c19bfe72246769d980144c9 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 14 May 2014 16:56:16 +0300 Subject: ath10k: retry warm reset a few times Sometimes warm reset works upon retry. It might be related to imperfect warm reset routine, but for now let's just do the retries. This should improve the reliability of some chips that hang/crash with cold reset which is used as a last resort if warm reset fails. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 02dc4083882d..21a32ad4c09c 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -59,6 +59,7 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)"); /* how long wait to wait for target to initialise, in ms */ #define ATH10K_PCI_TARGET_WAIT 3000 +#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3 #define QCA988X_2_0_DEVICE_ID (0x003c) @@ -2010,6 +2011,28 @@ err: return ret; } +static int ath10k_pci_hif_power_up_warm(struct ath10k *ar) +{ + int i, ret; + + /* + * Sometime warm reset succeeds after retries. + * + * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work + * at first try. + */ + for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) { + ret = __ath10k_pci_hif_power_up(ar, false); + if (ret == 0) + break; + + ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n", + i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret); + } + + return ret; +} + static int ath10k_pci_hif_power_up(struct ath10k *ar) { int ret; @@ -2021,10 +2044,10 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) * preferred (and safer) way to perform a device reset is through a * warm reset. * - * Warm reset doesn't always work though (notably after a firmware - * crash) so fall back to cold reset if necessary. + * Warm reset doesn't always work though so fall back to cold reset may + * be necessary. */ - ret = __ath10k_pci_hif_power_up(ar, false); + ret = ath10k_pci_hif_power_up_warm(ar); if (ret) { ath10k_warn("failed to power up target using warm reset: %d\n", ret); -- cgit From 1a4ab28fc0baccbc8d78fa9fa22821e108ae4f4a Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 14 May 2014 16:56:16 +0300 Subject: ath10k: enable early device dumps This can be useful for early initialization debugging, i.e. ROM crashes. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 21a32ad4c09c..33e87557a7dd 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2241,10 +2241,7 @@ static void ath10k_pci_early_irq_tasklet(unsigned long data) if (fw_ind & FW_IND_EVENT_PENDING) { ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, fw_ind & ~FW_IND_EVENT_PENDING); - - /* Some structures are unavailable during early boot or at - * driver teardown so just print that the device has crashed. */ - ath10k_warn("device crashed - no diagnostics available\n"); + ath10k_pci_hif_dump_area(ar); } ath10k_pci_sleep(ar); @@ -2521,6 +2518,9 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (val & FW_IND_EVENT_PENDING) { ath10k_warn("device has crashed during init\n"); + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, + val & ~FW_IND_EVENT_PENDING); + ath10k_pci_hif_dump_area(ar); ret = -ECOMM; goto out; } -- cgit From a6a2f74e565db27d6b23276a4df16e00d789f60c Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 14 May 2014 16:56:17 +0300 Subject: ath10k: get rid of pci_assign_resource() call from pci_probe On ARM-based (MSM mach), the pci_assign_resource() is passing some invalid pointers and leading to L2 cache errors, what prevents the PCI communication completly. So far I have not found this funtion to be directly called by any other wifi driver and did not found this assigning needed on any other platform. So removing it completely. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 33e87557a7dd..7d72b9cfe0be 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2647,18 +2647,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ar); - /* - * Without any knowledge of the Host, the Target may have been reset or - * power cycled and its Config Space may no longer reflect the PCI - * address space that was assigned earlier by the PCI infrastructure. - * Refresh it now. - */ - ret = pci_assign_resource(pdev, BAR_NUM); - if (ret) { - ath10k_err("failed to assign PCI space: %d\n", ret); - goto err_ar; - } - ret = pci_enable_device(pdev); if (ret) { ath10k_err("failed to enable PCI device: %d\n", ret); -- cgit From eeab266c0f3c9c85ef649ba737e2d553742a2532 Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Wed, 14 May 2014 16:56:17 +0300 Subject: ath10k: fix pmf for action frames Fix sending and receiveing protected managment frames. Lack of protected flag for received protected action frames causes report these frames as unprotected robust action frames. If the driver in AP mode sent frame with protected flag and CCMP header using IEEE80211_KEY_FLAG_SW_MGMT_TX flag, the FW encrypted frames once again. From user side all received SA Query Requests and Responses were skipped and all protected action frames were sent as malformed packets. Signed-off-by: Marek Kwaczynski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 5 ++++- drivers/net/wireless/ath/ath10k/wmi.c | 24 ++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 7026f021ccbb..12518047bd31 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -54,7 +54,10 @@ static int ath10k_send_key(struct ath10k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: arg.key_cipher = WMI_CIPHER_AES_CCM; - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; + else + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; case WLAN_CIPHER_SUITE_TKIP: arg.key_cipher = WMI_CIPHER_TKIP; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 72cc4f20d102..e355d5e4928d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -639,6 +639,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) struct sk_buff *wmi_skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int len; + u32 buf_len = skb->len; u16 fc; hdr = (struct ieee80211_hdr *)skb->data; @@ -648,6 +649,15 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) return -EINVAL; len = sizeof(cmd->hdr) + skb->len; + + 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)) { + len += IEEE80211_CCMP_MIC_LEN; + buf_len += IEEE80211_CCMP_MIC_LEN; + } + len = round_up(len, 4); wmi_skb = ath10k_wmi_alloc_skb(len); @@ -659,7 +669,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); cmd->hdr.tx_rate = 0; cmd->hdr.tx_power = 0; - cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len)); + cmd->hdr.buf_len = __cpu_to_le32(buf_len); memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); memcpy(cmd->buf, skb->data, skb->len); @@ -957,10 +967,16 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) * frames with Protected Bit set. */ if (ieee80211_has_protected(hdr->frame_control) && !ieee80211_is_auth(hdr->frame_control)) { - status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED; - hdr->frame_control = __cpu_to_le16(fc & + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_action(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & ~IEEE80211_FCTL_PROTECTED); + } } ath10k_dbg(ATH10K_DBG_MGMT, -- cgit From 46acf7bb9b61289ea3353561142e3bf41119bd3e Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 16 May 2014 17:15:38 +0300 Subject: ath10k: support get/set antenna configurations. Verified that target's tx/rx chain register is set appropriately, and that the tx rate goes down as number of chains decrease, but I did not actually try to verify antenna ceased to transmit when disabled. kvalo: move ar->supp_*_chainmask initialisation to ath10k_mac_register() Signed-off-by: Ben Greear Signed-off-by: Avery Pennarun Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 6 +++ drivers/net/wireless/ath/ath10k/mac.c | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2c1dfd719146..679b2a32331b 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -440,6 +440,12 @@ struct ath10k { bool radar_enabled; int num_started_vdevs; + /* Protected by conf-mutex */ + u8 supp_tx_chainmask; + u8 supp_rx_chainmask; + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; + struct wmi_pdev_set_wmm_params_arg wmm_params; struct completion install_key_done; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 12518047bd31..8c997626f45f 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2332,6 +2332,68 @@ void ath10k_halt(struct ath10k *ar) spin_unlock_bh(&ar->data_lock); } +static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + if (ar->cfg_tx_chainmask) { + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; + } else { + *tx_ant = ar->supp_tx_chainmask; + *rx_ant = ar->supp_rx_chainmask; + } + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ar->cfg_tx_chainmask = tx_ant; + ar->cfg_rx_chainmask = rx_ant; + + if ((ar->state != ATH10K_STATE_ON) && + (ar->state != ATH10K_STATE_RESTARTED)) + return 0; + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask, + tx_ant); + if (ret) { + ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n", + ret, tx_ant); + return ret; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask, + rx_ant); + if (ret) { + ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n", + ret, rx_ant); + return ret; + } + + return 0; +} + +static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = __ath10k_set_antenna(ar, tx_ant, rx_ant); + mutex_unlock(&ar->conf_mutex); + return ret; +} + static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; @@ -2373,6 +2435,10 @@ static int ath10k_start(struct ieee80211_hw *hw) if (ret) ath10k_warn("failed to enable dynamic BW: %d\n", ret); + if (ar->cfg_tx_chainmask) + __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, + ar->cfg_rx_chainmask); + /* * By default FW set ARP frames ac to voice (6). In that case ARP * exchange is not working properly for UAPSD enabled AP. ARP requests @@ -4256,6 +4322,8 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, + .set_antenna = ath10k_set_antenna, + .get_antenna = ath10k_get_antenna, .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_set_bitrate_mask, @@ -4605,6 +4673,18 @@ int ath10k_mac_register(struct ath10k *ar) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + /* TODO: Have to deal with 2x2 chips if/when the come out. */ + ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK; + } else { + ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK; + } + + ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; + ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; + if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | -- cgit From 95bf21f97f09b724ed7d5eb9f0b3c5c664f0329e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 16 May 2014 17:15:39 +0300 Subject: ath10k: fix core start sequence It was possible to call hif_stop() 2 times through ath10k_htc_connect_init() timeout failpath which could lead to double free_irq() kernel splat for multiple MSI interrupt case. Re-order init sequence to avoid this problem. The HTC stop shouldn't stop HIF implicitly since it doesn't implicitly start it. Since the re-ordering required some functions to be split/removed/renamed rename a few functions to make more sense while at it. Reported-By: Ben Greear Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 101 +++++++++++++++++-------------- drivers/net/wireless/ath/ath10k/htc.c | 6 -- drivers/net/wireless/ath/ath10k/htt.c | 42 +------------ drivers/net/wireless/ath/ath10k/htt.h | 18 +++--- drivers/net/wireless/ath/ath10k/htt_rx.c | 4 +- drivers/net/wireless/ath/ath10k/htt_tx.c | 8 +-- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- drivers/net/wireless/ath/ath10k/wmi.h | 2 +- 8 files changed, 76 insertions(+), 107 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 75b3dfbd6509..eca826ff393c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -58,36 +58,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) complete(&ar->target_suspend); } -static int ath10k_init_connect_htc(struct ath10k *ar) -{ - int status; - - status = ath10k_wmi_connect_htc_service(ar); - if (status) - goto conn_fail; - - /* Start HTC */ - status = ath10k_htc_start(&ar->htc); - if (status) - goto conn_fail; - - /* Wait for WMI event to be ready */ - status = ath10k_wmi_wait_for_service_ready(ar); - if (status <= 0) { - ath10k_warn("wmi service ready event not received"); - status = -ETIMEDOUT; - goto timeout; - } - - ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n"); - return 0; - -timeout: - ath10k_htc_stop(&ar->htc); -conn_fail: - return status; -} - static int ath10k_init_configure_target(struct ath10k *ar) { u32 param_host; @@ -805,10 +775,28 @@ int ath10k_core_start(struct ath10k *ar) goto err; } + status = ath10k_htt_init(ar); + if (status) { + ath10k_err("failed to init htt: %d\n", status); + goto err_wmi_detach; + } + + status = ath10k_htt_tx_alloc(&ar->htt); + if (status) { + ath10k_err("failed to alloc htt tx: %d\n", status); + goto err_wmi_detach; + } + + status = ath10k_htt_rx_alloc(&ar->htt); + if (status) { + ath10k_err("failed to alloc htt rx: %d\n", status); + goto err_htt_tx_detach; + } + status = ath10k_hif_start(ar); if (status) { ath10k_err("could not start HIF: %d\n", status); - goto err_wmi_detach; + goto err_htt_rx_detach; } status = ath10k_htc_wait_target(&ar->htc); @@ -817,15 +805,30 @@ int ath10k_core_start(struct ath10k *ar) goto err_hif_stop; } - status = ath10k_htt_attach(ar); + status = ath10k_htt_connect(&ar->htt); if (status) { - ath10k_err("could not attach htt (%d)\n", status); + ath10k_err("failed to connect htt (%d)\n", status); goto err_hif_stop; } - status = ath10k_init_connect_htc(ar); - if (status) - goto err_htt_detach; + status = ath10k_wmi_connect(ar); + if (status) { + ath10k_err("could not connect wmi: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_htc_start(&ar->htc); + if (status) { + ath10k_err("failed to start htc: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_wmi_wait_for_service_ready(ar); + if (status <= 0) { + ath10k_warn("wmi service ready event not received"); + status = -ETIMEDOUT; + goto err_htc_stop; + } ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n", ar->hw->wiphy->fw_version); @@ -833,23 +836,25 @@ int ath10k_core_start(struct ath10k *ar) status = ath10k_wmi_cmd_init(ar); if (status) { ath10k_err("could not send WMI init command (%d)\n", status); - goto err_disconnect_htc; + goto err_htc_stop; } status = ath10k_wmi_wait_for_unified_ready(ar); if (status <= 0) { ath10k_err("wmi unified ready event not received\n"); status = -ETIMEDOUT; - goto err_disconnect_htc; + goto err_htc_stop; } - status = ath10k_htt_attach_target(&ar->htt); - if (status) - goto err_disconnect_htc; + status = ath10k_htt_setup(&ar->htt); + if (status) { + ath10k_err("failed to setup htt: %d\n", status); + goto err_htc_stop; + } status = ath10k_debug_start(ar); if (status) - goto err_disconnect_htc; + goto err_htc_stop; ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; INIT_LIST_HEAD(&ar->arvifs); @@ -868,12 +873,14 @@ int ath10k_core_start(struct ath10k *ar) return 0; -err_disconnect_htc: +err_htc_stop: ath10k_htc_stop(&ar->htc); -err_htt_detach: - ath10k_htt_detach(&ar->htt); err_hif_stop: ath10k_hif_stop(ar); +err_htt_rx_detach: + ath10k_htt_rx_free(&ar->htt); +err_htt_tx_detach: + ath10k_htt_tx_free(&ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); err: @@ -913,7 +920,9 @@ void ath10k_core_stop(struct ath10k *ar) ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); - ath10k_htt_detach(&ar->htt); + ath10k_hif_stop(ar); + ath10k_htt_tx_free(&ar->htt); + ath10k_htt_rx_free(&ar->htt); ath10k_wmi_detach(ar); } EXPORT_SYMBOL(ath10k_core_stop); diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 5b58dbb17416..e493db4b4a41 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -830,17 +830,11 @@ int ath10k_htc_start(struct ath10k_htc *htc) return 0; } -/* - * stop HTC communications, i.e. stop interrupt reception, and flush all - * queued buffers - */ void ath10k_htc_stop(struct ath10k_htc *htc) { spin_lock_bh(&htc->tx_lock); htc->stopped = true; spin_unlock_bh(&htc->tx_lock); - - ath10k_hif_stop(htc->ar); } /* registered target arrival callback from the HIF layer */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 69697af59ce0..19c12cc8d663 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -22,7 +22,7 @@ #include "core.h" #include "debug.h" -static int ath10k_htt_htc_attach(struct ath10k_htt *htt) +int ath10k_htt_connect(struct ath10k_htt *htt) { struct ath10k_htc_svc_conn_req conn_req; struct ath10k_htc_svc_conn_resp conn_resp; @@ -48,38 +48,13 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) return 0; } -int ath10k_htt_attach(struct ath10k *ar) +int ath10k_htt_init(struct ath10k *ar) { struct ath10k_htt *htt = &ar->htt; - int ret; htt->ar = ar; htt->max_throughput_mbps = 800; - /* - * Connect to HTC service. - * This has to be done before calling ath10k_htt_rx_attach, - * since ath10k_htt_rx_attach involves sending a rx ring configure - * message to the target. - */ - ret = ath10k_htt_htc_attach(htt); - if (ret) { - ath10k_err("could not attach htt htc (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_tx_attach(htt); - if (ret) { - ath10k_err("could not attach htt tx (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_rx_attach(htt); - if (ret) { - ath10k_err("could not attach htt rx (%d)\n", ret); - goto err_rx_attach; - } - /* * Prefetch enough data to satisfy target * classification engine. @@ -93,11 +68,6 @@ int ath10k_htt_attach(struct ath10k *ar) 2; /* ip4 dscp or ip6 priority */ return 0; - -err_rx_attach: - ath10k_htt_tx_detach(htt); -err_htc_attach: - return ret; } #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) @@ -117,7 +87,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt) return 0; } -int ath10k_htt_attach_target(struct ath10k_htt *htt) +int ath10k_htt_setup(struct ath10k_htt *htt) { int status; @@ -140,9 +110,3 @@ int ath10k_htt_attach_target(struct ath10k_htt *htt) return ath10k_htt_send_rx_ring_cfg_ll(htt); } - -void ath10k_htt_detach(struct ath10k_htt *htt) -{ - ath10k_htt_rx_detach(htt); - ath10k_htt_tx_detach(htt); -} diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 645a563e3fb9..9a263462c793 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1328,14 +1328,16 @@ struct htt_rx_desc { #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) -int ath10k_htt_attach(struct ath10k *ar); -int ath10k_htt_attach_target(struct ath10k_htt *htt); -void ath10k_htt_detach(struct ath10k_htt *htt); - -int ath10k_htt_tx_attach(struct ath10k_htt *htt); -void ath10k_htt_tx_detach(struct ath10k_htt *htt); -int ath10k_htt_rx_attach(struct ath10k_htt *htt); -void ath10k_htt_rx_detach(struct ath10k_htt *htt); +int ath10k_htt_connect(struct ath10k_htt *htt); +int ath10k_htt_init(struct ath10k *ar); +int ath10k_htt_setup(struct ath10k_htt *htt); + +int ath10k_htt_tx_alloc(struct ath10k_htt *htt); +void ath10k_htt_tx_free(struct ath10k_htt *htt); + +int ath10k_htt_rx_alloc(struct ath10k_htt *htt); +void ath10k_htt_rx_free(struct ath10k_htt *htt); + void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index ac6a5fe75c87..f744e355c70a 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -243,7 +243,7 @@ static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt) } } -void ath10k_htt_rx_detach(struct ath10k_htt *htt) +void ath10k_htt_rx_free(struct ath10k_htt *htt) { del_timer_sync(&htt->rx_ring.refill_retry_timer); tasklet_kill(&htt->rx_replenish_task); @@ -492,7 +492,7 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr) ath10k_htt_rx_msdu_buff_replenish(htt); } -int ath10k_htt_rx_attach(struct ath10k_htt *htt) +int ath10k_htt_rx_alloc(struct ath10k_htt *htt) { dma_addr_t paddr; void *vaddr; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 7a3e2e40dd5c..7064354d1f4f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -83,7 +83,7 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) __clear_bit(msdu_id, htt->used_msdu_ids); } -int ath10k_htt_tx_attach(struct ath10k_htt *htt) +int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { spin_lock_init(&htt->tx_lock); init_waitqueue_head(&htt->empty_tx_wq); @@ -120,7 +120,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) return 0; } -static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) { struct htt_tx_done tx_done = {0}; int msdu_id; @@ -141,9 +141,9 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) spin_unlock_bh(&htt->tx_lock); } -void ath10k_htt_tx_detach(struct ath10k_htt *htt) +void ath10k_htt_tx_free(struct ath10k_htt *htt) { - ath10k_htt_tx_cleanup_pending(htt); + ath10k_htt_tx_free_pending(htt); kfree(htt->pending_tx); kfree(htt->used_msdu_ids); dma_pool_destroy(htt->tx_pool); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index e355d5e4928d..4b7782a529ac 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2375,7 +2375,7 @@ void ath10k_wmi_detach(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } -int ath10k_wmi_connect_htc_service(struct ath10k *ar) +int ath10k_wmi_connect(struct ath10k *ar) { int status; struct ath10k_htc_svc_conn_req conn_req; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index e59b245b79b9..89ef3b3749eb 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4259,7 +4259,7 @@ void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); -int ath10k_wmi_connect_htc_service(struct ath10k *ar); +int ath10k_wmi_connect(struct ath10k *ar); int ath10k_wmi_pdev_set_channel(struct ath10k *ar, const struct wmi_channel_arg *); int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); -- cgit From f2708bedf2bec33cab077defe0587497e203284d Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 16 May 2014 17:15:39 +0300 Subject: ath10k: prevent hif_stop being called twice Recently there was a bug discovered that involved hif_stop() being called twice that ended up with a double free_irq() call but it only manifested with multiple MSI interrupts mapping. Catch this kind of a problem early in driver regardless of interrupt mapping. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7d72b9cfe0be..91d6076d7a71 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1272,6 +1272,9 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n"); + if (WARN_ON(!ar_pci->started)) + return; + ret = ath10k_ce_disable_interrupts(ar); if (ret) ath10k_warn("failed to disable CE interrupts: %d\n", ret); -- cgit From afe5b7b4db0c95f22cdcbc02e0b021887faae277 Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Fri, 23 May 2014 11:04:49 +0300 Subject: ath10k: fix ath10k_bmi_read32 macro tmp may be used uninitialized if ath10k_bmi_read_memory() returns an error. Signed-off-by: Frederic Danis Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 3a9bdf51c96a..111ab701465c 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -201,7 +201,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, \ addr = host_interest_item_address(HI_ITEM(item)); \ ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \ - *val = __le32_to_cpu(tmp); \ + if (!ret) \ + *val = __le32_to_cpu(tmp); \ ret; \ }) -- cgit From aecdc89fb4664c76baa4bbd46008f220532309ff Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 23 May 2014 11:04:50 +0300 Subject: ath9k/ath10k: remove unnecessary channel_switch_beacon callbacks The channel_switch_beacon callback is optional, so it doesn't have to be defined if it's not going to do anything useful with it. Both ath9k and ath10k define the callback and just returns. This commit removes them. Cc: Michal Kazior Signed-off-by: Luciano Coelho Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 9 --------- drivers/net/wireless/ath/ath9k/main.c | 9 --------- 2 files changed, 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8c997626f45f..8f311b373859 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4211,14 +4211,6 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, fixed_nss, force_sgi); } -static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_chan_def *chandef) -{ - /* there's no need to do anything here. vif->csa_active is enough */ - return; -} - static void ath10k_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -4327,7 +4319,6 @@ static const struct ieee80211_ops ath10k_ops = { .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_set_bitrate_mask, - .channel_switch_beacon = ath10k_channel_switch_beacon, .sta_rc_update = ath10k_sta_rc_update, .get_tsf = ath10k_get_tsf, #ifdef CONFIG_PM diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8d7b9b66fefa..fc9344f2a974 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2191,14 +2191,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } -static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_chan_def *chandef) -{ - /* depend on vif->csa_active only */ - return; -} - struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -2246,5 +2238,4 @@ struct ieee80211_ops ath9k_ops = { #endif .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, - .channel_switch_beacon = ath9k_channel_switch_beacon, }; -- cgit From c071dcb2802a0349b05817010000082c6c105453 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 23 May 2014 11:33:18 +0300 Subject: ath10k: fix protected flag stripping This prevents protected flag being stripped from undecrypted raw sniffed frames when monitor interface is active. Reported-by: Chun-Yeow Yeoh Tested-by: Chun-Yeow Yeoh Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 33 +++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index f744e355c70a..08b5718cdbf1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -778,17 +778,30 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt, struct ieee80211_rx_status *rx_status, struct sk_buff *skb, - enum htt_rx_mpdu_encrypt_type enctype) + enum htt_rx_mpdu_encrypt_type enctype, + enum rx_msdu_decap_format fmt, + bool dot11frag) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + rx_status->flag &= ~(RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); - if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) { - rx_status->flag &= ~(RX_FLAG_DECRYPTED | - RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED); + if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) + return; + + /* + * There's no explicit rx descriptor flag to indicate whether a given + * frame has been decrypted or not. We're forced to use the decap + * format as an implicit indication. However fragmentation rx is always + * raw and it probably never reports undecrypted raws. + * + * This makes sure sniffed frames are reported as-is without stripping + * the protected flag. + */ + if (fmt == RX_MSDU_DECAP_RAW && !dot11frag) return; - } rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | @@ -942,7 +955,8 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, } skb_in = skb; - ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt, + false); skb = skb->next; skb_in->next = NULL; @@ -1024,7 +1038,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, break; } - ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false); ath10k_process_rx(htt->ar, rx_status, skb); } @@ -1330,7 +1344,8 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt, + true); msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); if (tkip_mic_err) -- cgit From 0d0a693971f6d2e3ca2cf458d60463ca5479d457 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 23 May 2014 12:28:45 +0200 Subject: ath10k: relocate core create/destroy functions This will avoid unnecessary forward declaration of any kind in the future. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 128 ++++++++++++++++----------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index eca826ff393c..3b72f277ef21 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -671,70 +671,6 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } -struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - const struct ath10k_hif_ops *hif_ops) -{ - struct ath10k *ar; - - ar = ath10k_mac_create(); - if (!ar) - return NULL; - - ar->ath_common.priv = ar; - ar->ath_common.hw = ar->hw; - - ar->p2p = !!ath10k_p2p; - ar->dev = dev; - - ar->hif.priv = hif_priv; - ar->hif.ops = hif_ops; - - init_completion(&ar->scan.started); - init_completion(&ar->scan.completed); - init_completion(&ar->scan.on_channel); - init_completion(&ar->target_suspend); - - init_completion(&ar->install_key_done); - init_completion(&ar->vdev_setup_done); - - setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); - - ar->workqueue = create_singlethread_workqueue("ath10k_wq"); - if (!ar->workqueue) - goto err_wq; - - mutex_init(&ar->conf_mutex); - spin_lock_init(&ar->data_lock); - - INIT_LIST_HEAD(&ar->peers); - init_waitqueue_head(&ar->peer_mapping_wq); - - init_completion(&ar->offchan_tx_completed); - INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); - skb_queue_head_init(&ar->offchan_tx_queue); - - INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); - skb_queue_head_init(&ar->wmi_mgmt_tx_queue); - - INIT_WORK(&ar->restart_work, ath10k_core_restart); - - return ar; - -err_wq: - ath10k_mac_destroy(ar); - return NULL; -} -EXPORT_SYMBOL(ath10k_core_create); - -void ath10k_core_destroy(struct ath10k *ar) -{ - flush_workqueue(ar->workqueue); - destroy_workqueue(ar->workqueue); - - ath10k_mac_destroy(ar); -} -EXPORT_SYMBOL(ath10k_core_destroy); - int ath10k_core_start(struct ath10k *ar) { int status; @@ -1067,6 +1003,70 @@ void ath10k_core_unregister(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_unregister); +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + const struct ath10k_hif_ops *hif_ops) +{ + struct ath10k *ar; + + ar = ath10k_mac_create(); + if (!ar) + return NULL; + + ar->ath_common.priv = ar; + ar->ath_common.hw = ar->hw; + + ar->p2p = !!ath10k_p2p; + ar->dev = dev; + + ar->hif.priv = hif_priv; + ar->hif.ops = hif_ops; + + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + init_completion(&ar->scan.on_channel); + init_completion(&ar->target_suspend); + + init_completion(&ar->install_key_done); + init_completion(&ar->vdev_setup_done); + + setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + + ar->workqueue = create_singlethread_workqueue("ath10k_wq"); + if (!ar->workqueue) + goto err_wq; + + mutex_init(&ar->conf_mutex); + spin_lock_init(&ar->data_lock); + + INIT_LIST_HEAD(&ar->peers); + init_waitqueue_head(&ar->peer_mapping_wq); + + init_completion(&ar->offchan_tx_completed); + INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); + skb_queue_head_init(&ar->offchan_tx_queue); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + + INIT_WORK(&ar->restart_work, ath10k_core_restart); + + return ar; + +err_wq: + ath10k_mac_destroy(ar); + return NULL; +} +EXPORT_SYMBOL(ath10k_core_create); + +void ath10k_core_destroy(struct ath10k *ar) +{ + flush_workqueue(ar->workqueue); + destroy_workqueue(ar->workqueue); + + ath10k_mac_destroy(ar); +} +EXPORT_SYMBOL(ath10k_core_destroy); + MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); MODULE_LICENSE("Dual BSD/GPL"); -- cgit From e5a1ef5f49532e2c199d62a66b67f3abe3fc84a8 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 23 May 2014 12:28:46 +0200 Subject: ath10k: remove unnecessary tasklet_kill() The tasklet is already guaranteed to be killed on the teardown path. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 91d6076d7a71..71ab110a4c4d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2761,8 +2761,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) if (!ar_pci) return; - tasklet_kill(&ar_pci->msi_fw_err); - ath10k_core_unregister(ar); ath10k_pci_free_ce(ar); -- cgit From 6782cb696ddecdd20f22a06dd228ea8ad28a3f81 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 23 May 2014 12:28:47 +0200 Subject: ath10k: make core registering async If ath10k was built into the kernel it could stall booting for 120 seconds by default (60 seconds for each firmware API variant) waiting for firmware files before userspace was ready or filesystems mounted. Fix this by making the core registering asynchronous. This also shoves off about 1 second from boot time on most systems since the driver is now mostly initialized in a worker and modprobe takes very little time to complete. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 43 ++++++++++++++++++++++++---------- drivers/net/wireless/ath/ath10k/core.h | 2 ++ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 3b72f277ef21..5980399029b4 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -950,22 +950,15 @@ static int ath10k_core_check_chip_id(struct ath10k *ar) return 0; } -int ath10k_core_register(struct ath10k *ar, u32 chip_id) +static void ath10k_core_register_work(struct work_struct *work) { + struct ath10k *ar = container_of(work, struct ath10k, register_work); int status; - ar->chip_id = chip_id; - - status = ath10k_core_check_chip_id(ar); - if (status) { - ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); - return status; - } - status = ath10k_core_probe_fw(ar); if (status) { ath10k_err("could not probe fw (%d)\n", status); - return status; + goto err; } status = ath10k_mac_register(ar); @@ -980,18 +973,43 @@ int ath10k_core_register(struct ath10k *ar, u32 chip_id) goto err_unregister_mac; } - return 0; + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); + return; err_unregister_mac: ath10k_mac_unregister(ar); err_release_fw: ath10k_core_free_firmware_files(ar); - return status; +err: + device_release_driver(ar->dev); + return; +} + +int ath10k_core_register(struct ath10k *ar, u32 chip_id) +{ + int status; + + ar->chip_id = chip_id; + + status = ath10k_core_check_chip_id(ar); + if (status) { + ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); + return status; + } + + queue_work(ar->workqueue, &ar->register_work); + + return 0; } EXPORT_SYMBOL(ath10k_core_register); void ath10k_core_unregister(struct ath10k *ar) { + cancel_work_sync(&ar->register_work); + + if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + return; + /* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ @@ -1048,6 +1066,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + INIT_WORK(&ar->register_work, ath10k_core_register_work); INIT_WORK(&ar->restart_work, ath10k_core_restart); return ar; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 679b2a32331b..68ceef61933d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -335,6 +335,7 @@ enum ath10k_dev_flags { /* Indicates that ath10k device is during CAC phase of DFS */ ATH10K_CAC_RUNNING, ATH10K_FLAG_FIRST_BOOT_DONE, + ATH10K_FLAG_CORE_REGISTERED, }; struct ath10k { @@ -476,6 +477,7 @@ struct ath10k { enum ath10k_state state; + struct work_struct register_work; struct work_struct restart_work; /* cycle count is reported twice for each visited channel during scan. -- cgit From ae254433a8dfaaf7983b39d87ce695a616d36163 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:46:02 +0300 Subject: ath10k: clean up start() callback This fixes failpath when override AC pdev param setup fails and makes other pdev params setting fail as well. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8f311b373859..b98f88746f15 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2404,22 +2404,19 @@ static int ath10k_start(struct ieee80211_hw *hw) if (ar->state != ATH10K_STATE_OFF && ar->state != ATH10K_STATE_RESTARTING) { ret = -EINVAL; - goto exit; + goto err; } ret = ath10k_hif_power_up(ar); if (ret) { ath10k_err("Could not init hif: %d\n", ret); - ar->state = ATH10K_STATE_OFF; - goto exit; + goto err_off; } ret = ath10k_core_start(ar); if (ret) { ath10k_err("Could not init core: %d\n", ret); - ath10k_hif_power_down(ar); - ar->state = ATH10K_STATE_OFF; - goto exit; + goto err_power_down; } if (ar->state == ATH10K_STATE_OFF) @@ -2428,12 +2425,16 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_RESTARTED; ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); - if (ret) + if (ret) { ath10k_warn("failed to enable PMF QOS: %d\n", ret); + goto err_core_stop; + } ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1); - if (ret) + if (ret) { ath10k_warn("failed to enable dynamic BW: %d\n", ret); + goto err_core_stop; + } if (ar->cfg_tx_chainmask) __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, @@ -2453,14 +2454,25 @@ static int ath10k_start(struct ieee80211_hw *hw) if (ret) { ath10k_warn("failed to set arp ac override parameter: %d\n", ret); - goto exit; + goto err_core_stop; } ar->num_started_vdevs = 0; ath10k_regd_update(ar); - ret = 0; -exit: + mutex_unlock(&ar->conf_mutex); + return 0; + +err_core_stop: + ath10k_core_stop(ar); + +err_power_down: + ath10k_hif_power_down(ar); + +err_off: + ar->state = ATH10K_STATE_OFF; + +err: mutex_unlock(&ar->conf_mutex); return ret; } -- cgit From c5058f5b82f226b236dc5a65015152ed3c23efff Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:46:03 +0300 Subject: ath10k: perform hw restart lazily This reduces risk of races and prepares for more hw restart fixes. It also makes sense to perform teardown after mac80211 starts its restart routine as it guarantees it has stopped itself by then (including tx queues). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 5 ++++- drivers/net/wireless/ath/ath10k/mac.c | 34 ++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5980399029b4..82017f56e661 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -651,7 +651,8 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: ar->state = ATH10K_STATE_RESTARTING; - ath10k_halt(ar); + del_timer_sync(&ar->scan.timeout); + ath10k_reset_scan((unsigned long)ar); ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: @@ -660,6 +661,8 @@ static void ath10k_core_restart(struct work_struct *work) ath10k_warn("cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: + /* hw restart might be requested from multiple places */ + break; case ATH10K_STATE_RESTARTED: ar->state = ATH10K_STATE_WEDGED; /* fall through */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b98f88746f15..d73f146611b9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2306,6 +2306,7 @@ void ath10k_halt(struct ath10k *ar) } del_timer_sync(&ar->scan.timeout); + ath10k_reset_scan((unsigned long)ar); ath10k_offchan_tx_purge(ar); ath10k_mgmt_over_wmi_tx_purge(ar); ath10k_peer_cleanup_all(ar); @@ -2313,12 +2314,6 @@ void ath10k_halt(struct ath10k *ar) ath10k_hif_power_down(ar); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - del_timer(&ar->scan.timeout); - ar->scan.in_progress = false; - ieee80211_scan_completed(ar->hw, true); - } - list_for_each_entry(arvif, &ar->arvifs, list) { if (!arvif->beacon) continue; @@ -2401,8 +2396,18 @@ static int ath10k_start(struct ieee80211_hw *hw) mutex_lock(&ar->conf_mutex); - if (ar->state != ATH10K_STATE_OFF && - ar->state != ATH10K_STATE_RESTARTING) { + switch (ar->state) { + case ATH10K_STATE_OFF: + ar->state = ATH10K_STATE_ON; + break; + case ATH10K_STATE_RESTARTING: + ath10k_halt(ar); + ar->state = ATH10K_STATE_RESTARTED; + break; + case ATH10K_STATE_ON: + case ATH10K_STATE_RESTARTED: + case ATH10K_STATE_WEDGED: + WARN_ON(1); ret = -EINVAL; goto err; } @@ -2419,11 +2424,6 @@ static int ath10k_start(struct ieee80211_hw *hw) goto err_power_down; } - if (ar->state == ATH10K_STATE_OFF) - ar->state = ATH10K_STATE_ON; - else if (ar->state == ATH10K_STATE_RESTARTING) - ar->state = ATH10K_STATE_RESTARTED; - ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); if (ret) { ath10k_warn("failed to enable PMF QOS: %d\n", ret); @@ -2482,12 +2482,10 @@ static void ath10k_stop(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); - if (ar->state == ATH10K_STATE_ON || - ar->state == ATH10K_STATE_RESTARTED || - ar->state == ATH10K_STATE_WEDGED) + if (ar->state != ATH10K_STATE_OFF) { ath10k_halt(ar); - - ar->state = ATH10K_STATE_OFF; + ar->state = ATH10K_STATE_OFF; + } mutex_unlock(&ar->conf_mutex); ath10k_mgmt_over_wmi_tx_purge(ar); -- cgit From bca7bafbe2b75ed31cc6943b81218ea0f8a13686 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:46:03 +0300 Subject: ath10k: drain tx before restarting hw This makes sure no further tx requests are submitted to HTT before driver teardown. This should prevent invalid pointer/NULL dereference on htt tx pool in ath10k_htt_tx() in some cases of heavy traffic. kvalo: remove the WARN_ON() if conf_mutex is held Reported-By: Ben Greear Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d73f146611b9..8ad03d530820 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2289,9 +2289,19 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_htt(ar, skb); } -/* - * Initialize various parameters with default vaules. - */ +/* Must not be called with conf_mutex held as workers can use that also. */ +static void ath10k_drain_tx(struct ath10k *ar) +{ + /* make sure rcu-protected mac80211 tx path itself is drained */ + synchronize_net(); + + ath10k_offchan_tx_purge(ar); + ath10k_mgmt_over_wmi_tx_purge(ar); + + cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->wmi_mgmt_tx_work); +} + void ath10k_halt(struct ath10k *ar) { struct ath10k_vif *arvif; @@ -2307,8 +2317,6 @@ void ath10k_halt(struct ath10k *ar) del_timer_sync(&ar->scan.timeout); ath10k_reset_scan((unsigned long)ar); - ath10k_offchan_tx_purge(ar); - ath10k_mgmt_over_wmi_tx_purge(ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); @@ -2394,6 +2402,13 @@ static int ath10k_start(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; int ret = 0; + /* + * This makes sense only when restarting hw. It is harmless to call + * uncoditionally. This is necessary to make sure no HTT/WMI tx + * commands will be submitted while restarting. + */ + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); switch (ar->state) { @@ -2481,6 +2496,8 @@ static void ath10k_stop(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); if (ar->state != ATH10K_STATE_OFF) { ath10k_halt(ar); @@ -2488,10 +2505,6 @@ static void ath10k_stop(struct ieee80211_hw *hw) } mutex_unlock(&ar->conf_mutex); - ath10k_mgmt_over_wmi_tx_purge(ar); - - cancel_work_sync(&ar->offchan_tx_work); - cancel_work_sync(&ar->wmi_mgmt_tx_work); cancel_work_sync(&ar->restart_work); } -- cgit From 911e6c0d8deeeaf526e67bde47bb6a40f0fd46aa Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:46:03 +0300 Subject: ath10k: protect wep tx key setup All configuration sequences should be protected with conf_mutex to avoid concurrent/conflicting requests. This should make sure that wep tx key setup is not performed while hw is restarted (at least). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8ad03d530820..f21d60fb1e1b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1891,8 +1891,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) wep_key_work); int ret, keyidx = arvif->def_wep_key_newidx; + mutex_lock(&arvif->ar->conf_mutex); + + if (arvif->ar->state != ATH10K_STATE_ON) + goto unlock; + if (arvif->def_wep_key_idx == keyidx) - return; + goto unlock; ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", arvif->vdev_id, keyidx); @@ -1905,10 +1910,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) ath10k_warn("failed to update wep key index for vdev %d: %d\n", arvif->vdev_id, ret); - return; + goto unlock; } arvif->def_wep_key_idx = keyidx; + +unlock: + mutex_unlock(&arvif->ar->conf_mutex); } static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) -- cgit From 7b161a7034378d7b5b19a5333b584781dc6ab727 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:46:03 +0300 Subject: ath10k: dont configure bssid for ap mode FW creates self-peer for AP internally. This prevents ath10k from trying to create explicit self-peer during hw recovery and thus prevents a timeout and a warning during teardown: ath10k: removing stale peer $AP_BSSID from vdev_id 0 Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f21d60fb1e1b..a21080028c54 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3025,7 +3025,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, arvif->u.ap.hidden_ssid = info->hidden_ssid; } - if (changed & BSS_CHANGED_BSSID) { + /* + * Firmware manages AP self-peer internally so make sure to not create + * it in driver. Otherwise AP self-peer deletion may timeout later. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type != NL80211_IFTYPE_AP) { if (!is_zero_ether_addr(info->bssid)) { ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d create peer %pM\n", -- cgit From 4b81d1776092125db98301b0044476478e1e8de2 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 26 May 2014 12:46:04 +0300 Subject: ath10k: ensure rx-frag ignores rssi It seems ath10k firmware gives us no way to know the rssi for rx-fragments. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 08b5718cdbf1..6c102b1312ff 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1326,6 +1326,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, } /* FIXME: implement signal strength */ + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; hdr = (struct ieee80211_hdr *)msdu_head->data; rxd = (void *)msdu_head->data - sizeof(*rxd); -- cgit From 7147a13135ee717397969d33a139204dfe793022 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:02:58 +0200 Subject: ath10k: protect src_ring state with ce_lock in tx_sg() It was possible to read invalid state of CE ring buffer indexes. This could lead to scatter-gather transfer failure in mid-way and crash firmware later by leaving garbage data on the ring. Reported-By: Avery Pennarun Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 71ab110a4c4d..b1eb9153c7f4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -762,13 +762,17 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id]; struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl; struct ath10k_ce_ring *src_ring = ce_pipe->src_ring; - unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int sw_index = src_ring->sw_index; - unsigned int write_index = src_ring->write_index; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; int err, i; spin_lock_bh(&ar_pci->ce_lock); + nentries_mask = src_ring->nentries_mask; + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) < n_items)) { err = -ENOBUFS; -- cgit From 08b8aa0931830cc4b96b47f884fe623aef5c4b84 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 26 May 2014 12:02:59 +0200 Subject: ath10k: abort incomplete scatter-gather pci tx properly This prevents leaving incomplete scatter-gather transfer on CE rings which can lead firmware to crash. Reported-By: Avery Pennarun Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 27 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/ce.h | 2 ++ drivers/net/wireless/ath/ath10k/pci.c | 17 +++++++++++------ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 1e4cad8632b5..d185dc0cd12b 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -329,6 +329,33 @@ exit: return ret; } +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_ring *src_ring = pipe->src_ring; + u32 ctrl_addr = pipe->ctrl_addr; + + lockdep_assert_held(&ar_pci->ce_lock); + + /* + * This function must be called only if there is an incomplete + * scatter-gather transfer (before index register is updated) + * that needs to be cleaned up. + */ + if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index)) + return; + + if (WARN_ON_ONCE(src_ring->write_index == + ath10k_ce_src_ring_write_index_get(ar, ctrl_addr))) + return; + + src_ring->write_index--; + src_ring->write_index &= src_ring->nentries_mask; + + src_ring->per_transfer_context[src_ring->write_index] = NULL; +} + int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_context, u32 buffer, diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index fd0bc3561e42..7a5a36fc59c1 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -160,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, unsigned int transfer_id, unsigned int flags); +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe); + void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index b1eb9153c7f4..d0004d59c97e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -765,7 +765,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, unsigned int nentries_mask; unsigned int sw_index; unsigned int write_index; - int err, i; + int err, i = 0; spin_lock_bh(&ar_pci->ce_lock); @@ -776,7 +776,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) < n_items)) { err = -ENOBUFS; - goto unlock; + goto err; } for (i = 0; i < n_items - 1; i++) { @@ -793,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, items[i].transfer_id, CE_SEND_FLAG_GATHER); if (err) - goto unlock; + goto err; } /* `i` is equal to `n_items -1` after for() */ @@ -811,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, items[i].transfer_id, 0); if (err) - goto unlock; + goto err; + + spin_unlock_bh(&ar_pci->ce_lock); + return 0; + +err: + for (; i > 0; i--) + __ath10k_ce_send_revert(ce_pipe); - err = 0; -unlock: spin_unlock_bh(&ar_pci->ce_lock); return err; } -- cgit