From a783c77076d9d06558a6d4bb07f0c1e0819c8c89 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:02 +0300 Subject: ath10k: fix teardown ordering This should fix memory corruption if HIF is tried to be restarted. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 33af4672c90..92f35f2c4f6 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1263,7 +1263,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_process_ce(ar); ath10k_pci_cleanup_ce(ar); ath10k_pci_buffer_cleanup(ar); - ath10k_pci_ce_deinit(ar); } static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, @@ -2333,6 +2332,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev) tasklet_kill(&ar_pci->msi_fw_err); ath10k_core_unregister(ar); + ath10k_pci_ce_deinit(ar); ath10k_pci_stop_intr(ar); pci_set_drvdata(pdev, NULL); -- cgit v1.2.3-70-g09d2 From 80c78c67e6660db46f0a6e60941386ce22863e24 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:03 +0300 Subject: ath10k: fix possible deadlock It was possible to have a deadlock due to inverted locking of local->iflist_mtx and ath10k->conf_mutex. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index da5c333d0d4..608e240cb56 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2581,8 +2581,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ar_iter.ar = ar; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath10k_set_rts_iter, &ar_iter); + ieee80211_iterate_active_interfaces_atomic( + hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_rts_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); return ar_iter.ret; @@ -2622,8 +2623,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) ar_iter.ar = ar; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath10k_set_frag_iter, &ar_iter); + ieee80211_iterate_active_interfaces_atomic( + hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_frag_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); return ar_iter.ret; -- cgit v1.2.3-70-g09d2 From 679c54a67141fb12fd579c6097ebfab4cecf0043 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:04 +0300 Subject: ath10k: setup rts/frag thresholds upon vdev creation mac80211 configures rts/frag thresholds per-hw not per-vif. ath10k FW expects those values to be set per-vdev (i.e. per-vif). ath10k should now respect rts/frag thresholds set before a given interface was brought up. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 608e240cb56..2bfb8fd9b28 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1756,7 +1756,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); enum wmi_sta_powersave_param param; int ret = 0; - u32 value; + u32 value, rts, frag; int bit; mutex_lock(&ar->conf_mutex); @@ -1859,6 +1859,24 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ath10k_warn("Failed to set PSPOLL count: %d\n", ret); } + rts = min_t(u32, ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX); + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + rts); + if (ret) + ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + + frag = clamp_t(u32, ar->hw->wiphy->frag_threshold, + ATH10K_FRAGMT_THRESHOLD_MIN, + ATH10K_FRAGMT_THRESHOLD_MAX); + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + frag); + if (ret) + ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ar->monitor_present = true; -- cgit v1.2.3-70-g09d2 From 671b96db55c3ef372a5b01dfeb6b445b5c7cc3d1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:05 +0300 Subject: ath10k: do not setup rts/frag thresholds for suspended interfaces mac80211 calls for rts/frag threshold hooks before any interface is brought back up again when resuming. We would set vdev parameters before given vdev is created lading to a FW crash. rts/frag thresholds will be re-set accordingly in add_interface() hook anyway. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2bfb8fd9b28..87fa3c2d938 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2600,7 +2600,7 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) mutex_lock(&ar->conf_mutex); ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_RESUME_ALL, + hw, IEEE80211_IFACE_ITER_NORMAL, ath10k_set_rts_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); @@ -2642,7 +2642,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) mutex_lock(&ar->conf_mutex); ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_RESUME_ALL, + hw, IEEE80211_IFACE_ITER_NORMAL, ath10k_set_frag_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); -- cgit v1.2.3-70-g09d2 From 3a0861fffd2238438101b830dc06b13233d1cf92 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:06 +0300 Subject: ath10k: remove ath10k_bus It serves no purpose. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 -- drivers/net/wireless/ath/ath10k/core.h | 6 ------ drivers/net/wireless/ath/ath10k/pci.c | 3 +-- 3 files changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 2b3426b1ff3..aabe16634c1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -441,7 +441,6 @@ static int ath10k_init_hw_params(struct ath10k *ar) } struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - enum ath10k_bus bus, const struct ath10k_hif_ops *hif_ops) { struct ath10k *ar; @@ -458,7 +457,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ar->hif.priv = hif_priv; ar->hif.ops = hif_ops; - ar->hif.bus = bus; ar->free_vdev_map = 0xFF; /* 8 vdevs */ diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 539336d1be4..74897703760 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -43,10 +43,6 @@ struct ath10k; -enum ath10k_bus { - ATH10K_BUS_PCI, -}; - struct ath10k_skb_cb { dma_addr_t paddr; bool is_mapped; @@ -274,7 +270,6 @@ struct ath10k { struct { void *priv; - enum ath10k_bus bus; const struct ath10k_hif_ops *ops; } hif; @@ -356,7 +351,6 @@ struct ath10k { }; struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - enum ath10k_bus bus, const struct ath10k_hif_ops *hif_ops); void ath10k_core_destroy(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 92f35f2c4f6..926a7ebae22 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2157,8 +2157,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ath10k_pci_dump_features(ar_pci); - ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI, - &ath10k_pci_hif_ops); + ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops); if (!ar) { ath10k_err("ath10k_core_create failed!\n"); ret = -EINVAL; -- cgit v1.2.3-70-g09d2 From cba4ca7553deb8273d393645a1be8b01d3761543 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:07 +0300 Subject: ath10k: fix typo in define name Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 2 +- drivers/net/wireless/ath/ath10k/pci.c | 6 +++--- drivers/net/wireless/ath/ath10k/pci.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 61a8ac70d3c..b40792900bd 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *indicator_addr; - if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); return; } diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 926a7ebae22..fe43b4105d7 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2117,7 +2117,7 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) case ATH10K_PCI_FEATURE_MSI_X: ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); break; - case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND: + case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND: ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); break; } @@ -2144,7 +2144,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, switch (pci_dev->device) { case QCA988X_1_0_DEVICE_ID: - set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features); + set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features); break; case QCA988X_2_0_DEVICE_ID: set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); @@ -2165,7 +2165,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, } /* Enable QCA988X_1.0 HW workarounds */ - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) spin_lock_init(&ar_pci->hw_v1_workaround_lock); ar_pci->ar = ar; diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index d2a055a07dc..d3a2e6cc917 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -152,7 +152,7 @@ struct service_to_pipe { enum ath10k_pci_features { ATH10K_PCI_FEATURE_MSI_X = 0, - ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, + ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1, /* keep last */ ATH10K_PCI_FEATURE_COUNT @@ -311,7 +311,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *addr = ar_pci->mem; - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { unsigned long irq_flags; spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); -- cgit v1.2.3-70-g09d2 From 09af8f85c6a1fbe9c1ffc6253a4c0e8a0d8a9033 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:08 +0300 Subject: ath10k: silent warning in IBSS mode There is no TIM IE generated in IBSS beacons by mac80211. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 7d4b7987422..1b5312d1f0c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -501,8 +501,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies, (u8 *)skb_tail_pointer(bcn) - ies); if (!ie) { - /* highly unlikely for mac80211 */ - ath10k_warn("no tim ie found;\n"); + if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS) + ath10k_warn("no tim ie found;\n"); return; } -- cgit v1.2.3-70-g09d2 From 342004bea753434fb8e0d486afa8fc72b2be4bd7 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:09 +0300 Subject: ath10k: lower print level for a message Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 74363c94939..7b5c33445dc 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -751,8 +751,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) - ath10k_warn("HTC Service %s does not allocate target credits\n", - htc_service_name(conn_req->service_id)); + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); if (!skb) { -- cgit v1.2.3-70-g09d2 From ed48b35644a08da5ee3c54570241a023abf3ea40 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:10 +0300 Subject: ath10k: provide errno if bmi read/write fails Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 1a2ef51b69d..aeae02979d4 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -105,7 +105,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &rxlen); if (ret) { - ath10k_warn("unable to read from the device\n"); + ath10k_warn("unable to read from the device (%d)\n", + ret); return ret; } @@ -149,7 +150,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, NULL, NULL); if (ret) { - ath10k_warn("unable to write to the device\n"); + ath10k_warn("unable to write to the device (%d)\n", + ret); return ret; } -- cgit v1.2.3-70-g09d2 From 7a5fe3f87c39565e4c5eb7e39638fc979c8fd98b Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:11 +0300 Subject: ath10k: change function to take struct ath10k as arg This aligns it to the argument list of other similar functions. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index fe43b4105d7..445ec78d498 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2058,9 +2058,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar) return 0; } -static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci) +static void ath10k_pci_device_reset(struct ath10k *ar) { - struct ath10k *ar = ar_pci->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *mem = ar_pci->mem; int i; u32 val; @@ -2255,7 +2255,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, * is in an unexpected state. We try to catch that here in order to * reset the Target and retry the probe. */ - ath10k_pci_device_reset(ar_pci); + ath10k_pci_device_reset(ar); ret = ath10k_pci_reset_target(ar); if (ret) -- cgit v1.2.3-70-g09d2 From e799bbffdd6e67305b057e3c13c0eed23523bdad Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:12 +0300 Subject: ath10k: rename hif callback The `set_callbacks` is a more appopriate name for the function. Let's leave `init` for something else. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hif.h | 10 +++++----- drivers/net/wireless/ath/ath10k/htc.c | 2 +- drivers/net/wireless/ath/ath10k/pci.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 73a24d44d1b..010e26528f3 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -66,8 +66,8 @@ struct ath10k_hif_ops { */ void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); - void (*init)(struct ath10k *ar, - struct ath10k_hif_cb *callbacks); + void (*set_callbacks)(struct ath10k *ar, + struct ath10k_hif_cb *callbacks); u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); }; @@ -122,10 +122,10 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar, ar->hif.ops->send_complete_check(ar, pipe_id, force); } -static inline void ath10k_hif_init(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) +static inline void ath10k_hif_set_callbacks(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) { - ar->hif.ops->init(ar, callbacks); + ar->hif.ops->set_callbacks(ar, callbacks); } static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 7b5c33445dc..f37a6e18c0c 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -987,7 +987,7 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, /* Get HIF default pipe for HTC message exchange */ ep = &htc->endpoint[ATH10K_HTC_EP_0]; - ath10k_hif_init(ar, &htc_callbacks); + ath10k_hif_set_callbacks(ar, &htc_callbacks); ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); init_completion(&htc->ctl_resp); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 445ec78d498..7da09832b9d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -744,8 +744,8 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, ath10k_ce_per_engine_service(ar, pipe); } -static void ath10k_pci_hif_post_init(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) +static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1742,7 +1742,7 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, .get_default_pipe = ath10k_pci_hif_get_default_pipe, .send_complete_check = ath10k_pci_hif_send_complete_check, - .init = ath10k_pci_hif_post_init, + .set_callbacks = ath10k_pci_hif_set_callbacks, .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, }; -- cgit v1.2.3-70-g09d2 From cd003fad17d9258efdc5dd658666731377cfebd1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:13 +0300 Subject: ath10k: embed HTC struct inside ath10k This reduces number of allocations and simplifies memory managemnt. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 28 ++++++++++++---------------- drivers/net/wireless/ath/ath10k/core.h | 2 +- drivers/net/wireless/ath/ath10k/htc.c | 23 +++++------------------ drivers/net/wireless/ath/ath10k/htc.h | 4 +--- drivers/net/wireless/ath/ath10k/htt.c | 2 +- drivers/net/wireless/ath/ath10k/htt_tx.c | 10 +++++----- drivers/net/wireless/ath/ath10k/wmi.c | 4 ++-- 7 files changed, 27 insertions(+), 46 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index aabe16634c1..4199560b469 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) goto conn_fail; /* Start HTC */ - status = ath10k_htc_start(ar->htc); + status = ath10k_htc_start(&ar->htc); if (status) goto conn_fail; @@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) return 0; timeout: - ath10k_htc_stop(ar->htc); + ath10k_htc_stop(&ar->htc); conn_fail: return status; } @@ -505,7 +505,6 @@ EXPORT_SYMBOL(ath10k_core_destroy); int ath10k_core_register(struct ath10k *ar) { - struct ath10k_htc_ops htc_ops; struct bmi_target_info target_info; int status; @@ -534,26 +533,26 @@ int ath10k_core_register(struct ath10k *ar) if (status) goto err; - htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; + ar->htc.htc_ops.target_send_suspend_complete = + ath10k_send_suspend_complete; - ar->htc = ath10k_htc_create(ar, &htc_ops); - if (IS_ERR(ar->htc)) { - status = PTR_ERR(ar->htc); - ath10k_err("could not create HTC (%d)\n", status); + status = ath10k_htc_init(ar); + if (status) { + ath10k_err("could not init HTC (%d)\n", status); goto err; } status = ath10k_bmi_done(ar); if (status) - goto err_htc_destroy; + goto err; status = ath10k_wmi_attach(ar); if (status) { ath10k_err("WMI attach failed: %d\n", status); - goto err_htc_destroy; + goto err; } - status = ath10k_htc_wait_target(ar->htc); + status = ath10k_htc_wait_target(&ar->htc); if (status) goto err_wmi_detach; @@ -605,13 +604,11 @@ int ath10k_core_register(struct ath10k *ar) err_unregister_mac: ath10k_mac_unregister(ar); err_disconnect_htc: - ath10k_htc_stop(ar->htc); + ath10k_htc_stop(&ar->htc); err_htt_detach: ath10k_htt_detach(ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); -err_htc_destroy: - ath10k_htc_destroy(ar->htc); err: return status; } @@ -623,10 +620,9 @@ void ath10k_core_unregister(struct ath10k *ar) * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); - ath10k_htc_stop(ar->htc); + ath10k_htc_stop(&ar->htc); ath10k_htt_detach(ar->htt); ath10k_wmi_detach(ar); - ath10k_htc_destroy(ar->htc); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 74897703760..dfd4fe1e82f 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -279,8 +279,8 @@ struct ath10k { bool is_target_paused; struct ath10k_bmi bmi; + struct ath10k_htc htc; - struct ath10k_htc *htc; struct ath10k_htt *htt; struct ath10k_hw_params { diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index f37a6e18c0c..7d5a36616b7 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -265,7 +265,7 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb, unsigned int eid) { - struct ath10k_htc *htc = ar->htc; + struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_ep *ep = &htc->endpoint[eid]; bool stopping; @@ -414,7 +414,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, u8 pipe_id) { int status = 0; - struct ath10k_htc *htc = ar->htc; + struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_hdr *hdr; struct ath10k_htc_ep *ep; u16 payload_len; @@ -961,22 +961,14 @@ void ath10k_htc_stop(struct ath10k_htc *htc) } /* registered target arrival callback from the HIF layer */ -struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, - struct ath10k_htc_ops *htc_ops) +int ath10k_htc_init(struct ath10k *ar) { struct ath10k_hif_cb htc_callbacks; struct ath10k_htc_ep *ep = NULL; - struct ath10k_htc *htc = NULL; - - /* FIXME: use struct ath10k instead */ - htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL); - if (!htc) - return ERR_PTR(-ENOMEM); + struct ath10k_htc *htc = &ar->htc; spin_lock_init(&htc->tx_lock); - memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops)); - ath10k_htc_reset_endpoint_states(htc); /* setup HIF layer callbacks */ @@ -992,10 +984,5 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, init_completion(&htc->ctl_resp); - return htc; -} - -void ath10k_htc_destroy(struct ath10k_htc *htc) -{ - kfree(htc); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index fa45844b59f..1606c9f9dff 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -352,8 +352,7 @@ struct ath10k_htc { bool stopping; }; -struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, - struct ath10k_htc_ops *htc_ops); +int ath10k_htc_init(struct ath10k *ar); int ath10k_htc_wait_target(struct ath10k_htc *htc); int ath10k_htc_start(struct ath10k_htc *htc); int ath10k_htc_connect_service(struct ath10k_htc *htc, @@ -362,7 +361,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *packet); void ath10k_htc_stop(struct ath10k_htc *htc); -void ath10k_htc_destroy(struct ath10k_htc *htc); struct sk_buff *ath10k_htc_alloc_skb(int size); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 185a5468a2f..2bfb9b488e0 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -36,7 +36,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG; - status = ath10k_htc_connect_service(htt->ar->htc, &conn_req, + status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req, &conn_resp); if (status) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ef79106db24..fa568f10e9d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) /* At the beginning free queue number should hint us the maximum * queue length */ - pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id; + pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id; htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, pipe); @@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; @@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; @@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.refcount = 2; skb_cb->htt.msdu = msdu; - res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) goto err; @@ -486,7 +486,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.txfrag = txfrag; skb_cb->htt.msdu = msdu; - res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) goto err; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 1b5312d1f0c..681e941073c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -111,7 +111,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); - status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb); + status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); if (status) { dev_kfree_skb_any(skb); atomic_dec(&ar->wmi.pending_tx_count); @@ -1114,7 +1114,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; - status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp); + status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); if (status) { ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", status); -- cgit v1.2.3-70-g09d2 From edb8236df4d0429d973e093062a4806471f5efa2 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:14 +0300 Subject: ath10k: embed HTT struct inside ath10k This reduces number of allocations and simplifies memory managemnt. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 12 ++++++------ drivers/net/wireless/ath/ath10k/core.h | 7 +++---- drivers/net/wireless/ath/ath10k/htt.c | 25 +++++++++++++------------ drivers/net/wireless/ath/ath10k/htt.h | 3 +-- drivers/net/wireless/ath/ath10k/htt_rx.c | 3 ++- drivers/net/wireless/ath/ath10k/htt_tx.c | 2 +- drivers/net/wireless/ath/ath10k/mac.c | 16 ++++++++-------- 7 files changed, 34 insertions(+), 34 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4199560b469..f1312fae805 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -556,9 +556,9 @@ int ath10k_core_register(struct ath10k *ar) if (status) goto err_wmi_detach; - ar->htt = ath10k_htt_attach(ar); - if (!ar->htt) { - status = -ENOMEM; + status = ath10k_htt_attach(ar); + if (status) { + ath10k_err("could not attach htt (%d)\n", status); goto err_wmi_detach; } @@ -585,7 +585,7 @@ int ath10k_core_register(struct ath10k *ar) goto err_disconnect_htc; } - status = ath10k_htt_attach_target(ar->htt); + status = ath10k_htt_attach_target(&ar->htt); if (status) goto err_disconnect_htc; @@ -606,7 +606,7 @@ err_unregister_mac: err_disconnect_htc: ath10k_htc_stop(&ar->htc); err_htt_detach: - ath10k_htt_detach(ar->htt); + ath10k_htt_detach(&ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); err: @@ -621,7 +621,7 @@ void ath10k_core_unregister(struct ath10k *ar) * unhappy about callback failures. */ ath10k_mac_unregister(ar); ath10k_htc_stop(&ar->htc); - ath10k_htt_detach(ar->htt); + ath10k_htt_detach(&ar->htt); ath10k_wmi_detach(ar); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index dfd4fe1e82f..2f7065c48f5 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -23,6 +23,7 @@ #include #include +#include "htt.h" #include "htc.h" #include "hw.h" #include "targaddrs.h" @@ -273,15 +274,13 @@ struct ath10k { const struct ath10k_hif_ops *ops; } hif; - struct ath10k_wmi wmi; - wait_queue_head_t event_queue; bool is_target_paused; struct ath10k_bmi bmi; + struct ath10k_wmi wmi; struct ath10k_htc htc; - - struct ath10k_htt *htt; + struct ath10k_htt htt; struct ath10k_hw_params { u32 id; diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 2bfb9b488e0..39342c5cfcb 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -16,6 +16,7 @@ */ #include +#include #include "htt.h" #include "core.h" @@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) return 0; } -struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) +int ath10k_htt_attach(struct ath10k *ar) { - struct ath10k_htt *htt; + struct ath10k_htt *htt = &ar->htt; int ret; - htt = kzalloc(sizeof(*htt), GFP_KERNEL); - if (!htt) - return NULL; - htt->ar = ar; htt->max_throughput_mbps = 800; @@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) * since ath10k_htt_rx_attach involves sending a rx ring configure * message to the target. */ - if (ath10k_htt_htc_attach(htt)) + 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) { @@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) goto err_htc_attach; } - if (ath10k_htt_rx_attach(htt)) + 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 @@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) 8 + /* llc snap */ 2; /* ip4 dscp or ip6 priority */ - return htt; + return 0; err_rx_attach: ath10k_htt_tx_detach(htt); err_htc_attach: - kfree(htt); - return NULL; + return ret; } #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) @@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt) { ath10k_htt_rx_detach(htt); ath10k_htt_tx_detach(htt); - kfree(htt); } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index a7a7aa04053..318be4629cd 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -20,7 +20,6 @@ #include -#include "core.h" #include "htc.h" #include "rx_desc.h" @@ -1317,7 +1316,7 @@ 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) -struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar); +int ath10k_htt_attach(struct ath10k *ar); int ath10k_htt_attach_target(struct ath10k_htt *htt); void ath10k_htt_detach(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 de058d7adca..04f08d94647 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -15,6 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "core.h" #include "htc.h" #include "htt.h" #include "txrx.h" @@ -1036,7 +1037,7 @@ end: void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k_htt *htt = ar->htt; + struct ath10k_htt *htt = &ar->htt; struct htt_resp *resp = (struct htt_resp *)skb->data; /* confirm alignment */ diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index fa568f10e9d..dc3f3e8de32 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt) void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - struct ath10k_htt *htt = ar->htt; + struct ath10k_htt *htt = &ar->htt; if (skb_cb->htt.is_conf) { dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 87fa3c2d938..f91701c6963 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1397,15 +1397,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) int ret; if (ieee80211_is_mgmt(hdr->frame_control)) - ret = ath10k_htt_mgmt_tx(ar->htt, skb); + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); else if (ieee80211_is_nullfunc(hdr->frame_control)) /* FW does not report tx status properly for NullFunc frames * unless they are sent through mgmt tx path. mac80211 sends * those frames when it detects link/beacon loss and depends on * the tx status to be correct. */ - ret = ath10k_htt_mgmt_tx(ar->htt, skb); + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); else - ret = ath10k_htt_tx(ar->htt, skb); + ret = ath10k_htt_tx(&ar->htt, skb); if (ret) { ath10k_warn("tx failed (%d). dropping packet.\n", ret); @@ -2659,12 +2659,12 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) if (drop) return; - ret = wait_event_timeout(ar->htt->empty_tx_wq, ({ + ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ bool empty; - spin_lock_bh(&ar->htt->tx_lock); - empty = bitmap_empty(ar->htt->used_msdu_ids, - ar->htt->max_num_pending_tx); - spin_unlock_bh(&ar->htt->tx_lock); + spin_lock_bh(&ar->htt.tx_lock); + empty = bitmap_empty(ar->htt.used_msdu_ids, + ar->htt.max_num_pending_tx); + spin_unlock_bh(&ar->htt.tx_lock); (empty); }), ATH10K_FLUSH_TIMEOUT_HZ); if (ret <= 0) -- cgit v1.2.3-70-g09d2 From 548db54cc1890b161e87d4ca1028cf77f51fd16c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:15 +0300 Subject: ath10k: improve locking Add more lockdep asserts and a few conf_mutex locks. It's better to be on the safe side. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 57 +++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f91701c6963..278c9689ce7 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -43,6 +43,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif, .macaddr = macaddr, }; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) arg.key_flags = WMI_KEY_PAIRWISE; else @@ -87,6 +89,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif, struct ath10k *ar = arvif->ar; int ret; + lockdep_assert_held(&ar->conf_mutex); + INIT_COMPLETION(ar->install_key_done); ret = ath10k_send_key(arvif, key, cmd, macaddr); @@ -372,6 +376,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) { int ret; + lockdep_assert_held(&ar->conf_mutex); + ret = wait_for_completion_timeout(&ar->vdev_setup_done, ATH10K_VDEV_SETUP_TIMEOUT_HZ); if (ret == 0) @@ -605,6 +611,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (!info->enable_beacon) { ath10k_vdev_stop(arvif); return; @@ -631,6 +639,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (!info->ibss_joined) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); if (ret) @@ -680,6 +690,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) enum wmi_sta_ps_mode psmode; int ret; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (vif->type != NL80211_IFTYPE_STATION) return; @@ -722,6 +734,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar, struct ieee80211_bss_conf *bss_conf, struct wmi_peer_assoc_complete_arg *arg) { + lockdep_assert_held(&ar->conf_mutex); + memcpy(arg->addr, sta->addr, ETH_ALEN); arg->vdev_id = arvif->vdev_id; arg->peer_aid = sta->aid; @@ -764,6 +778,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, const u8 *rsnie = NULL; const u8 *wpaie = NULL; + lockdep_assert_held(&ar->conf_mutex); + bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, info->bssid, NULL, 0, 0, 0); if (bss) { @@ -804,6 +820,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar, u32 ratemask; int i; + lockdep_assert_held(&ar->conf_mutex); + sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band]; ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band]; rates = sband->bitrates; @@ -827,6 +845,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, int smps; int i, n; + lockdep_assert_held(&ar->conf_mutex); + if (!ht_cap->ht_supported) return; @@ -905,6 +925,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, u32 uapsd = 0; u32 max_sp = 0; + lockdep_assert_held(&ar->conf_mutex); + if (sta->wme) arg->peer_flags |= WMI_PEER_QOS; @@ -1056,6 +1078,8 @@ static int ath10k_peer_assoc(struct ath10k *ar, { struct wmi_peer_assoc_complete_arg arg; + lockdep_assert_held(&ar->conf_mutex); + memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); @@ -1079,6 +1103,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_sta *ap_sta; int ret; + lockdep_assert_held(&ar->conf_mutex); + rcu_read_lock(); ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); @@ -1119,6 +1145,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret; + lockdep_assert_held(&ar->conf_mutex); + /* * For some reason, calling VDEV-DOWN before VDEV-STOP * makes the FW to send frames via HTT after disassociation. @@ -1152,6 +1180,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + ret = ath10k_peer_assoc(ar, arvif, sta, NULL); if (ret) { ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); @@ -1172,6 +1202,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) { ath10k_warn("could not clear all peer wep keys (%d)\n", ret); @@ -1198,6 +1230,8 @@ static int ath10k_update_channel_list(struct ath10k *ar) int ret; int i; + lockdep_assert_held(&ar->conf_mutex); + bands = hw->wiphy->bands; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!bands[band]) @@ -1284,6 +1318,8 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, struct ath10k *ar = hw->priv; int ret; + mutex_lock(&ar->conf_mutex); + ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); ret = ath10k_update_channel_list(ar); @@ -1301,6 +1337,8 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, regpair->reg_5ghz_ctl); if (ret) ath10k_warn("could not set pdev regdomain (%d)\n", ret); + + mutex_unlock(&ar->conf_mutex); } /***************/ @@ -1678,6 +1716,8 @@ static int ath10k_start(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; int ret; + mutex_lock(&ar->conf_mutex); + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); if (ret) ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", @@ -1688,6 +1728,7 @@ static int ath10k_start(struct ieee80211_hw *hw) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); + mutex_unlock(&ar->conf_mutex); return 0; } @@ -1695,9 +1736,11 @@ static void ath10k_stop(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; - /* avoid leaks in case FW never confirms scan for offchannel */ - cancel_work_sync(&ar->offchan_tx_work); + mutex_lock(&ar->conf_mutex); ath10k_offchan_tx_purge(ar); + mutex_unlock(&ar->conf_mutex); + + cancel_work_sync(&ar->offchan_tx_work); } static int ath10k_config(struct ieee80211_hw *hw, u32 changed) @@ -2381,6 +2424,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, u32 value = 0; int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) return 0; @@ -2576,6 +2621,8 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; + lockdep_assert_held(&arvif->ar->conf_mutex); + rts = min_t(u32, rts, ATH10K_RTS_MAX); ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, @@ -2614,6 +2661,8 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; int ret; + lockdep_assert_held(&arvif->ar->conf_mutex); + frag = clamp_t(u32, frag, ATH10K_FRAGMT_THRESHOLD_MIN, ATH10K_FRAGMT_THRESHOLD_MAX); @@ -2659,6 +2708,8 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) if (drop) return; + mutex_lock(&ar->conf_mutex); + ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ bool empty; spin_lock_bh(&ar->htt.tx_lock); @@ -2669,6 +2720,8 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) }), ATH10K_FLUSH_TIMEOUT_HZ); if (ret <= 0) ath10k_warn("tx not flushed\n"); + + mutex_unlock(&ar->conf_mutex); } /* TODO: Implement this function properly -- cgit v1.2.3-70-g09d2 From adb8c9b77cd182b1b3f2b145b8db4043623e3745 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:16 +0300 Subject: ath10k: abort scan properly if wmi_scan_stop fails Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 278c9689ce7..50825030bb1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1590,6 +1590,10 @@ static int ath10k_abort_scan(struct ath10k *ar) ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) { ath10k_warn("could not submit wmi stop scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + ath10k_offchan_tx_purge(ar); + spin_unlock_bh(&ar->data_lock); return -EIO; } -- cgit v1.2.3-70-g09d2 From e0c508ab099bda4629768107d14bbe9afa5c6705 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 5 Jul 2013 16:15:17 +0300 Subject: ath10k: add missing debug prints Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 681e941073c..b7e7e456b5d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1748,6 +1748,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, if (arg->key_data) memcpy(cmd->key_data, arg->key_data, arg->key_len); + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev install key idx %d cipher %d len %d\n", + arg->key_idx, arg->key_cipher, arg->key_len); return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); } @@ -2011,6 +2014,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, cmd->peer_vht_rates.tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM\n", + arg->vdev_id, arg->addr); return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); } -- cgit v1.2.3-70-g09d2 From 8c5c53682f0da87b91ff87d060a5d92df524c13d Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:50 +0200 Subject: ath10k: decouple pci start/stop logic Split logic that prepares the device for BMI phase/cleans up related resources. This is necessary for ath10k to be able to restart hw on the fly without reloading the module. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hif.h | 20 +++++++ drivers/net/wireless/ath/ath10k/pci.c | 110 ++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 39 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 010e26528f3..00d29402fa1 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -46,8 +46,11 @@ struct ath10k_hif_ops { void *request, u32 request_len, void *response, u32 *response_len); + /* Post BMI phase, after FW is loaded. Starts regular operation */ int (*start)(struct ath10k *ar); + /* Clean up what start() did. This does not revert to BMI phase. If + * desired so, call power_down() and power_up() */ void (*stop)(struct ath10k *ar); int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, @@ -70,6 +73,13 @@ struct ath10k_hif_ops { struct ath10k_hif_cb *callbacks); u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); + + /* Power up the device and enter BMI transfer mode for FW download */ + int (*power_up)(struct ath10k *ar); + + /* Power down the device and free up resources. stop() must be called + * before this if start() was called earlier */ + void (*power_down)(struct ath10k *ar); }; @@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, return ar->hif.ops->get_free_queue_number(ar, pipe_id); } +static inline int ath10k_hif_power_up(struct ath10k *ar) +{ + return ar->hif.ops->power_up(ar); +} + +static inline void ath10k_hif_power_down(struct ath10k *ar) +{ + ar->hif.ops->power_down(ar); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7da09832b9d..c15b0c58cc8 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, int num); static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); +static void ath10k_pci_device_reset(struct ath10k *ar); +static int ath10k_pci_reset_target(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* host->target HTC control and raw streams */ @@ -1734,6 +1736,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } +static int ath10k_pci_hif_power_up(struct ath10k *ar) +{ + int ret; + + /* + * Bring the target up cleanly. + * + * The target may be in an undefined state with an AUX-powered Target + * and a Host in WoW mode. If the Host crashes, loses power, or is + * restarted (without unloading the driver) then the Target is left + * (aux) powered and running. On a subsequent driver load, the Target + * is in an unexpected state. We try to catch that here in order to + * reset the Target and retry the probe. + */ + ath10k_pci_device_reset(ar); + + ret = ath10k_pci_reset_target(ar); + if (ret) + goto err; + + if (ath10k_target_ps) { + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n"); + } else { + /* Force AWAKE forever */ + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n"); + ath10k_do_pci_wake(ar); + } + + ret = ath10k_pci_ce_init(ar); + if (ret) + goto err_ps; + + ret = ath10k_pci_init_config(ar); + if (ret) + goto err_ce; + + ret = ath10k_pci_wake_target_cpu(ar); + if (ret) { + ath10k_err("could not wake up target CPU (%d)\n", ret); + goto err_ce; + } + + return 0; + +err_ce: + ath10k_pci_ce_deinit(ar); +err_ps: + if (!ath10k_target_ps) + ath10k_do_pci_sleep(ar); +err: + return ret; +} + +static void ath10k_pci_hif_power_down(struct ath10k *ar) +{ + ath10k_pci_ce_deinit(ar); + if (!ath10k_target_ps) + ath10k_do_pci_sleep(ar); +} + static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .send_head = ath10k_pci_hif_send_head, .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, @@ -1744,6 +1806,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .send_complete_check = ath10k_pci_hif_send_complete_check, .set_callbacks = ath10k_pci_hif_set_callbacks, .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, + .power_up = ath10k_pci_hif_power_up, + .power_down = ath10k_pci_hif_power_down, }; static void ath10k_pci_ce_tasklet(unsigned long ptr) @@ -2245,54 +2309,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_iomap; } - /* - * Bring the target up cleanly. - * - * The target may be in an undefined state with an AUX-powered Target - * and a Host in WoW mode. If the Host crashes, loses power, or is - * restarted (without unloading the driver) then the Target is left - * (aux) powered and running. On a subsequent driver load, the Target - * is in an unexpected state. We try to catch that here in order to - * reset the Target and retry the probe. - */ - ath10k_pci_device_reset(ar); - - ret = ath10k_pci_reset_target(ar); - if (ret) - goto err_intr; - - if (ath10k_target_ps) { - ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n"); - } else { - /* Force AWAKE forever */ - ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n"); - ath10k_do_pci_wake(ar); - } - - ret = ath10k_pci_ce_init(ar); - if (ret) - goto err_intr; - - ret = ath10k_pci_init_config(ar); - if (ret) - goto err_ce; - - ret = ath10k_pci_wake_target_cpu(ar); + ret = ath10k_pci_hif_power_up(ar); if (ret) { - ath10k_err("could not wake up target CPU (%d)\n", ret); - goto err_ce; + ath10k_err("could not start pci hif (%d)\n", ret); + goto err_intr; } ret = ath10k_core_register(ar); if (ret) { ath10k_err("could not register driver core (%d)\n", ret); - goto err_ce; + goto err_hif; } return 0; -err_ce: - ath10k_pci_ce_deinit(ar); +err_hif: + ath10k_pci_hif_power_down(ar); err_intr: ath10k_pci_stop_intr(ar); err_iomap: @@ -2331,7 +2363,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev) tasklet_kill(&ar_pci->msi_fw_err); ath10k_core_unregister(ar); - ath10k_pci_ce_deinit(ar); + ath10k_pci_hif_power_down(ar); ath10k_pci_stop_intr(ar); pci_set_drvdata(pdev, NULL); -- cgit v1.2.3-70-g09d2 From dd30a36e11a1315751c668832cbaa2c42f9e9002 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:51 +0200 Subject: ath10k: decouple core start/stop logic Enables code reuse for proper hw reconfiguration that is in turn required for proper suspend/hibernation/wowlan support. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 44 ++++++++++++++++++++++++---------- drivers/net/wireless/ath/ath10k/core.h | 2 ++ 2 files changed, 34 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index f1312fae805..dcddae4d5d9 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_destroy); - -int ath10k_core_register(struct ath10k *ar) +int ath10k_core_start(struct ath10k *ar) { struct bmi_target_info target_info; int status; @@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar) if (status) goto err_disconnect_htc; + return 0; + +err_disconnect_htc: + ath10k_htc_stop(&ar->htc); +err_htt_detach: + ath10k_htt_detach(&ar->htt); +err_wmi_detach: + ath10k_wmi_detach(ar); +err: + return status; +} + +void ath10k_core_stop(struct ath10k *ar) +{ + ath10k_htc_stop(&ar->htc); + ath10k_htt_detach(&ar->htt); + ath10k_wmi_detach(ar); +} + +int ath10k_core_register(struct ath10k *ar) +{ + int status; + + status = ath10k_core_start(ar); + if (status) + goto err; + status = ath10k_mac_register(ar); if (status) - goto err_disconnect_htc; + goto err_core_stop; status = ath10k_debug_create(ar); if (status) { @@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar) err_unregister_mac: ath10k_mac_unregister(ar); -err_disconnect_htc: - ath10k_htc_stop(&ar->htc); -err_htt_detach: - ath10k_htt_detach(&ar->htt); -err_wmi_detach: - ath10k_wmi_detach(ar); +err_core_stop: + ath10k_core_stop(ar); err: return status; } @@ -620,9 +642,7 @@ void ath10k_core_unregister(struct ath10k *ar) * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); - ath10k_htc_stop(&ar->htc); - ath10k_htt_detach(&ar->htt); - ath10k_wmi_detach(ar); + ath10k_core_stop(ar); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2f7065c48f5..5a0b2cef7d9 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, const struct ath10k_hif_ops *hif_ops); void ath10k_core_destroy(struct ath10k *ar); +int ath10k_core_start(struct ath10k *ar); +void ath10k_core_stop(struct ath10k *ar); int ath10k_core_register(struct ath10k *ar); void ath10k_core_unregister(struct ath10k *ar); -- cgit v1.2.3-70-g09d2 From f7843d7f1ac57fb388d5814cdcbd309fa18214be Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:52 +0200 Subject: ath10k: allow deferred regd update Regulatory domain notification hook can be called regardless of the hw state (i.e. before start mac80211 callback). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 7 +++++++ drivers/net/wireless/ath/ath10k/mac.c | 26 +++++++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 5a0b2cef7d9..d5b2533d33a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -247,6 +247,11 @@ struct ath10k_debug { struct completion event_stats_compl; }; +enum ath10k_state { + ATH10K_STATE_OFF = 0, + ATH10K_STATE_ON, +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -344,6 +349,8 @@ struct ath10k { struct completion offchan_tx_completed; struct sk_buff *offchan_tx_skb; + enum ath10k_state state; + #ifdef CONFIG_ATH10K_DEBUGFS struct ath10k_debug debug; #endif diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 50825030bb1..4627c16d2d0 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1310,23 +1310,19 @@ static int ath10k_update_channel_list(struct ath10k *ar) return ret; } -static void ath10k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void ath10k_regd_update(struct ath10k *ar) { - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct reg_dmn_pair_mapping *regpair; - struct ath10k *ar = hw->priv; int ret; - mutex_lock(&ar->conf_mutex); - - ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + lockdep_assert_held(&ar->conf_mutex); ret = ath10k_update_channel_list(ar); if (ret) ath10k_warn("could not update channel list (%d)\n", ret); regpair = ar->ath_common.regulatory.regpair; + /* Target allows setting up per-band regdomain but ath_common provides * a combined one only */ ret = ath10k_wmi_pdev_set_regdomain(ar, @@ -1337,7 +1333,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, regpair->reg_5ghz_ctl); if (ret) ath10k_warn("could not set pdev regdomain (%d)\n", ret); +} +static void ath10k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath10k *ar = hw->priv; + + ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + + mutex_lock(&ar->conf_mutex); + if (ar->state == ATH10K_STATE_ON) + ath10k_regd_update(ar); mutex_unlock(&ar->conf_mutex); } @@ -1732,6 +1740,9 @@ static int ath10k_start(struct ieee80211_hw *hw) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); + ar->state = ATH10K_STATE_ON; + ath10k_regd_update(ar); + mutex_unlock(&ar->conf_mutex); return 0; } @@ -1742,6 +1753,7 @@ static void ath10k_stop(struct ieee80211_hw *hw) mutex_lock(&ar->conf_mutex); ath10k_offchan_tx_purge(ar); + ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); cancel_work_sync(&ar->offchan_tx_work); -- cgit v1.2.3-70-g09d2 From 64d151d47030d0d73d82bb6fa7bfe1e29385ed43 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:53 +0200 Subject: ath10k: reset BMI state upon init This is necessary if we want to be able to restart hw on-the-fly. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 6 ++++++ drivers/net/wireless/ath/ath10k/bmi.h | 1 + drivers/net/wireless/ath/ath10k/core.c | 2 ++ 3 files changed, 9 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index aeae02979d4..744da6d1c40 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -20,6 +20,12 @@ #include "debug.h" #include "htc.h" +void ath10k_bmi_start(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n"); + ar->bmi.done_sent = false; +} + int ath10k_bmi_done(struct ath10k *ar) { struct bmi_cmd cmd; diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 32c56aa33a5..8d81ce1cec2 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -184,6 +184,7 @@ struct bmi_target_info { #define BMI_CE_NUM_TO_TARG 0 #define BMI_CE_NUM_TO_HOST 1 +void ath10k_bmi_start(struct ath10k *ar); int ath10k_bmi_done(struct ath10k *ar); int ath10k_bmi_get_target_info(struct ath10k *ar, struct bmi_target_info *target_info); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index dcddae4d5d9..3d75c6a74fc 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar) struct bmi_target_info target_info; int status; + ath10k_bmi_start(ar); + memset(&target_info, 0, sizeof(target_info)); status = ath10k_bmi_get_target_info(ar, &target_info); if (status) -- cgit v1.2.3-70-g09d2 From 8cd13cad1caf94ba66f626a94887b795fe23f939 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:54 +0200 Subject: ath10k: decouple suspend code Split up fw-related and hw-related suspension code. Although we don't advertise WoW support to mac80211 yet it's useful to keep the code in suspend/resume hooks. At this point there's no need to keep pci pm ops. In case of WoW mac80211 calls ath10k_suspend() which should take care of entering low-power mode. In case WoW is not available mac80211 will go through regular interface teradown and use start/stop. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 28 ------ drivers/net/wireless/ath/ath10k/core.h | 3 - drivers/net/wireless/ath/ath10k/hif.h | 19 ++++ drivers/net/wireless/ath/ath10k/mac.c | 66 +++++++++++++ drivers/net/wireless/ath/ath10k/pci.c | 176 ++++++++++----------------------- 5 files changed, 138 insertions(+), 154 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 3d75c6a74fc..01c1d82f674 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -648,34 +648,6 @@ void ath10k_core_unregister(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_unregister); -int ath10k_core_target_suspend(struct ath10k *ar) -{ - int ret; - - ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); - - ret = ath10k_wmi_pdev_suspend_target(ar); - if (ret) - ath10k_warn("could not suspend target (%d)\n", ret); - - return ret; -} -EXPORT_SYMBOL(ath10k_core_target_suspend); - -int ath10k_core_target_resume(struct ath10k *ar) -{ - int ret; - - ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); - - ret = ath10k_wmi_pdev_resume_target(ar); - if (ret) - ath10k_warn("could not resume target (%d)\n", ret); - - return ret; -} -EXPORT_SYMBOL(ath10k_core_target_resume); - MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index d5b2533d33a..5c94ea06eb7 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -365,7 +365,4 @@ void ath10k_core_stop(struct ath10k *ar); int ath10k_core_register(struct ath10k *ar); void ath10k_core_unregister(struct ath10k *ar); -int ath10k_core_target_suspend(struct ath10k *ar); -int ath10k_core_target_resume(struct ath10k *ar); - #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 00d29402fa1..dcdea68bcc0 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -80,6 +80,9 @@ struct ath10k_hif_ops { /* Power down the device and free up resources. stop() must be called * before this if start() was called earlier */ void (*power_down)(struct ath10k *ar); + + int (*suspend)(struct ath10k *ar); + int (*resume)(struct ath10k *ar); }; @@ -154,4 +157,20 @@ static inline void ath10k_hif_power_down(struct ath10k *ar) ar->hif.ops->power_down(ar); } +static inline int ath10k_hif_suspend(struct ath10k *ar) +{ + if (!ar->hif.ops->suspend) + return -EOPNOTSUPP; + + return ar->hif.ops->suspend(ar); +} + +static inline int ath10k_hif_resume(struct ath10k *ar) +{ + if (!ar->hif.ops->resume) + return -EOPNOTSUPP; + + return ar->hif.ops->resume(ar); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 4627c16d2d0..febba7d8ca4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -20,6 +20,7 @@ #include #include +#include "hif.h" #include "core.h" #include "debug.h" #include "wmi.h" @@ -2749,6 +2750,67 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) return 1; } +#ifdef CONFIG_PM +static int ath10k_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct ath10k *ar = hw->priv; + int ret; + + ar->is_target_paused = false; + + ret = ath10k_wmi_pdev_suspend_target(ar); + if (ret) { + ath10k_warn("could not suspend target (%d)\n", ret); + return 1; + } + + ret = wait_event_interruptible_timeout(ar->event_queue, + ar->is_target_paused == true, + 1 * HZ); + if (ret < 0) { + ath10k_warn("suspend interrupted (%d)\n", ret); + goto resume; + } else if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + goto resume; + } + + ret = ath10k_hif_suspend(ar); + if (ret) { + ath10k_warn("could not suspend hif (%d)\n", ret); + goto resume; + } + + return 0; +resume: + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) + ath10k_warn("could not resume target (%d)\n", ret); + return 1; +} + +static int ath10k_resume(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + int ret; + + ret = ath10k_hif_resume(ar); + if (ret) { + ath10k_warn("could not resume hif (%d)\n", ret); + return 1; + } + + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) { + ath10k_warn("could not resume target (%d)\n", ret); + return 1; + } + + return 0; +} +#endif + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -2769,6 +2831,10 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, +#ifdef CONFIG_PM + .suspend = ath10k_suspend, + .resume = ath10k_resume, +#endif }; #define RATETAB_ENT(_rate, _rateid, _flags) { \ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index c15b0c58cc8..75f5427bb41 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1796,6 +1796,55 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) ath10k_do_pci_sleep(ar); } +#ifdef CONFIG_PM + +#define ATH10K_PCI_PM_CONTROL 0x44 + +static int ath10k_pci_hif_suspend(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + u32 val; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0x3) { + pci_save_state(pdev); + pci_disable_device(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + (val & 0xffffff00) | 0x03); + } + + return 0; +} + +static int ath10k_pci_hif_resume(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + u32 val; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0) { + pci_restore_state(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + val & 0xffffff00); + /* + * Suspend/Resume resets the PCI configuration space, + * so we have to re-disable the RETRY_TIMEOUT register (0x41) + * to keep PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + } + + return 0; +} +#endif + static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .send_head = ath10k_pci_hif_send_head, .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, @@ -1808,6 +1857,10 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, .power_up = ath10k_pci_hif_power_up, .power_down = ath10k_pci_hif_power_down, +#ifdef CONFIG_PM + .suspend = ath10k_pci_hif_suspend, + .resume = ath10k_pci_hif_resume, +#endif }; static void ath10k_pci_ce_tasklet(unsigned long ptr) @@ -2376,128 +2429,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) kfree(ar_pci); } -#if defined(CONFIG_PM_SLEEP) - -#define ATH10K_PCI_PM_CONTROL 0x44 - -static int ath10k_pci_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct ath10k *ar = pci_get_drvdata(pdev); - struct ath10k_pci *ar_pci; - u32 val; - int ret, retval; - - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - - if (!ar) - return -ENODEV; - - ar_pci = ath10k_pci_priv(ar); - if (!ar_pci) - return -ENODEV; - - if (ath10k_core_target_suspend(ar)) - return -EBUSY; - - ret = wait_event_interruptible_timeout(ar->event_queue, - ar->is_target_paused == true, - 1 * HZ); - if (ret < 0) { - ath10k_warn("suspend interrupted (%d)\n", ret); - retval = ret; - goto resume; - } else if (ret == 0) { - ath10k_warn("suspend timed out - target pause event never came\n"); - retval = EIO; - goto resume; - } - - /* - * reset is_target_paused and host can check that in next time, - * or it will always be TRUE and host just skip the waiting - * condition, it causes target assert due to host already - * suspend - */ - ar->is_target_paused = false; - - pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); - - if ((val & 0x000000ff) != 0x3) { - pci_save_state(pdev); - pci_disable_device(pdev); - pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, - (val & 0xffffff00) | 0x03); - } - - return 0; -resume: - ret = ath10k_core_target_resume(ar); - if (ret) - ath10k_warn("could not resume (%d)\n", ret); - - return retval; -} - -static int ath10k_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct ath10k *ar = pci_get_drvdata(pdev); - struct ath10k_pci *ar_pci; - int ret; - u32 val; - - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - - if (!ar) - return -ENODEV; - ar_pci = ath10k_pci_priv(ar); - - if (!ar_pci) - return -ENODEV; - - ret = pci_enable_device(pdev); - if (ret) { - ath10k_warn("cannot enable PCI device: %d\n", ret); - return ret; - } - - pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); - - if ((val & 0x000000ff) != 0) { - pci_restore_state(pdev); - pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, - val & 0xffffff00); - /* - * Suspend/Resume resets the PCI configuration space, - * so we have to re-disable the RETRY_TIMEOUT register (0x41) - * to keep PCI Tx retries from interfering with C3 CPU state - */ - pci_read_config_dword(pdev, 0x40, &val); - - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - } - - ret = ath10k_core_target_resume(ar); - if (ret) - ath10k_warn("target resume failed: %d\n", ret); - - return ret; -} - -static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops, - ath10k_pci_suspend, - ath10k_pci_resume); - -#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops) - -#else - -#define ATH10K_PCI_PM_OPS NULL - -#endif /* CONFIG_PM_SLEEP */ - MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); static struct pci_driver ath10k_pci_driver = { @@ -2505,7 +2436,6 @@ static struct pci_driver ath10k_pci_driver = { .id_table = ath10k_pci_id_table, .probe = ath10k_pci_probe, .remove = ath10k_pci_remove, - .driver.pm = ATH10K_PCI_PM_OPS, }; static int __init ath10k_pci_init(void) -- cgit v1.2.3-70-g09d2 From 1a1b8a889d1fad1a0b48c07c26b894085fcc692c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:55 +0200 Subject: ath10k: move free_vdev_map initialization This is necessary for hw reconfiguration to work. Since mac80211 is not calling remove_interface() is such case we must reset free_vdev_map. Also use a define instead of a hardcoded value for vdev map initialization. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 01c1d82f674..0e4a704516f 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ar->hif.priv = hif_priv; ar->hif.ops = hif_ops; - ar->free_vdev_map = 0xFF; /* 8 vdevs */ - init_completion(&ar->scan.started); init_completion(&ar->scan.completed); init_completion(&ar->scan.on_channel); @@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar) if (status) goto err_disconnect_htc; + ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + return 0; err_disconnect_htc: -- cgit v1.2.3-70-g09d2 From a96d7745592274077e517173ec2cdac2a22d5b79 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:56 +0200 Subject: ath10k: make sure all resources are freed upon ath10k_stop() This is necessary for proper hw reconfiguration and to avoid memory leaks. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index febba7d8ca4..b9663e94dba 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -369,6 +369,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) spin_unlock_bh(&ar->data_lock); } +static void ath10k_peer_cleanup_all(struct ath10k *ar) +{ + struct ath10k_peer *peer, *tmp; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(peer, tmp, &ar->peers, list) { + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ar->data_lock); +} + /************************/ /* Interface management */ /************************/ @@ -1753,7 +1767,18 @@ static void ath10k_stop(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); + del_timer_sync(&ar->scan.timeout); ath10k_offchan_tx_purge(ar); + ath10k_peer_cleanup_all(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); + } + spin_unlock_bh(&ar->data_lock); + ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); -- cgit v1.2.3-70-g09d2 From 818bdd16b229919cfd07447d261154a1343871e1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:57 +0200 Subject: ath10k: defer hw setup to start/stop mac80211 hooks This fixes suspend-to-disk. The hardware is now re-initialized upon freeze/thaw properly. This also makes suspend/resume re-initialize the hardware as WoWLAN support is not done yet. With some little work it should be possible to support hw reconfiguration for hw/fw recovery. HW must be initialized once before registering to mac80211 because FW determinates what hw capabilities can be advertised. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 46 ++++++++++++++++++++++------ drivers/net/wireless/ath/ath10k/mac.c | 56 ++++++++++++++++++++++++++-------- drivers/net/wireless/ath/ath10k/pci.c | 11 +------ 3 files changed, 81 insertions(+), 32 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0e4a704516f..1d4c24583f1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -601,6 +601,7 @@ err_wmi_detach: err: return status; } +EXPORT_SYMBOL(ath10k_core_start); void ath10k_core_stop(struct ath10k *ar) { @@ -608,18 +609,49 @@ void ath10k_core_stop(struct ath10k *ar) ath10k_htt_detach(&ar->htt); ath10k_wmi_detach(ar); } +EXPORT_SYMBOL(ath10k_core_stop); + +/* mac80211 manages fw/hw initialization through start/stop hooks. However in + * order to know what hw capabilities should be advertised to mac80211 it is + * necessary to load the firmware (and tear it down immediately since start + * hook will try to init it again) before registering */ +static int ath10k_core_probe_fw(struct ath10k *ar) +{ + int ret; + + ret = ath10k_hif_power_up(ar); + if (ret) { + ath10k_err("could not start pci hif (%d)\n", ret); + return ret; + } + + ret = ath10k_core_start(ar); + if (ret) { + ath10k_err("could not init core (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + + ath10k_core_stop(ar); + ath10k_hif_power_down(ar); + return 0; +} int ath10k_core_register(struct ath10k *ar) { int status; - status = ath10k_core_start(ar); - if (status) - goto err; + status = ath10k_core_probe_fw(ar); + if (status) { + ath10k_err("could not probe fw (%d)\n", status); + return status; + } status = ath10k_mac_register(ar); - if (status) - goto err_core_stop; + if (status) { + ath10k_err("could not register to mac80211 (%d)\n", status); + return status; + } status = ath10k_debug_create(ar); if (status) { @@ -631,9 +663,6 @@ int ath10k_core_register(struct ath10k *ar) err_unregister_mac: ath10k_mac_unregister(ar); -err_core_stop: - ath10k_core_stop(ar); -err: return status; } EXPORT_SYMBOL(ath10k_core_register); @@ -644,7 +673,6 @@ void ath10k_core_unregister(struct ath10k *ar) * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); - ath10k_core_stop(ar); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b9663e94dba..2dfd446251b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1738,13 +1738,52 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* * Initialize various parameters with default vaules. */ +static void ath10k_halt(struct ath10k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + del_timer_sync(&ar->scan.timeout); + ath10k_offchan_tx_purge(ar); + ath10k_peer_cleanup_all(ar); + ath10k_core_stop(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); + } + spin_unlock_bh(&ar->data_lock); +} + static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; - int ret; + int ret = 0; mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_OFF) { + ret = -EINVAL; + goto exit; + } + + ret = ath10k_hif_power_up(ar); + if (ret) { + ath10k_err("could not init hif (%d)\n", ret); + ar->state = ATH10K_STATE_OFF; + goto exit; + } + + 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; + } + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); if (ret) ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", @@ -1755,9 +1794,9 @@ static int ath10k_start(struct ieee80211_hw *hw) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); - ar->state = ATH10K_STATE_ON; ath10k_regd_update(ar); +exit: mutex_unlock(&ar->conf_mutex); return 0; } @@ -1767,17 +1806,8 @@ static void ath10k_stop(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); - del_timer_sync(&ar->scan.timeout); - ath10k_offchan_tx_purge(ar); - ath10k_peer_cleanup_all(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); - } - spin_unlock_bh(&ar->data_lock); + if (ar->state == ATH10K_STATE_ON) + ath10k_halt(ar); ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 75f5427bb41..bfe856166bb 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2362,22 +2362,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_iomap; } - ret = ath10k_pci_hif_power_up(ar); - if (ret) { - ath10k_err("could not start pci hif (%d)\n", ret); - goto err_intr; - } - ret = ath10k_core_register(ar); if (ret) { ath10k_err("could not register driver core (%d)\n", ret); - goto err_hif; + goto err_intr; } return 0; -err_hif: - ath10k_pci_hif_power_down(ar); err_intr: ath10k_pci_stop_intr(ar); err_iomap: @@ -2416,7 +2408,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) tasklet_kill(&ar_pci->msi_fw_err); ath10k_core_unregister(ar); - ath10k_pci_hif_power_down(ar); ath10k_pci_stop_intr(ar); pci_set_drvdata(pdev, NULL); -- cgit v1.2.3-70-g09d2 From 293850575d0cba068913bcb22fc082286650516c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:58 +0200 Subject: ath10k: store firmware files in memory Different FW versions may provide different functions thus mean different hw capabilities advertised to mac80211. It is safe to swap firmware files on disk during driver/device runtime without worries. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 157 ++++++++++++++++++++++----------- drivers/net/wireless/ath/ath10k/core.h | 4 + 2 files changed, 109 insertions(+), 52 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 1d4c24583f1..c37f79f6f8c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { + const struct firmware *fw = ar->board_data; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; - const struct firmware *fw; int ret; - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.board); - if (IS_ERR(fw)) { - ath10k_err("could not fetch board data fw file (%ld)\n", - PTR_ERR(fw)); - return PTR_ERR(fw); - } - ret = ath10k_push_board_ext_data(ar, fw); if (ret) { ath10k_err("could not push board ext data (%d)\n", ret); @@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar) } exit: - release_firmware(fw); return ret; } static int ath10k_download_and_run_otp(struct ath10k *ar) { - const struct firmware *fw; - u32 address; + const struct firmware *fw = ar->otp; + u32 address = ar->hw_params.patch_load_addr; u32 exec_param; int ret; /* OTP is optional */ - if (ar->hw_params.fw.otp == NULL) { - ath10k_info("otp file not defined\n"); - return 0; - } - - address = ar->hw_params.patch_load_addr; - - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.otp); - if (IS_ERR(fw)) { - ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw)); + if (!ar->otp) return 0; - } ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); if (ret) { @@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) } exit: - release_firmware(fw); return ret; } static int ath10k_download_fw(struct ath10k *ar) { - const struct firmware *fw; + const struct firmware *fw = ar->firmware; u32 address; int ret; - if (ar->hw_params.fw.fw == NULL) - return -EINVAL; - address = ar->hw_params.patch_load_addr; - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.fw); - if (IS_ERR(fw)) { - ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw)); - return PTR_ERR(fw); - } - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); if (ret) { ath10k_err("could not write fw (%d)\n", ret); @@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar) } exit: - release_firmware(fw); + return ret; +} + +static void ath10k_core_free_firmware_files(struct ath10k *ar) +{ + if (ar->board_data && !IS_ERR(ar->board_data)) + release_firmware(ar->board_data); + + if (ar->otp && !IS_ERR(ar->otp)) + release_firmware(ar->otp); + + if (ar->firmware && !IS_ERR(ar->firmware)) + release_firmware(ar->firmware); + + ar->board_data = NULL; + ar->otp = NULL; + ar->firmware = NULL; +} + +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret = 0; + + if (ar->hw_params.fw.fw == NULL) { + ath10k_err("firmware file not defined\n"); + return -EINVAL; + } + + if (ar->hw_params.fw.board == NULL) { + ath10k_err("board data file not defined"); + return -EINVAL; + } + + ar->board_data = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board_data)) { + ret = PTR_ERR(ar->board_data); + ath10k_err("could not fetch board data (%d)\n", ret); + goto err; + } + + ar->firmware = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.fw); + if (IS_ERR(ar->firmware)) { + ret = PTR_ERR(ar->firmware); + ath10k_err("could not fetch firmware (%d)\n", ret); + goto err; + } + + /* OTP may be undefined. If so, don't fetch it at all */ + if (ar->hw_params.fw.otp == NULL) + return 0; + + ar->otp = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.otp); + if (IS_ERR(ar->otp)) { + ret = PTR_ERR(ar->otp); + ath10k_err("could not fetch otp (%d)\n", ret); + goto err; + } + + return 0; + +err: + ath10k_core_free_firmware_files(ar); return ret; } @@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy); int ath10k_core_start(struct ath10k *ar) { - struct bmi_target_info target_info; int status; ath10k_bmi_start(ar); - memset(&target_info, 0, sizeof(target_info)); - status = ath10k_bmi_get_target_info(ar, &target_info); - if (status) - goto err; - - ar->target_version = target_info.version; - ar->hw->wiphy->hw_version = target_info.version; - - status = ath10k_init_hw_params(ar); - if (status) - goto err; - if (ath10k_init_configure_target(ar)) { status = -EINVAL; goto err; @@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop); * hook will try to init it again) before registering */ static int ath10k_core_probe_fw(struct ath10k *ar) { - int ret; + struct bmi_target_info target_info; + int ret = 0; ret = ath10k_hif_power_up(ar); if (ret) { @@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } + memset(&target_info, 0, sizeof(target_info)); + ret = ath10k_bmi_get_target_info(ar, &target_info); + if (ret) { + ath10k_err("could not get target info (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + + ar->target_version = target_info.version; + ar->hw->wiphy->hw_version = target_info.version; + + ret = ath10k_init_hw_params(ar); + if (ret) { + ath10k_err("could not get hw params (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + + ret = ath10k_core_fetch_firmware_files(ar); + if (ret) { + ath10k_err("could not fetch firmware files (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + ret = ath10k_core_start(ar); if (ret) { ath10k_err("could not init core (%d)\n", ret); + ath10k_core_free_firmware_files(ar); ath10k_hif_power_down(ar); return ret; } @@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar) status = ath10k_mac_register(ar); if (status) { ath10k_err("could not register to mac80211 (%d)\n", status); - return status; + goto err_release_fw; } status = ath10k_debug_create(ar); @@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar) err_unregister_mac: ath10k_mac_unregister(ar); +err_release_fw: + ath10k_core_free_firmware_files(ar); return status; } EXPORT_SYMBOL(ath10k_core_register); @@ -673,6 +725,7 @@ void ath10k_core_unregister(struct ath10k *ar) * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); + ath10k_core_free_firmware_files(ar); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 5c94ea06eb7..413f1c5a811 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -300,6 +300,10 @@ struct ath10k { } fw; } hw_params; + const struct firmware *board_data; + const struct firmware *otp; + const struct firmware *firmware; + struct { struct completion started; struct completion completed; -- cgit v1.2.3-70-g09d2 From 87571bf0b8b27d4a97848ce48de34fa6d3b12db8 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:38:59 +0200 Subject: ath10k: skip fw stats debugfs interface if device is down If the device is not running then there may be no FW at all to send the query to. If the FW is already there it might still trigger a crash if the command is sent before the device is fully initialized. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 499034b873d..65279f5347c 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_pdev_stats *ps; int i; - mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->data_lock); stats = &ar->debug.target_stats; @@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, } } + spin_unlock_bh(&ar->data_lock); mutex_unlock(&ar->conf_mutex); complete(&ar->debug.event_stats_compl); } @@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, { struct ath10k *ar = file->private_data; struct ath10k_target_stats *fw_stats; - char *buf; + char *buf = NULL; unsigned int len = 0, buf_len = 2500; - ssize_t ret_cnt; + ssize_t ret_cnt = 0; long left; int i; int ret; fw_stats = &ar->debug.target_stats; + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_ON) + goto exit; + buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) - return -ENOMEM; + goto exit; ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); if (ret) { ath10k_warn("could not request stats (%d)\n", ret); - kfree(buf); - return -EIO; + goto exit; } left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ); + if (left <= 0) + goto exit; - if (left <= 0) { - kfree(buf); - return -ETIMEDOUT; - } - - mutex_lock(&ar->conf_mutex); - + spin_lock_bh(&ar->data_lock); len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%30s\n", "ath10k PDEV stats"); @@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, fw_stats->peer_stat[i].peer_tx_rate); len += scnprintf(buf + len, buf_len - len, "\n"); } + spin_unlock_bh(&ar->data_lock); if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); +exit: mutex_unlock(&ar->conf_mutex); - kfree(buf); return ret_cnt; } -- cgit v1.2.3-70-g09d2 From affd321733eebc92b12cd329505f63e94ae80c93 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:54:35 +0200 Subject: ath10k: implement device recovery Restart the hardware if FW crashes. If FW crashes during recovery we leave the hardware in a "wedged" state to avoid recursive recoveries. When in "wedged" state userspace may bring interfaces down (to issue stop()) and then bring one interface (to issue start()) to reload hardware manually. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 30 ++++++++++ drivers/net/wireless/ath/ath10k/core.h | 19 +++++++ drivers/net/wireless/ath/ath10k/htc.c | 3 + drivers/net/wireless/ath/ath10k/mac.c | 101 ++++++++++++++++++++++++++------- drivers/net/wireless/ath/ath10k/mac.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 2 + drivers/net/wireless/ath/ath10k/wmi.c | 7 +++ 7 files changed, 144 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c37f79f6f8c..7226c23b956 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar) return 0; } +static void ath10k_core_restart(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, restart_work); + + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH10K_STATE_ON: + ath10k_halt(ar); + ar->state = ATH10K_STATE_RESTARTING; + ieee80211_restart_hw(ar->hw); + break; + case ATH10K_STATE_OFF: + /* this can happen if driver is being unloaded */ + ath10k_warn("cannot restart a device that hasn't been started\n"); + break; + case ATH10K_STATE_RESTARTING: + case ATH10K_STATE_RESTARTED: + ar->state = ATH10K_STATE_WEDGED; + /* fall through */ + case ATH10K_STATE_WEDGED: + ath10k_warn("device is wedged, will not restart\n"); + break; + } + + mutex_unlock(&ar->conf_mutex); +} + struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, const struct ath10k_hif_ops *hif_ops) { @@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, init_waitqueue_head(&ar->event_queue); + INIT_WORK(&ar->restart_work, ath10k_core_restart); + return ar; err_wq: diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 413f1c5a811..9f21ecb239d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -250,6 +250,23 @@ struct ath10k_debug { enum ath10k_state { ATH10K_STATE_OFF = 0, ATH10K_STATE_ON, + + /* When doing firmware recovery the device is first powered down. + * mac80211 is supposed to call in to start() hook later on. It is + * however possible that driver unloading and firmware crash overlap. + * mac80211 can wait on conf_mutex in stop() while the device is + * stopped in ath10k_core_restart() work holding conf_mutex. The state + * RESTARTED means that the device is up and mac80211 has started hw + * reconfiguration. Once mac80211 is done with the reconfiguration we + * set the state to STATE_ON in restart_complete(). */ + ATH10K_STATE_RESTARTING, + ATH10K_STATE_RESTARTED, + + /* The device has crashed while restarting hw. This state is like ON + * but commands are blocked in HTC and -ECOMM response is given. This + * prevents completion timeouts and makes the driver more responsive to + * userspace commands. This is also prevents recursive recovery. */ + ATH10K_STATE_WEDGED, }; struct ath10k { @@ -355,6 +372,8 @@ struct ath10k { enum ath10k_state state; + struct work_struct restart_work; + #ifdef CONFIG_ATH10K_DEBUGFS struct ath10k_debug debug; #endif diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 7d5a36616b7..72e072c9758 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc, { struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + if (htc->ar->state == ATH10K_STATE_WEDGED) + return -ECOMM; + if (eid >= ATH10K_HTC_EP_COUNT) { ath10k_warn("Invalid endpoint id: %d\n", eid); return -ENOENT; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2dfd446251b..b1bb318d153 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1738,7 +1738,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* * Initialize various parameters with default vaules. */ -static void ath10k_halt(struct ath10k *ar) +void ath10k_halt(struct ath10k *ar) { lockdep_assert_held(&ar->conf_mutex); @@ -1764,7 +1764,8 @@ static int ath10k_start(struct ieee80211_hw *hw) mutex_lock(&ar->conf_mutex); - if (ar->state != ATH10K_STATE_OFF) { + if (ar->state != ATH10K_STATE_OFF && + ar->state != ATH10K_STATE_RESTARTING) { ret = -EINVAL; goto exit; } @@ -1784,6 +1785,11 @@ static int ath10k_start(struct ieee80211_hw *hw) goto exit; } + 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, WMI_PDEV_PARAM_PMF_QOS, 1); if (ret) ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", @@ -1806,22 +1812,47 @@ static void ath10k_stop(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); - if (ar->state == ATH10K_STATE_ON) + if (ar->state == ATH10K_STATE_ON || + ar->state == ATH10K_STATE_RESTARTED || + ar->state == ATH10K_STATE_WEDGED) ath10k_halt(ar); ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->restart_work); } -static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +static void ath10k_config_ps(struct ath10k *ar) { struct ath10k_generic_iter ar_iter; + + lockdep_assert_held(&ar->conf_mutex); + + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar->state == ATH10K_STATE_RESTARTED) + return; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + ieee80211_iterate_active_interfaces_atomic( + ar->hw, IEEE80211_IFACE_ITER_NORMAL, + ath10k_ps_iter, &ar_iter); + + if (ar_iter.ret) + ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); +} + +static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +{ struct ath10k *ar = hw->priv; struct ieee80211_conf *conf = &hw->conf; int ret = 0; - u32 flags; mutex_lock(&ar->conf_mutex); @@ -1833,18 +1864,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) spin_unlock_bh(&ar->data_lock); } - if (changed & IEEE80211_CONF_CHANGE_PS) { - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; - flags = IEEE80211_IFACE_ITER_RESUME_ALL; - - ieee80211_iterate_active_interfaces_atomic(hw, - flags, - ath10k_ps_iter, - &ar_iter); - - ret = ar_iter.ret; - } + if (changed & IEEE80211_CONF_CHANGE_PS) + ath10k_config_ps(ar); if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (conf->flags & IEEE80211_CONF_MONITOR) @@ -1853,6 +1874,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ret = ath10k_monitor_destroy(ar); } + ath10k_wmi_flush_tx(ar); mutex_unlock(&ar->conf_mutex); return ret; } @@ -2695,6 +2717,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) lockdep_assert_held(&arvif->ar->conf_mutex); + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) + return; + rts = min_t(u32, rts, ATH10K_RTS_MAX); ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, @@ -2735,6 +2764,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) lockdep_assert_held(&arvif->ar->conf_mutex); + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) + return; + frag = clamp_t(u32, frag, ATH10K_FRAGMT_THRESHOLD_MIN, ATH10K_FRAGMT_THRESHOLD_MAX); @@ -2773,6 +2809,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ath10k *ar = hw->priv; + bool skip; int ret; /* mac80211 doesn't care if we really xmit queued frames or not @@ -2782,17 +2819,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) mutex_lock(&ar->conf_mutex); + if (ar->state == ATH10K_STATE_WEDGED) + goto skip; + ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ bool empty; + spin_lock_bh(&ar->htt.tx_lock); empty = bitmap_empty(ar->htt.used_msdu_ids, ar->htt.max_num_pending_tx); spin_unlock_bh(&ar->htt.tx_lock); - (empty); + + skip = (ar->state == ATH10K_STATE_WEDGED); + + (empty || skip); }), ATH10K_FLUSH_TIMEOUT_HZ); - if (ret <= 0) + + if (ret <= 0 || skip) ath10k_warn("tx not flushed\n"); +skip: mutex_unlock(&ar->conf_mutex); } @@ -2866,6 +2912,22 @@ static int ath10k_resume(struct ieee80211_hw *hw) } #endif +static void ath10k_restart_complete(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + /* If device failed to restart it will be in a different state, e.g. + * ATH10K_STATE_WEDGED */ + if (ar->state == ATH10K_STATE_RESTARTED) { + ath10k_info("device successfully recovered\n"); + ar->state = ATH10K_STATE_ON; + } + + mutex_unlock(&ar->conf_mutex); +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -2886,6 +2948,7 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, + .restart_complete = ath10k_restart_complete, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 27fc92e5882..6fce9bfb19a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); void ath10k_reset_scan(unsigned long ptr); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); +void ath10k_halt(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index bfe856166bb..c71b488eba9 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) reg_dump_values[i + 1], reg_dump_values[i + 2], reg_dump_values[i + 3]); + + ieee80211_queue_work(ar->hw, &ar->restart_work); } static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b7e7e456b5d..0d25cd733af 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar) { int ret; + lockdep_assert_held(&ar->conf_mutex); + + if (ar->state == ATH10K_STATE_WEDGED) { + ath10k_warn("wmi flush skipped - device is wedged anyway\n"); + return; + } + ret = wait_event_timeout(ar->wmi.wq, atomic_read(&ar->wmi.pending_tx_count) == 0, 5*HZ); -- cgit v1.2.3-70-g09d2 From 9cfbce75c806e151b11a5d2c04b3aea3e920b3c1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 09:54:36 +0200 Subject: ath10k: implement fw crash simulation command This can be useful to test FW crash handling. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 19 +++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 0d25cd733af..5e4246015cd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2092,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); } + +int ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) +{ + struct wmi_force_fw_hang_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_force_fw_hang_cmd *)skb->data; + cmd->type = __cpu_to_le32(type); + cmd->delay_ms = __cpu_to_le32(delay_ms); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", + type, delay_ms); + return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9555f5a0e04..da3b2bc4c88 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -416,6 +416,7 @@ enum wmi_cmd_id { WMI_PDEV_FTM_INTG_CMDID, WMI_VDEV_SET_KEEPALIVE_CMDID, WMI_VDEV_GET_KEEPALIVE_CMDID, + WMI_FORCE_FW_HANG_CMDID, /* GPIO Configuration */ WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO), @@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd { struct wmi_sta_keepalive_arp_resp arp_resp; } __packed; +enum wmi_force_fw_hang_type { + WMI_FORCE_FW_HANG_ASSERT = 1, + WMI_FORCE_FW_HANG_NO_DETECT, + WMI_FORCE_FW_HANG_CTRL_EP_FULL, + WMI_FORCE_FW_HANG_EMPTY_POINT, + WMI_FORCE_FW_HANG_STACK_OVERFLOW, + WMI_FORCE_FW_HANG_INFINITE_LOOP, +}; + +#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF + +struct wmi_force_fw_hang_cmd { + __le32 type; + __le32 delay_ms; +} __packed; + #define ATH10K_RTS_MAX 2347 #define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346 @@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, const struct wmi_pdev_set_wmm_params_arg *arg); int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); +int ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms); #endif /* _WMI_H_ */ -- cgit v1.2.3-70-g09d2 From cf84bd4defe22c7359bd3e4d6978bd88af1f8f90 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 16 Jul 2013 11:04:54 +0200 Subject: ath10k: fix NULL dereference for injected packets Tx processing functions dereference vif and caused NULL to be dereferenced for injected frames. Don't call these functions at all for injected frames. It doesn't make much sense to do so anyway. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b1bb318d153..07e5f7d4046 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1710,10 +1710,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; } - ath10k_tx_h_qos_workaround(hw, control, skb); - ath10k_tx_h_update_wep_key(skb); - ath10k_tx_h_add_p2p_noa_ie(ar, skb); - ath10k_tx_h_seq_no(skb); + /* it makes no sense to process injected frames like that */ + if (info->control.vif && + info->control.vif->type != NL80211_IFTYPE_MONITOR) { + ath10k_tx_h_qos_workaround(hw, control, skb); + ath10k_tx_h_update_wep_key(skb); + ath10k_tx_h_add_p2p_noa_ie(ar, skb); + ath10k_tx_h_seq_no(skb); + } memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; -- cgit v1.2.3-70-g09d2 From 278c4a85e6267979ab164c0a8b57447005cf388c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 22 Jul 2013 14:08:51 +0200 Subject: ath10k: create debugfs interface to trigger fw crash This can be useful for testing. To perform a forced firmware crash write 'crash' to 'simulate_fw_crash' debugfs file. E.g. echo crash > /sys/kernel/debug/ieee80211/phy1/ath10k/simulate_fw_crash Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 57 +++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 65279f5347c..3d65594fa09 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -445,6 +445,60 @@ static const struct file_operations fops_fw_stats = { .llseek = default_llseek, }; +static ssize_t ath10k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = "To simulate firmware crash write the keyword" + " `crash` to this file.\nThis will force firmware" + " to report a crash to the host system.\n"; + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static ssize_t ath10k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32] = {}; + int ret; + + mutex_lock(&ar->conf_mutex); + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) { + ret = -EINVAL; + goto exit; + } + + if (ar->state != ATH10K_STATE_ON && + ar->state != ATH10K_STATE_RESTARTED) { + ret = -ENETDOWN; + goto exit; + } + + ath10k_info("simulating firmware crash\n"); + + ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0); + if (ret) + ath10k_warn("failed to force fw hang (%d)\n", ret); + + if (ret == 0) + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath10k_read_simulate_fw_crash, + .write = ath10k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.debugfs_phy = debugfs_create_dir("ath10k", @@ -461,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_wmi_services); + debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_simulate_fw_crash); + return 0; } #endif /* CONFIG_ATH10K_DEBUGFS */ -- cgit v1.2.3-70-g09d2 From 08fe9b40d055d4ace995a5e1e93c7c17573f17a5 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 22 Jul 2013 14:13:28 +0200 Subject: ath10k: prevent HTC from being used after stopping It was possible to submit new HTC commands after/while HTC stopped. This led to memory corruption in some rare cases. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 27 +++++++++++++-------------- drivers/net/wireless/ath/ath10k/htc.h | 4 ++-- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 72e072c9758..47b7752656f 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -254,10 +254,14 @@ int ath10k_htc_send(struct ath10k_htc *htc, return -ENOENT; } - skb_push(skb, sizeof(struct ath10k_htc_hdr)); - spin_lock_bh(&htc->tx_lock); + if (htc->stopped) { + spin_unlock_bh(&htc->tx_lock); + return -ESHUTDOWN; + } + __skb_queue_tail(&ep->tx_queue, skb); + skb_push(skb, sizeof(struct ath10k_htc_hdr)); spin_unlock_bh(&htc->tx_lock); queue_work(htc->ar->workqueue, &ep->send_work); @@ -270,23 +274,17 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, { struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_ep *ep = &htc->endpoint[eid]; - bool stopping; ath10k_htc_notify_tx_completion(ep, skb); /* the skb now belongs to the completion handler */ + /* note: when using TX credit flow, the re-checking of queues happens + * when credits flow back from the target. in the non-TX credit case, + * we recheck after the packet completes */ spin_lock_bh(&htc->tx_lock); - stopping = htc->stopping; - spin_unlock_bh(&htc->tx_lock); - - if (!ep->tx_credit_flow_enabled && !stopping) - /* - * note: when using TX credit flow, the re-checking of - * queues happens when credits flow back from the target. - * in the non-TX credit case, we recheck after the packet - * completes - */ + if (!ep->tx_credit_flow_enabled && !htc->stopped) queue_work(ar->workqueue, &ep->send_work); + spin_unlock_bh(&htc->tx_lock); return 0; } @@ -951,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc) struct ath10k_htc_ep *ep; spin_lock_bh(&htc->tx_lock); - htc->stopping = true; + htc->stopped = true; spin_unlock_bh(&htc->tx_lock); for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { @@ -972,6 +970,7 @@ int ath10k_htc_init(struct ath10k *ar) spin_lock_init(&htc->tx_lock); + htc->stopped = false; ath10k_htc_reset_endpoint_states(htc); /* setup HIF layer callbacks */ diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index 1606c9f9dff..e1dd8c76185 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -335,7 +335,7 @@ struct ath10k_htc { struct ath10k *ar; struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; - /* protects endpoint and stopping fields */ + /* protects endpoint and stopped fields */ spinlock_t tx_lock; struct ath10k_htc_ops htc_ops; @@ -349,7 +349,7 @@ struct ath10k_htc { struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; int target_credit_size; - bool stopping; + bool stopped; }; int ath10k_htc_init(struct ath10k *ar); -- cgit v1.2.3-70-g09d2 From 21bf9112b596cb91e4f63de859ea514f081031fd Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 22 Jul 2013 14:13:29 +0200 Subject: ath10k: don't reset HTC endpoints unnecessarily Endpoints are re-initialized upon HTC start anyway so there's no need to do that twice in case of restarting HTC (i.e. in case of hardware recovery). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 47b7752656f..ef3329ef52f 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -958,7 +958,6 @@ void ath10k_htc_stop(struct ath10k_htc *htc) } ath10k_hif_stop(htc->ar); - ath10k_htc_reset_endpoint_states(htc); } /* registered target arrival callback from the HIF layer */ -- cgit v1.2.3-70-g09d2 From d6015b27f7dd6e167cd90b590dc9ed5dfbc68086 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 22 Jul 2013 14:13:30 +0200 Subject: ath10k: fix memleak in mac setup In some cases channel arrays were never freed. The patch also unifies error handling in the mac setup function. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 07e5f7d4046..6144b3bf3da 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3225,8 +3225,10 @@ int ath10k_mac_register(struct ath10k *ar) channels = kmemdup(ath10k_2ghz_channels, sizeof(ath10k_2ghz_channels), GFP_KERNEL); - if (!channels) - return -ENOMEM; + if (!channels) { + ret = -ENOMEM; + goto err_free; + } band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels); @@ -3245,11 +3247,8 @@ int ath10k_mac_register(struct ath10k *ar) sizeof(ath10k_5ghz_channels), GFP_KERNEL); if (!channels) { - if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { - band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; - kfree(band->channels); - } - return -ENOMEM; + ret = -ENOMEM; + goto err_free; } band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; @@ -3313,25 +3312,30 @@ int ath10k_mac_register(struct ath10k *ar) ath10k_reg_notifier); if (ret) { ath10k_err("Regulatory initialization failed\n"); - return ret; + goto err_free; } ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err("ieee80211 registration failed: %d\n", ret); - return ret; + goto err_free; } if (!ath_is_world_regd(&ar->ath_common.regulatory)) { ret = regulatory_hint(ar->hw->wiphy, ar->ath_common.regulatory.alpha2); if (ret) - goto exit; + goto err_unregister; } return 0; -exit: + +err_unregister: ieee80211_unregister_hw(ar->hw); +err_free: + kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); + return ret; } -- cgit v1.2.3-70-g09d2 From 424121c365aed6ec93bbc8b515548df79c5af61c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 22 Jul 2013 14:13:31 +0200 Subject: ath10k: fix rts/fragmentation threshold setup If RTS and fragmentation threshold values are 0xFFFFFFFF they should be considered disabled and no min/max limits must be applied. This fixes some issues with throughput issues, especially with VHT. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 54 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 6144b3bf3da..d0a776124f1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -332,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) return 0; } +static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) +{ + if (value != 0xFFFFFFFF) + value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold, + ATH10K_RTS_MAX); + + return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + value); +} + +static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value) +{ + if (value != 0xFFFFFFFF) + value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold, + ATH10K_FRAGMT_THRESHOLD_MIN, + ATH10K_FRAGMT_THRESHOLD_MAX); + + return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + value); +} + static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) { int ret; @@ -1897,7 +1920,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); enum wmi_sta_powersave_param param; int ret = 0; - u32 value, rts, frag; + u32 value; int bit; mutex_lock(&ar->conf_mutex); @@ -2000,20 +2023,12 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ath10k_warn("Failed to set PSPOLL count: %d\n", ret); } - rts = min_t(u32, ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - rts); + ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); if (ret) ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", arvif->vdev_id, ret); - frag = clamp_t(u32, ar->hw->wiphy->frag_threshold, - ATH10K_FRAGMT_THRESHOLD_MIN, - ATH10K_FRAGMT_THRESHOLD_MAX); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - frag); + ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); if (ret) ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", arvif->vdev_id, ret); @@ -2728,11 +2743,7 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) return; - rts = min_t(u32, rts, ATH10K_RTS_MAX); - - ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - rts); + ar_iter->ret = ath10k_mac_set_rts(arvif, rts); if (ar_iter->ret) ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", arvif->vdev_id); @@ -2764,7 +2775,6 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath10k_generic_iter *ar_iter = data; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; - int ret; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -2775,15 +2785,7 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) return; - frag = clamp_t(u32, frag, - ATH10K_FRAGMT_THRESHOLD_MIN, - ATH10K_FRAGMT_THRESHOLD_MAX); - - ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - frag); - - ar_iter->ret = ret; + ar_iter->ret = ath10k_mac_set_frag(arvif, frag); if (ar_iter->ret) ath10k_warn("Failed to set frag threshold for VDEV: %d\n", arvif->vdev_id); -- cgit v1.2.3-70-g09d2