2 * Copyright (c) 2015 Qualcomm Atheros, Inc.
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.
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.
19 #include <net/mac80211.h>
26 static const struct wiphy_wowlan_support ath10k_wowlan_support
= {
27 .flags
= WIPHY_WOWLAN_DISCONNECT
|
28 WIPHY_WOWLAN_MAGIC_PKT
,
29 .pattern_min_len
= WOW_MIN_PATTERN_SIZE
,
30 .pattern_max_len
= WOW_MAX_PATTERN_SIZE
,
31 .max_pkt_offset
= WOW_MAX_PKT_OFFSET
,
34 static int ath10k_wow_vif_cleanup(struct ath10k_vif
*arvif
)
36 struct ath10k
*ar
= arvif
->ar
;
39 for (i
= 0; i
< WOW_EVENT_MAX
; i
++) {
40 ret
= ath10k_wmi_wow_add_wakeup_event(ar
, arvif
->vdev_id
, i
, 0);
42 ath10k_warn(ar
, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
43 wow_wakeup_event(i
), arvif
->vdev_id
, ret
);
48 for (i
= 0; i
< ar
->wow
.max_num_patterns
; i
++) {
49 ret
= ath10k_wmi_wow_del_pattern(ar
, arvif
->vdev_id
, i
);
51 ath10k_warn(ar
, "failed to delete wow pattern %d for vdev %i: %d\n",
52 i
, arvif
->vdev_id
, ret
);
60 static int ath10k_wow_cleanup(struct ath10k
*ar
)
62 struct ath10k_vif
*arvif
;
65 lockdep_assert_held(&ar
->conf_mutex
);
67 list_for_each_entry(arvif
, &ar
->arvifs
, list
) {
68 ret
= ath10k_wow_vif_cleanup(arvif
);
70 ath10k_warn(ar
, "failed to clean wow wakeups on vdev %i: %d\n",
79 static int ath10k_vif_wow_set_wakeups(struct ath10k_vif
*arvif
,
80 struct cfg80211_wowlan
*wowlan
)
83 unsigned long wow_mask
= 0;
84 struct ath10k
*ar
= arvif
->ar
;
85 const struct cfg80211_pkt_pattern
*patterns
= wowlan
->patterns
;
88 /* Setup requested WOW features */
89 switch (arvif
->vdev_type
) {
90 case WMI_VDEV_TYPE_IBSS
:
91 __set_bit(WOW_BEACON_EVENT
, &wow_mask
);
93 case WMI_VDEV_TYPE_AP
:
94 __set_bit(WOW_DEAUTH_RECVD_EVENT
, &wow_mask
);
95 __set_bit(WOW_DISASSOC_RECVD_EVENT
, &wow_mask
);
96 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT
, &wow_mask
);
97 __set_bit(WOW_AUTH_REQ_EVENT
, &wow_mask
);
98 __set_bit(WOW_ASSOC_REQ_EVENT
, &wow_mask
);
99 __set_bit(WOW_HTT_EVENT
, &wow_mask
);
100 __set_bit(WOW_RA_MATCH_EVENT
, &wow_mask
);
102 case WMI_VDEV_TYPE_STA
:
103 if (wowlan
->disconnect
) {
104 __set_bit(WOW_DEAUTH_RECVD_EVENT
, &wow_mask
);
105 __set_bit(WOW_DISASSOC_RECVD_EVENT
, &wow_mask
);
106 __set_bit(WOW_BMISS_EVENT
, &wow_mask
);
107 __set_bit(WOW_CSA_IE_EVENT
, &wow_mask
);
110 if (wowlan
->magic_pkt
)
111 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT
, &wow_mask
);
117 for (i
= 0; i
< wowlan
->n_patterns
; i
++) {
118 u8 bitmask
[WOW_MAX_PATTERN_SIZE
] = {};
121 if (patterns
[i
].pattern_len
> WOW_MAX_PATTERN_SIZE
)
124 /* convert bytemask to bitmask */
125 for (j
= 0; j
< patterns
[i
].pattern_len
; j
++)
126 if (patterns
[i
].mask
[j
/ 8] & BIT(j
% 8))
129 ret
= ath10k_wmi_wow_add_pattern(ar
, arvif
->vdev_id
,
133 patterns
[i
].pattern_len
,
134 patterns
[i
].pkt_offset
);
136 ath10k_warn(ar
, "failed to add pattern %i to vdev %i: %d\n",
138 arvif
->vdev_id
, ret
);
143 __set_bit(WOW_PATTERN_MATCH_EVENT
, &wow_mask
);
146 for (i
= 0; i
< WOW_EVENT_MAX
; i
++) {
147 if (!test_bit(i
, &wow_mask
))
149 ret
= ath10k_wmi_wow_add_wakeup_event(ar
, arvif
->vdev_id
, i
, 1);
151 ath10k_warn(ar
, "failed to enable wakeup event %s on vdev %i: %d\n",
152 wow_wakeup_event(i
), arvif
->vdev_id
, ret
);
160 static int ath10k_wow_set_wakeups(struct ath10k
*ar
,
161 struct cfg80211_wowlan
*wowlan
)
163 struct ath10k_vif
*arvif
;
166 lockdep_assert_held(&ar
->conf_mutex
);
168 list_for_each_entry(arvif
, &ar
->arvifs
, list
) {
169 ret
= ath10k_vif_wow_set_wakeups(arvif
, wowlan
);
171 ath10k_warn(ar
, "failed to set wow wakeups on vdev %i: %d\n",
172 arvif
->vdev_id
, ret
);
180 static int ath10k_wow_enable(struct ath10k
*ar
)
184 lockdep_assert_held(&ar
->conf_mutex
);
186 reinit_completion(&ar
->target_suspend
);
188 ret
= ath10k_wmi_wow_enable(ar
);
190 ath10k_warn(ar
, "failed to issue wow enable: %d\n", ret
);
194 ret
= wait_for_completion_timeout(&ar
->target_suspend
, 3 * HZ
);
196 ath10k_warn(ar
, "timed out while waiting for suspend completion\n");
203 static int ath10k_wow_wakeup(struct ath10k
*ar
)
207 lockdep_assert_held(&ar
->conf_mutex
);
209 reinit_completion(&ar
->wow
.wakeup_completed
);
211 ret
= ath10k_wmi_wow_host_wakeup_ind(ar
);
213 ath10k_warn(ar
, "failed to send wow wakeup indication: %d\n",
218 ret
= wait_for_completion_timeout(&ar
->wow
.wakeup_completed
, 3 * HZ
);
220 ath10k_warn(ar
, "timed out while waiting for wow wakeup completion\n");
227 int ath10k_wow_op_suspend(struct ieee80211_hw
*hw
,
228 struct cfg80211_wowlan
*wowlan
)
230 struct ath10k
*ar
= hw
->priv
;
233 mutex_lock(&ar
->conf_mutex
);
235 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT
,
241 ret
= ath10k_wow_cleanup(ar
);
243 ath10k_warn(ar
, "failed to clear wow wakeup events: %d\n",
248 ret
= ath10k_wow_set_wakeups(ar
, wowlan
);
250 ath10k_warn(ar
, "failed to set wow wakeup events: %d\n",
255 ret
= ath10k_wow_enable(ar
);
257 ath10k_warn(ar
, "failed to start wow: %d\n", ret
);
261 ret
= ath10k_hif_suspend(ar
);
263 ath10k_warn(ar
, "failed to suspend hif: %d\n", ret
);
270 ath10k_wow_wakeup(ar
);
273 ath10k_wow_cleanup(ar
);
276 mutex_unlock(&ar
->conf_mutex
);
280 int ath10k_wow_op_resume(struct ieee80211_hw
*hw
)
282 struct ath10k
*ar
= hw
->priv
;
285 mutex_lock(&ar
->conf_mutex
);
287 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT
,
293 ret
= ath10k_hif_resume(ar
);
295 ath10k_warn(ar
, "failed to resume hif: %d\n", ret
);
299 ret
= ath10k_wow_wakeup(ar
);
301 ath10k_warn(ar
, "failed to wakeup from wow: %d\n", ret
);
306 case ATH10K_STATE_ON
:
307 ar
->state
= ATH10K_STATE_RESTARTING
;
310 case ATH10K_STATE_OFF
:
311 case ATH10K_STATE_RESTARTING
:
312 case ATH10K_STATE_RESTARTED
:
313 case ATH10K_STATE_UTF
:
314 case ATH10K_STATE_WEDGED
:
315 ath10k_warn(ar
, "encountered unexpected device state %d on resume, cannot recover\n",
322 mutex_unlock(&ar
->conf_mutex
);
326 int ath10k_wow_init(struct ath10k
*ar
)
328 if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT
, ar
->fw_features
))
331 if (WARN_ON(!test_bit(WMI_SERVICE_WOW
, ar
->wmi
.svc_map
)))
334 ar
->wow
.wowlan_support
= ath10k_wowlan_support
;
335 ar
->wow
.wowlan_support
.n_patterns
= ar
->wow
.max_num_patterns
;
336 ar
->hw
->wiphy
->wowlan
= &ar
->wow
.wowlan_support
;