diff options
author | Janusz Dziedzic <janusz.dziedzic@tieto.com> | 2015-03-23 11:32:53 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2015-03-30 02:09:30 -0400 |
commit | 5fd3ac3c36f8b28f54ce3094e69bfc1d54ca0be9 (patch) | |
tree | f24ee44ff64844d3e3d7b6c5ebc646a8a02d269a | |
parent | f5431e87ae0357bd72cfbfbe9552aa3d555c3800 (diff) |
ath10k: add WOW disconnect/magic-packet support
Add support for WOW disconnect and magic-packet.
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r-- | drivers/net/wireless/ath/ath10k/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 75 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wow.c | 276 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wow.h | 38 |
7 files changed, 346 insertions, 67 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 92a1e097e436..9729e6941635 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile | |||
@@ -18,6 +18,7 @@ ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o | |||
18 | ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o | 18 | ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o |
19 | ath10k_core-$(CONFIG_THERMAL) += thermal.o | 19 | ath10k_core-$(CONFIG_THERMAL) += thermal.o |
20 | ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o | 20 | ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o |
21 | ath10k_core-$(CONFIG_PM) += wow.o | ||
21 | 22 | ||
22 | obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o | 23 | obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o |
23 | ath10k_pci-y += pci.o \ | 24 | ath10k_pci-y += pci.o \ |
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c0e454bb6a8d..e7fc531993ea 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c | |||
@@ -1386,6 +1386,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, | |||
1386 | init_completion(&ar->scan.completed); | 1386 | init_completion(&ar->scan.completed); |
1387 | init_completion(&ar->scan.on_channel); | 1387 | init_completion(&ar->scan.on_channel); |
1388 | init_completion(&ar->target_suspend); | 1388 | init_completion(&ar->target_suspend); |
1389 | init_completion(&ar->wow.wakeup_completed); | ||
1389 | 1390 | ||
1390 | init_completion(&ar->install_key_done); | 1391 | init_completion(&ar->install_key_done); |
1391 | init_completion(&ar->vdev_setup_done); | 1392 | init_completion(&ar->vdev_setup_done); |
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index cfd37a6e8931..348efd88bccf 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h | |||
@@ -35,6 +35,7 @@ | |||
35 | #include "../dfs_pattern_detector.h" | 35 | #include "../dfs_pattern_detector.h" |
36 | #include "spectral.h" | 36 | #include "spectral.h" |
37 | #include "thermal.h" | 37 | #include "thermal.h" |
38 | #include "wow.h" | ||
38 | 39 | ||
39 | #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) | 40 | #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) |
40 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) | 41 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) |
@@ -443,6 +444,12 @@ enum ath10k_fw_features { | |||
443 | */ | 444 | */ |
444 | ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5, | 445 | ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5, |
445 | 446 | ||
447 | /* Some firmware revisions have an incomplete WoWLAN implementation | ||
448 | * despite WMI service bit being advertised. This feature flag is used | ||
449 | * to distinguish whether WoWLAN is really supported or not. | ||
450 | */ | ||
451 | ATH10K_FW_FEATURE_WOWLAN_SUPPORT = 6, | ||
452 | |||
446 | /* keep last */ | 453 | /* keep last */ |
447 | ATH10K_FW_FEATURE_COUNT, | 454 | ATH10K_FW_FEATURE_COUNT, |
448 | }; | 455 | }; |
@@ -691,6 +698,7 @@ struct ath10k { | |||
691 | } stats; | 698 | } stats; |
692 | 699 | ||
693 | struct ath10k_thermal thermal; | 700 | struct ath10k_thermal thermal; |
701 | struct ath10k_wow wow; | ||
694 | 702 | ||
695 | /* must be last */ | 703 | /* must be last */ |
696 | u8 drv_priv[0] __aligned(sizeof(void *)); | 704 | u8 drv_priv[0] __aligned(sizeof(void *)); |
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 73a9d3843e91..755ea6114079 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "testmode.h" | 29 | #include "testmode.h" |
30 | #include "wmi.h" | 30 | #include "wmi.h" |
31 | #include "wmi-ops.h" | 31 | #include "wmi-ops.h" |
32 | #include "wow.h" | ||
32 | 33 | ||
33 | /**********/ | 34 | /**********/ |
34 | /* Crypto */ | 35 | /* Crypto */ |
@@ -4810,70 +4811,6 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) | |||
4810 | return 1; | 4811 | return 1; |
4811 | } | 4812 | } |
4812 | 4813 | ||
4813 | #ifdef CONFIG_PM | ||
4814 | static int ath10k_suspend(struct ieee80211_hw *hw, | ||
4815 | struct cfg80211_wowlan *wowlan) | ||
4816 | { | ||
4817 | struct ath10k *ar = hw->priv; | ||
4818 | int ret; | ||
4819 | |||
4820 | mutex_lock(&ar->conf_mutex); | ||
4821 | |||
4822 | ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND); | ||
4823 | if (ret) { | ||
4824 | if (ret == -ETIMEDOUT) | ||
4825 | goto resume; | ||
4826 | ret = 1; | ||
4827 | goto exit; | ||
4828 | } | ||
4829 | |||
4830 | ret = ath10k_hif_suspend(ar); | ||
4831 | if (ret) { | ||
4832 | ath10k_warn(ar, "failed to suspend hif: %d\n", ret); | ||
4833 | goto resume; | ||
4834 | } | ||
4835 | |||
4836 | ret = 0; | ||
4837 | goto exit; | ||
4838 | resume: | ||
4839 | ret = ath10k_wmi_pdev_resume_target(ar); | ||
4840 | if (ret) | ||
4841 | ath10k_warn(ar, "failed to resume target: %d\n", ret); | ||
4842 | |||
4843 | ret = 1; | ||
4844 | exit: | ||
4845 | mutex_unlock(&ar->conf_mutex); | ||
4846 | return ret; | ||
4847 | } | ||
4848 | |||
4849 | static int ath10k_resume(struct ieee80211_hw *hw) | ||
4850 | { | ||
4851 | struct ath10k *ar = hw->priv; | ||
4852 | int ret; | ||
4853 | |||
4854 | mutex_lock(&ar->conf_mutex); | ||
4855 | |||
4856 | ret = ath10k_hif_resume(ar); | ||
4857 | if (ret) { | ||
4858 | ath10k_warn(ar, "failed to resume hif: %d\n", ret); | ||
4859 | ret = 1; | ||
4860 | goto exit; | ||
4861 | } | ||
4862 | |||
4863 | ret = ath10k_wmi_pdev_resume_target(ar); | ||
4864 | if (ret) { | ||
4865 | ath10k_warn(ar, "failed to resume target: %d\n", ret); | ||
4866 | ret = 1; | ||
4867 | goto exit; | ||
4868 | } | ||
4869 | |||
4870 | ret = 0; | ||
4871 | exit: | ||
4872 | mutex_unlock(&ar->conf_mutex); | ||
4873 | return ret; | ||
4874 | } | ||
4875 | #endif | ||
4876 | |||
4877 | static void ath10k_reconfig_complete(struct ieee80211_hw *hw, | 4814 | static void ath10k_reconfig_complete(struct ieee80211_hw *hw, |
4878 | enum ieee80211_reconfig_type reconfig_type) | 4815 | enum ieee80211_reconfig_type reconfig_type) |
4879 | { | 4816 | { |
@@ -5423,8 +5360,8 @@ static const struct ieee80211_ops ath10k_ops = { | |||
5423 | CFG80211_TESTMODE_CMD(ath10k_tm_cmd) | 5360 | CFG80211_TESTMODE_CMD(ath10k_tm_cmd) |
5424 | 5361 | ||
5425 | #ifdef CONFIG_PM | 5362 | #ifdef CONFIG_PM |
5426 | .suspend = ath10k_suspend, | 5363 | .suspend = ath10k_wow_op_suspend, |
5427 | .resume = ath10k_resume, | 5364 | .resume = ath10k_wow_op_resume, |
5428 | #endif | 5365 | #endif |
5429 | #ifdef CONFIG_MAC80211_DEBUGFS | 5366 | #ifdef CONFIG_MAC80211_DEBUGFS |
5430 | .sta_add_debugfs = ath10k_sta_add_debugfs, | 5367 | .sta_add_debugfs = ath10k_sta_add_debugfs, |
@@ -5861,6 +5798,12 @@ int ath10k_mac_register(struct ath10k *ar) | |||
5861 | 5798 | ||
5862 | ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; | 5799 | ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; |
5863 | 5800 | ||
5801 | ret = ath10k_wow_init(ar); | ||
5802 | if (ret) { | ||
5803 | ath10k_warn(ar, "failed to init wow: %d\n", ret); | ||
5804 | goto err_free; | ||
5805 | } | ||
5806 | |||
5864 | /* | 5807 | /* |
5865 | * on LL hardware queues are managed entirely by the FW | 5808 | * on LL hardware queues are managed entirely by the FW |
5866 | * so we only advertise to mac we can do the queues thing | 5809 | * so we only advertise to mac we can do the queues thing |
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 54430a1a3f73..a5ed54959a10 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c | |||
@@ -2898,7 +2898,19 @@ void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb) | |||
2898 | 2898 | ||
2899 | void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) | 2899 | void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) |
2900 | { | 2900 | { |
2901 | ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); | 2901 | struct wmi_wow_ev_arg ev = {}; |
2902 | int ret; | ||
2903 | |||
2904 | complete(&ar->wow.wakeup_completed); | ||
2905 | |||
2906 | ret = ath10k_wmi_pull_wow_event(ar, skb, &ev); | ||
2907 | if (ret) { | ||
2908 | ath10k_warn(ar, "failed to parse wow wakeup event: %d\n", ret); | ||
2909 | return; | ||
2910 | } | ||
2911 | |||
2912 | ath10k_dbg(ar, ATH10K_DBG_WMI, "wow wakeup host reason %s\n", | ||
2913 | wow_reason(ev.wake_reason)); | ||
2902 | } | 2914 | } |
2903 | 2915 | ||
2904 | void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) | 2916 | void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) |
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c new file mode 100644 index 000000000000..dc1b02580e79 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wow.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "mac.h" | ||
18 | |||
19 | #include <net/mac80211.h> | ||
20 | #include "hif.h" | ||
21 | #include "core.h" | ||
22 | #include "debug.h" | ||
23 | #include "wmi.h" | ||
24 | #include "wmi-ops.h" | ||
25 | |||
26 | static const struct wiphy_wowlan_support ath10k_wowlan_support = { | ||
27 | .flags = WIPHY_WOWLAN_DISCONNECT | | ||
28 | WIPHY_WOWLAN_MAGIC_PKT, | ||
29 | }; | ||
30 | |||
31 | static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) | ||
32 | { | ||
33 | struct ath10k *ar = arvif->ar; | ||
34 | int i, ret; | ||
35 | |||
36 | for (i = 0; i < WOW_EVENT_MAX; i++) { | ||
37 | ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); | ||
38 | if (ret) { | ||
39 | ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", | ||
40 | wow_wakeup_event(i), arvif->vdev_id, ret); | ||
41 | return ret; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int ath10k_wow_cleanup(struct ath10k *ar) | ||
49 | { | ||
50 | struct ath10k_vif *arvif; | ||
51 | int ret; | ||
52 | |||
53 | lockdep_assert_held(&ar->conf_mutex); | ||
54 | |||
55 | list_for_each_entry(arvif, &ar->arvifs, list) { | ||
56 | ret = ath10k_wow_vif_cleanup(arvif); | ||
57 | if (ret) { | ||
58 | ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", | ||
59 | arvif->vdev_id, ret); | ||
60 | return ret; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, | ||
68 | struct cfg80211_wowlan *wowlan) | ||
69 | { | ||
70 | int ret, i; | ||
71 | unsigned long wow_mask = 0; | ||
72 | struct ath10k *ar = arvif->ar; | ||
73 | |||
74 | /* Setup requested WOW features */ | ||
75 | switch (arvif->vdev_type) { | ||
76 | case WMI_VDEV_TYPE_IBSS: | ||
77 | __set_bit(WOW_BEACON_EVENT, &wow_mask); | ||
78 | /* fall through */ | ||
79 | case WMI_VDEV_TYPE_AP: | ||
80 | __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); | ||
81 | __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); | ||
82 | __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); | ||
83 | __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); | ||
84 | __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); | ||
85 | __set_bit(WOW_HTT_EVENT, &wow_mask); | ||
86 | __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); | ||
87 | break; | ||
88 | case WMI_VDEV_TYPE_STA: | ||
89 | if (wowlan->disconnect) { | ||
90 | __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); | ||
91 | __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); | ||
92 | __set_bit(WOW_BMISS_EVENT, &wow_mask); | ||
93 | __set_bit(WOW_CSA_IE_EVENT, &wow_mask); | ||
94 | } | ||
95 | |||
96 | if (wowlan->magic_pkt) | ||
97 | __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); | ||
98 | break; | ||
99 | default: | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | for (i = 0; i < WOW_EVENT_MAX; i++) { | ||
104 | if (!test_bit(i, &wow_mask)) | ||
105 | continue; | ||
106 | ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); | ||
107 | if (ret) { | ||
108 | ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", | ||
109 | wow_wakeup_event(i), arvif->vdev_id, ret); | ||
110 | return ret; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int ath10k_wow_set_wakeups(struct ath10k *ar, | ||
118 | struct cfg80211_wowlan *wowlan) | ||
119 | { | ||
120 | struct ath10k_vif *arvif; | ||
121 | int ret; | ||
122 | |||
123 | lockdep_assert_held(&ar->conf_mutex); | ||
124 | |||
125 | list_for_each_entry(arvif, &ar->arvifs, list) { | ||
126 | ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); | ||
127 | if (ret) { | ||
128 | ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", | ||
129 | arvif->vdev_id, ret); | ||
130 | return ret; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int ath10k_wow_enable(struct ath10k *ar) | ||
138 | { | ||
139 | int ret; | ||
140 | |||
141 | lockdep_assert_held(&ar->conf_mutex); | ||
142 | |||
143 | reinit_completion(&ar->target_suspend); | ||
144 | |||
145 | ret = ath10k_wmi_wow_enable(ar); | ||
146 | if (ret) { | ||
147 | ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); | ||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); | ||
152 | if (ret == 0) { | ||
153 | ath10k_warn(ar, "timed out while waiting for suspend completion\n"); | ||
154 | return -ETIMEDOUT; | ||
155 | } | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int ath10k_wow_wakeup(struct ath10k *ar) | ||
161 | { | ||
162 | int ret; | ||
163 | |||
164 | lockdep_assert_held(&ar->conf_mutex); | ||
165 | |||
166 | reinit_completion(&ar->wow.wakeup_completed); | ||
167 | |||
168 | ret = ath10k_wmi_wow_host_wakeup_ind(ar); | ||
169 | if (ret) { | ||
170 | ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", | ||
171 | ret); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); | ||
176 | if (ret == 0) { | ||
177 | ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); | ||
178 | return -ETIMEDOUT; | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | int ath10k_wow_op_suspend(struct ieee80211_hw *hw, | ||
185 | struct cfg80211_wowlan *wowlan) | ||
186 | { | ||
187 | struct ath10k *ar = hw->priv; | ||
188 | int ret; | ||
189 | |||
190 | mutex_lock(&ar->conf_mutex); | ||
191 | |||
192 | if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, | ||
193 | ar->fw_features))) { | ||
194 | ret = 1; | ||
195 | goto exit; | ||
196 | } | ||
197 | |||
198 | ret = ath10k_wow_cleanup(ar); | ||
199 | if (ret) { | ||
200 | ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", | ||
201 | ret); | ||
202 | goto exit; | ||
203 | } | ||
204 | |||
205 | ret = ath10k_wow_set_wakeups(ar, wowlan); | ||
206 | if (ret) { | ||
207 | ath10k_warn(ar, "failed to set wow wakeup events: %d\n", | ||
208 | ret); | ||
209 | goto cleanup; | ||
210 | } | ||
211 | |||
212 | ret = ath10k_wow_enable(ar); | ||
213 | if (ret) { | ||
214 | ath10k_warn(ar, "failed to start wow: %d\n", ret); | ||
215 | goto cleanup; | ||
216 | } | ||
217 | |||
218 | ret = ath10k_hif_suspend(ar); | ||
219 | if (ret) { | ||
220 | ath10k_warn(ar, "failed to suspend hif: %d\n", ret); | ||
221 | goto wakeup; | ||
222 | } | ||
223 | |||
224 | goto exit; | ||
225 | |||
226 | wakeup: | ||
227 | ath10k_wow_wakeup(ar); | ||
228 | |||
229 | cleanup: | ||
230 | ath10k_wow_cleanup(ar); | ||
231 | |||
232 | exit: | ||
233 | mutex_unlock(&ar->conf_mutex); | ||
234 | return ret ? 1 : 0; | ||
235 | } | ||
236 | |||
237 | int ath10k_wow_op_resume(struct ieee80211_hw *hw) | ||
238 | { | ||
239 | struct ath10k *ar = hw->priv; | ||
240 | int ret; | ||
241 | |||
242 | mutex_lock(&ar->conf_mutex); | ||
243 | |||
244 | if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, | ||
245 | ar->fw_features))) { | ||
246 | ret = 1; | ||
247 | goto exit; | ||
248 | } | ||
249 | |||
250 | ret = ath10k_hif_resume(ar); | ||
251 | if (ret) { | ||
252 | ath10k_warn(ar, "failed to resume hif: %d\n", ret); | ||
253 | goto exit; | ||
254 | } | ||
255 | |||
256 | ret = ath10k_wow_wakeup(ar); | ||
257 | if (ret) | ||
258 | ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); | ||
259 | |||
260 | exit: | ||
261 | mutex_unlock(&ar->conf_mutex); | ||
262 | return ret ? 1 : 0; | ||
263 | } | ||
264 | |||
265 | int ath10k_wow_init(struct ath10k *ar) | ||
266 | { | ||
267 | if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features)) | ||
268 | return 0; | ||
269 | |||
270 | if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) | ||
271 | return -EINVAL; | ||
272 | |||
273 | ar->hw->wiphy->wowlan = &ath10k_wowlan_support; | ||
274 | |||
275 | return 0; | ||
276 | } | ||
diff --git a/drivers/net/wireless/ath/ath10k/wow.h b/drivers/net/wireless/ath/ath10k/wow.h new file mode 100644 index 000000000000..21c43c9c91e0 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wow.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | #ifndef _WOW_H_ | ||
17 | #define _WOW_H_ | ||
18 | |||
19 | struct ath10k_wow { | ||
20 | struct completion wakeup_completed; | ||
21 | }; | ||
22 | |||
23 | #ifdef CONFIG_PM | ||
24 | |||
25 | int ath10k_wow_init(struct ath10k *ar); | ||
26 | int ath10k_wow_op_suspend(struct ieee80211_hw *hw, | ||
27 | struct cfg80211_wowlan *wowlan); | ||
28 | int ath10k_wow_op_resume(struct ieee80211_hw *hw); | ||
29 | |||
30 | #else | ||
31 | |||
32 | static inline int ath10k_wow_init(struct ath10k *ar) | ||
33 | { | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | #endif /* CONFIG_PM */ | ||
38 | #endif /* _WOW_H_ */ | ||