Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (c) 2015-2017 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
 26static 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,
 32};
 33
 34static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
 35{
 36	struct ath10k *ar = arvif->ar;
 37	int i, ret;
 38
 39	for (i = 0; i < WOW_EVENT_MAX; i++) {
 40		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
 41		if (ret) {
 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);
 44			return ret;
 45		}
 46	}
 47
 48	for (i = 0; i < ar->wow.max_num_patterns; i++) {
 49		ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
 50		if (ret) {
 51			ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
 52				    i, arvif->vdev_id, ret);
 53			return ret;
 54		}
 55	}
 56
 57	return 0;
 58}
 59
 60static int ath10k_wow_cleanup(struct ath10k *ar)
 61{
 62	struct ath10k_vif *arvif;
 63	int ret;
 64
 65	lockdep_assert_held(&ar->conf_mutex);
 66
 67	list_for_each_entry(arvif, &ar->arvifs, list) {
 68		ret = ath10k_wow_vif_cleanup(arvif);
 69		if (ret) {
 70			ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
 71				    arvif->vdev_id, ret);
 72			return ret;
 73		}
 74	}
 75
 76	return 0;
 77}
 78
 79static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
 80				      struct cfg80211_wowlan *wowlan)
 81{
 82	int ret, i;
 83	unsigned long wow_mask = 0;
 84	struct ath10k *ar = arvif->ar;
 85	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
 86	int pattern_id = 0;
 87
 88	/* Setup requested WOW features */
 89	switch (arvif->vdev_type) {
 90	case WMI_VDEV_TYPE_IBSS:
 91		__set_bit(WOW_BEACON_EVENT, &wow_mask);
 92		 /* fall through */
 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);
101		break;
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);
108		}
109
110		if (wowlan->magic_pkt)
111			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
112		break;
113	default:
114		break;
115	}
116
117	for (i = 0; i < wowlan->n_patterns; i++) {
118		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
119		int j;
120
121		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
122			continue;
123
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))
127				bitmask[j] = 0xff;
128
129		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
130						 pattern_id,
131						 patterns[i].pattern,
132						 bitmask,
133						 patterns[i].pattern_len,
134						 patterns[i].pkt_offset);
135		if (ret) {
136			ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
137				    pattern_id,
138				    arvif->vdev_id, ret);
139			return ret;
140		}
141
142		pattern_id++;
143		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
144	}
145
146	for (i = 0; i < WOW_EVENT_MAX; i++) {
147		if (!test_bit(i, &wow_mask))
148			continue;
149		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
150		if (ret) {
151			ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
152				    wow_wakeup_event(i), arvif->vdev_id, ret);
153			return ret;
154		}
155	}
156
157	return 0;
158}
159
160static int ath10k_wow_set_wakeups(struct ath10k *ar,
161				  struct cfg80211_wowlan *wowlan)
162{
163	struct ath10k_vif *arvif;
164	int ret;
165
166	lockdep_assert_held(&ar->conf_mutex);
167
168	list_for_each_entry(arvif, &ar->arvifs, list) {
169		ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
170		if (ret) {
171			ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
172				    arvif->vdev_id, ret);
173			return ret;
174		}
175	}
176
177	return 0;
178}
179
180static int ath10k_wow_enable(struct ath10k *ar)
181{
182	int ret;
183
184	lockdep_assert_held(&ar->conf_mutex);
185
186	reinit_completion(&ar->target_suspend);
187
188	ret = ath10k_wmi_wow_enable(ar);
189	if (ret) {
190		ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
191		return ret;
192	}
193
194	ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
195	if (ret == 0) {
196		ath10k_warn(ar, "timed out while waiting for suspend completion\n");
197		return -ETIMEDOUT;
198	}
199
200	return 0;
201}
202
203static int ath10k_wow_wakeup(struct ath10k *ar)
204{
205	int ret;
206
207	lockdep_assert_held(&ar->conf_mutex);
208
209	reinit_completion(&ar->wow.wakeup_completed);
210
211	ret = ath10k_wmi_wow_host_wakeup_ind(ar);
212	if (ret) {
213		ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
214			    ret);
215		return ret;
216	}
217
218	ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
219	if (ret == 0) {
220		ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
221		return -ETIMEDOUT;
222	}
223
224	return 0;
225}
226
227int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
228			  struct cfg80211_wowlan *wowlan)
229{
230	struct ath10k *ar = hw->priv;
231	int ret;
232
233	mutex_lock(&ar->conf_mutex);
234
235	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
236			      ar->running_fw->fw_file.fw_features))) {
237		ret = 1;
238		goto exit;
239	}
240
241	ret =  ath10k_wow_cleanup(ar);
242	if (ret) {
243		ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
244			    ret);
245		goto exit;
246	}
247
248	ret = ath10k_wow_set_wakeups(ar, wowlan);
249	if (ret) {
250		ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
251			    ret);
252		goto cleanup;
253	}
254
255	ret = ath10k_wow_enable(ar);
256	if (ret) {
257		ath10k_warn(ar, "failed to start wow: %d\n", ret);
258		goto cleanup;
259	}
260
261	ret = ath10k_hif_suspend(ar);
262	if (ret) {
263		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
264		goto wakeup;
265	}
266
267	goto exit;
268
269wakeup:
270	ath10k_wow_wakeup(ar);
271
272cleanup:
273	ath10k_wow_cleanup(ar);
274
275exit:
276	mutex_unlock(&ar->conf_mutex);
277	return ret ? 1 : 0;
278}
279
280void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
281{
282	struct ath10k *ar = hw->priv;
283
284	mutex_lock(&ar->conf_mutex);
285	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
286		     ar->running_fw->fw_file.fw_features)) {
287		device_set_wakeup_enable(ar->dev, enabled);
288	}
289	mutex_unlock(&ar->conf_mutex);
290}
291
292int ath10k_wow_op_resume(struct ieee80211_hw *hw)
293{
294	struct ath10k *ar = hw->priv;
295	int ret;
296
297	mutex_lock(&ar->conf_mutex);
298
299	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
300			      ar->running_fw->fw_file.fw_features))) {
301		ret = 1;
302		goto exit;
303	}
304
305	ret = ath10k_hif_resume(ar);
306	if (ret) {
307		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
308		goto exit;
309	}
310
311	ret = ath10k_wow_wakeup(ar);
312	if (ret)
313		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
314
315exit:
316	if (ret) {
317		switch (ar->state) {
318		case ATH10K_STATE_ON:
319			ar->state = ATH10K_STATE_RESTARTING;
320			ret = 1;
321			break;
322		case ATH10K_STATE_OFF:
323		case ATH10K_STATE_RESTARTING:
324		case ATH10K_STATE_RESTARTED:
325		case ATH10K_STATE_UTF:
326		case ATH10K_STATE_WEDGED:
327			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
328				    ar->state);
329			ret = -EIO;
330			break;
331		}
332	}
333
334	mutex_unlock(&ar->conf_mutex);
335	return ret;
336}
337
338int ath10k_wow_init(struct ath10k *ar)
339{
340	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
341		      ar->running_fw->fw_file.fw_features))
342		return 0;
343
344	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
345		return -EINVAL;
346
347	ar->wow.wowlan_support = ath10k_wowlan_support;
348	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
349	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
350
351	device_set_wakeup_capable(ar->dev, true);
352
353	return 0;
354}