xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/wow.c (revision 828853ac58265c93249b53ba81782213962d5d4e)
15fd3ac3cSJanusz Dziedzic /*
28b1083d6SKalle Valo  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
3fa3440faSWen Gong  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
45fd3ac3cSJanusz Dziedzic  *
55fd3ac3cSJanusz Dziedzic  * Permission to use, copy, modify, and/or distribute this software for any
65fd3ac3cSJanusz Dziedzic  * purpose with or without fee is hereby granted, provided that the above
75fd3ac3cSJanusz Dziedzic  * copyright notice and this permission notice appear in all copies.
85fd3ac3cSJanusz Dziedzic  *
95fd3ac3cSJanusz Dziedzic  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105fd3ac3cSJanusz Dziedzic  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115fd3ac3cSJanusz Dziedzic  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125fd3ac3cSJanusz Dziedzic  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135fd3ac3cSJanusz Dziedzic  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145fd3ac3cSJanusz Dziedzic  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155fd3ac3cSJanusz Dziedzic  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165fd3ac3cSJanusz Dziedzic  */
175fd3ac3cSJanusz Dziedzic 
185fd3ac3cSJanusz Dziedzic #include "mac.h"
195fd3ac3cSJanusz Dziedzic 
205fd3ac3cSJanusz Dziedzic #include <net/mac80211.h>
215fd3ac3cSJanusz Dziedzic #include "hif.h"
225fd3ac3cSJanusz Dziedzic #include "core.h"
235fd3ac3cSJanusz Dziedzic #include "debug.h"
245fd3ac3cSJanusz Dziedzic #include "wmi.h"
255fd3ac3cSJanusz Dziedzic #include "wmi-ops.h"
265fd3ac3cSJanusz Dziedzic 
275fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = {
285fd3ac3cSJanusz Dziedzic 	.flags = WIPHY_WOWLAN_DISCONNECT |
295fd3ac3cSJanusz Dziedzic 		 WIPHY_WOWLAN_MAGIC_PKT,
3025c86619SJanusz Dziedzic 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
3125c86619SJanusz Dziedzic 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
3225c86619SJanusz Dziedzic 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
335fd3ac3cSJanusz Dziedzic };
345fd3ac3cSJanusz Dziedzic 
355fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
365fd3ac3cSJanusz Dziedzic {
375fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
385fd3ac3cSJanusz Dziedzic 	int i, ret;
395fd3ac3cSJanusz Dziedzic 
405fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
415fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
425fd3ac3cSJanusz Dziedzic 		if (ret) {
435fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
445fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
455fd3ac3cSJanusz Dziedzic 			return ret;
465fd3ac3cSJanusz Dziedzic 		}
475fd3ac3cSJanusz Dziedzic 	}
485fd3ac3cSJanusz Dziedzic 
4925c86619SJanusz Dziedzic 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
5025c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
5125c86619SJanusz Dziedzic 		if (ret) {
5225c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
5325c86619SJanusz Dziedzic 				    i, arvif->vdev_id, ret);
5425c86619SJanusz Dziedzic 			return ret;
5525c86619SJanusz Dziedzic 		}
5625c86619SJanusz Dziedzic 	}
5725c86619SJanusz Dziedzic 
585fd3ac3cSJanusz Dziedzic 	return 0;
595fd3ac3cSJanusz Dziedzic }
605fd3ac3cSJanusz Dziedzic 
615fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar)
625fd3ac3cSJanusz Dziedzic {
635fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
645fd3ac3cSJanusz Dziedzic 	int ret;
655fd3ac3cSJanusz Dziedzic 
665fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
675fd3ac3cSJanusz Dziedzic 
685fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
695fd3ac3cSJanusz Dziedzic 		ret = ath10k_wow_vif_cleanup(arvif);
705fd3ac3cSJanusz Dziedzic 		if (ret) {
715fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
725fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
735fd3ac3cSJanusz Dziedzic 			return ret;
745fd3ac3cSJanusz Dziedzic 		}
755fd3ac3cSJanusz Dziedzic 	}
765fd3ac3cSJanusz Dziedzic 
775fd3ac3cSJanusz Dziedzic 	return 0;
785fd3ac3cSJanusz Dziedzic }
795fd3ac3cSJanusz Dziedzic 
80fa3440faSWen Gong /**
81fa3440faSWen Gong  * Convert a 802.3 format to a 802.11 format.
82fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
83fa3440faSWen Gong  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
84fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
85fa3440faSWen Gong  *                |__         |_______    |____________  |________
86fa3440faSWen Gong  *                   |                |                |          |
87fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
88fa3440faSWen Gong  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
89fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
90fa3440faSWen Gong  */
91fa3440faSWen Gong static void ath10k_wow_convert_8023_to_80211
92fa3440faSWen Gong 					(struct cfg80211_pkt_pattern *new,
93fa3440faSWen Gong 					const struct cfg80211_pkt_pattern *old)
94fa3440faSWen Gong {
95fa3440faSWen Gong 	u8 hdr_8023_pattern[ETH_HLEN] = {};
96fa3440faSWen Gong 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
97fa3440faSWen Gong 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
98fa3440faSWen Gong 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
99fa3440faSWen Gong 
100fa3440faSWen Gong 	int total_len = old->pkt_offset + old->pattern_len;
101fa3440faSWen Gong 	int hdr_80211_end_offset;
102fa3440faSWen Gong 
103fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_pattern =
104fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
105fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_mask =
106fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
107fa3440faSWen Gong 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
108fa3440faSWen Gong 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
109fa3440faSWen Gong 	int hdr_len = sizeof(*new_hdr_pattern);
110fa3440faSWen Gong 
111fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_pattern =
112fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
113fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_mask =
114fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
115fa3440faSWen Gong 	int rfc_len = sizeof(*new_rfc_pattern);
116fa3440faSWen Gong 
117fa3440faSWen Gong 	memcpy(hdr_8023_pattern + old->pkt_offset,
118fa3440faSWen Gong 	       old->pattern, ETH_HLEN - old->pkt_offset);
119fa3440faSWen Gong 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
120fa3440faSWen Gong 	       old->mask, ETH_HLEN - old->pkt_offset);
121fa3440faSWen Gong 
122fa3440faSWen Gong 	/* Copy destination address */
123fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
124fa3440faSWen Gong 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
125fa3440faSWen Gong 
126fa3440faSWen Gong 	/* Copy source address */
127fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
128fa3440faSWen Gong 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
129fa3440faSWen Gong 
130fa3440faSWen Gong 	/* Copy logic link type */
131fa3440faSWen Gong 	memcpy(&new_rfc_pattern->snap_type,
132fa3440faSWen Gong 	       &old_hdr_pattern->h_proto,
133fa3440faSWen Gong 	       sizeof(old_hdr_pattern->h_proto));
134fa3440faSWen Gong 	memcpy(&new_rfc_mask->snap_type,
135fa3440faSWen Gong 	       &old_hdr_mask->h_proto,
136fa3440faSWen Gong 	       sizeof(old_hdr_mask->h_proto));
137fa3440faSWen Gong 
138fa3440faSWen Gong 	/* Caculate new pkt_offset */
139fa3440faSWen Gong 	if (old->pkt_offset < ETH_ALEN)
140fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
141fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
142fa3440faSWen Gong 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
143fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
144fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3) -
145fa3440faSWen Gong 			offsetof(struct ethhdr, h_source);
146fa3440faSWen Gong 	else
147fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
148fa3440faSWen Gong 
149fa3440faSWen Gong 	/* Caculate new hdr end offset */
150fa3440faSWen Gong 	if (total_len > ETH_HLEN)
151fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len;
152fa3440faSWen Gong 	else if (total_len > offsetof(struct ethhdr, h_proto))
153fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
154fa3440faSWen Gong 	else if (total_len > ETH_ALEN)
155fa3440faSWen Gong 		hdr_80211_end_offset = total_len - ETH_ALEN +
156fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3);
157fa3440faSWen Gong 	else
158fa3440faSWen Gong 		hdr_80211_end_offset = total_len +
159fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
160fa3440faSWen Gong 
161fa3440faSWen Gong 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
162fa3440faSWen Gong 
163fa3440faSWen Gong 	memcpy((u8 *)new->pattern,
164fa3440faSWen Gong 	       hdr_80211_pattern + new->pkt_offset,
165fa3440faSWen Gong 	       new->pattern_len);
166fa3440faSWen Gong 	memcpy((u8 *)new->mask,
167fa3440faSWen Gong 	       hdr_80211_bit_mask + new->pkt_offset,
168fa3440faSWen Gong 	       new->pattern_len);
169fa3440faSWen Gong 
170fa3440faSWen Gong 	if (total_len > ETH_HLEN) {
171fa3440faSWen Gong 		/* Copy frame body */
172fa3440faSWen Gong 		memcpy((u8 *)new->pattern + new->pattern_len,
173fa3440faSWen Gong 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
174fa3440faSWen Gong 		       total_len - ETH_HLEN);
175fa3440faSWen Gong 		memcpy((u8 *)new->mask + new->pattern_len,
176fa3440faSWen Gong 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
177fa3440faSWen Gong 		       total_len - ETH_HLEN);
178fa3440faSWen Gong 
179fa3440faSWen Gong 		new->pattern_len += total_len - ETH_HLEN;
180fa3440faSWen Gong 	}
181fa3440faSWen Gong }
182fa3440faSWen Gong 
1835fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
1845fd3ac3cSJanusz Dziedzic 				      struct cfg80211_wowlan *wowlan)
1855fd3ac3cSJanusz Dziedzic {
1865fd3ac3cSJanusz Dziedzic 	int ret, i;
1875fd3ac3cSJanusz Dziedzic 	unsigned long wow_mask = 0;
1885fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
18925c86619SJanusz Dziedzic 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
19025c86619SJanusz Dziedzic 	int pattern_id = 0;
1915fd3ac3cSJanusz Dziedzic 
1925fd3ac3cSJanusz Dziedzic 	/* Setup requested WOW features */
1935fd3ac3cSJanusz Dziedzic 	switch (arvif->vdev_type) {
1945fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_IBSS:
1955fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
1965fd3ac3cSJanusz Dziedzic 		 /* fall through */
1975fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_AP:
1985fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
1995fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
2005fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
2015fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
2025fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
2035fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_HTT_EVENT, &wow_mask);
2045fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
2055fd3ac3cSJanusz Dziedzic 		break;
2065fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_STA:
2075fd3ac3cSJanusz Dziedzic 		if (wowlan->disconnect) {
2085fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
2095fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
2105fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
2115fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
2125fd3ac3cSJanusz Dziedzic 		}
2135fd3ac3cSJanusz Dziedzic 
2145fd3ac3cSJanusz Dziedzic 		if (wowlan->magic_pkt)
2155fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
2165fd3ac3cSJanusz Dziedzic 		break;
2175fd3ac3cSJanusz Dziedzic 	default:
2185fd3ac3cSJanusz Dziedzic 		break;
2195fd3ac3cSJanusz Dziedzic 	}
2205fd3ac3cSJanusz Dziedzic 
22125c86619SJanusz Dziedzic 	for (i = 0; i < wowlan->n_patterns; i++) {
22225c86619SJanusz Dziedzic 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
223fa3440faSWen Gong 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
224fa3440faSWen Gong 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
225fa3440faSWen Gong 		struct cfg80211_pkt_pattern new_pattern = {};
226fa3440faSWen Gong 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
22725c86619SJanusz Dziedzic 		int j;
22825c86619SJanusz Dziedzic 
229fa3440faSWen Gong 		new_pattern.pattern = ath_pattern;
230fa3440faSWen Gong 		new_pattern.mask = ath_bitmask;
23125c86619SJanusz Dziedzic 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
23225c86619SJanusz Dziedzic 			continue;
23325c86619SJanusz Dziedzic 		/* convert bytemask to bitmask */
23425c86619SJanusz Dziedzic 		for (j = 0; j < patterns[i].pattern_len; j++)
23525c86619SJanusz Dziedzic 			if (patterns[i].mask[j / 8] & BIT(j % 8))
23625c86619SJanusz Dziedzic 				bitmask[j] = 0xff;
237fa3440faSWen Gong 		old_pattern.mask = bitmask;
238fa3440faSWen Gong 		new_pattern = old_pattern;
239fa3440faSWen Gong 
240fa3440faSWen Gong 		if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
241fa3440faSWen Gong 			if (patterns[i].pkt_offset < ETH_HLEN)
242fa3440faSWen Gong 				ath10k_wow_convert_8023_to_80211(&new_pattern,
243fa3440faSWen Gong 								 &old_pattern);
244fa3440faSWen Gong 			else
245fa3440faSWen Gong 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
246fa3440faSWen Gong 		}
247fa3440faSWen Gong 
248fa3440faSWen Gong 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
249fa3440faSWen Gong 			return -EINVAL;
25025c86619SJanusz Dziedzic 
25125c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
25225c86619SJanusz Dziedzic 						 pattern_id,
253fa3440faSWen Gong 						 new_pattern.pattern,
254fa3440faSWen Gong 						 new_pattern.mask,
255fa3440faSWen Gong 						 new_pattern.pattern_len,
256fa3440faSWen Gong 						 new_pattern.pkt_offset);
25725c86619SJanusz Dziedzic 		if (ret) {
25825c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
25925c86619SJanusz Dziedzic 				    pattern_id,
26025c86619SJanusz Dziedzic 				    arvif->vdev_id, ret);
26125c86619SJanusz Dziedzic 			return ret;
26225c86619SJanusz Dziedzic 		}
26325c86619SJanusz Dziedzic 
26425c86619SJanusz Dziedzic 		pattern_id++;
26525c86619SJanusz Dziedzic 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
26625c86619SJanusz Dziedzic 	}
26725c86619SJanusz Dziedzic 
2685fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
2695fd3ac3cSJanusz Dziedzic 		if (!test_bit(i, &wow_mask))
2705fd3ac3cSJanusz Dziedzic 			continue;
2715fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
2725fd3ac3cSJanusz Dziedzic 		if (ret) {
2735fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
2745fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
2755fd3ac3cSJanusz Dziedzic 			return ret;
2765fd3ac3cSJanusz Dziedzic 		}
2775fd3ac3cSJanusz Dziedzic 	}
2785fd3ac3cSJanusz Dziedzic 
2795fd3ac3cSJanusz Dziedzic 	return 0;
2805fd3ac3cSJanusz Dziedzic }
2815fd3ac3cSJanusz Dziedzic 
2825fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar,
2835fd3ac3cSJanusz Dziedzic 				  struct cfg80211_wowlan *wowlan)
2845fd3ac3cSJanusz Dziedzic {
2855fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
2865fd3ac3cSJanusz Dziedzic 	int ret;
2875fd3ac3cSJanusz Dziedzic 
2885fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
2895fd3ac3cSJanusz Dziedzic 
2905fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
2915fd3ac3cSJanusz Dziedzic 		ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
2925fd3ac3cSJanusz Dziedzic 		if (ret) {
2935fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
2945fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
2955fd3ac3cSJanusz Dziedzic 			return ret;
2965fd3ac3cSJanusz Dziedzic 		}
2975fd3ac3cSJanusz Dziedzic 	}
2985fd3ac3cSJanusz Dziedzic 
2995fd3ac3cSJanusz Dziedzic 	return 0;
3005fd3ac3cSJanusz Dziedzic }
3015fd3ac3cSJanusz Dziedzic 
3025fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar)
3035fd3ac3cSJanusz Dziedzic {
3045fd3ac3cSJanusz Dziedzic 	int ret;
3055fd3ac3cSJanusz Dziedzic 
3065fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
3075fd3ac3cSJanusz Dziedzic 
3085fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->target_suspend);
3095fd3ac3cSJanusz Dziedzic 
3105fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_enable(ar);
3115fd3ac3cSJanusz Dziedzic 	if (ret) {
3125fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
3135fd3ac3cSJanusz Dziedzic 		return ret;
3145fd3ac3cSJanusz Dziedzic 	}
3155fd3ac3cSJanusz Dziedzic 
3165fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
3175fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
3185fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for suspend completion\n");
3195fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
3205fd3ac3cSJanusz Dziedzic 	}
3215fd3ac3cSJanusz Dziedzic 
3225fd3ac3cSJanusz Dziedzic 	return 0;
3235fd3ac3cSJanusz Dziedzic }
3245fd3ac3cSJanusz Dziedzic 
3255fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar)
3265fd3ac3cSJanusz Dziedzic {
3275fd3ac3cSJanusz Dziedzic 	int ret;
3285fd3ac3cSJanusz Dziedzic 
3295fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
3305fd3ac3cSJanusz Dziedzic 
3315fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->wow.wakeup_completed);
3325fd3ac3cSJanusz Dziedzic 
3335fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_host_wakeup_ind(ar);
3345fd3ac3cSJanusz Dziedzic 	if (ret) {
3355fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
3365fd3ac3cSJanusz Dziedzic 			    ret);
3375fd3ac3cSJanusz Dziedzic 		return ret;
3385fd3ac3cSJanusz Dziedzic 	}
3395fd3ac3cSJanusz Dziedzic 
3405fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
3415fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
3425fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
3435fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
3445fd3ac3cSJanusz Dziedzic 	}
3455fd3ac3cSJanusz Dziedzic 
3465fd3ac3cSJanusz Dziedzic 	return 0;
3475fd3ac3cSJanusz Dziedzic }
3485fd3ac3cSJanusz Dziedzic 
3495fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
3505fd3ac3cSJanusz Dziedzic 			  struct cfg80211_wowlan *wowlan)
3515fd3ac3cSJanusz Dziedzic {
3525fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
3535fd3ac3cSJanusz Dziedzic 	int ret;
3545fd3ac3cSJanusz Dziedzic 
3555fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
3565fd3ac3cSJanusz Dziedzic 
3575fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
358c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
3595fd3ac3cSJanusz Dziedzic 		ret = 1;
3605fd3ac3cSJanusz Dziedzic 		goto exit;
3615fd3ac3cSJanusz Dziedzic 	}
3625fd3ac3cSJanusz Dziedzic 
3635fd3ac3cSJanusz Dziedzic 	ret =  ath10k_wow_cleanup(ar);
3645fd3ac3cSJanusz Dziedzic 	if (ret) {
3655fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
3665fd3ac3cSJanusz Dziedzic 			    ret);
3675fd3ac3cSJanusz Dziedzic 		goto exit;
3685fd3ac3cSJanusz Dziedzic 	}
3695fd3ac3cSJanusz Dziedzic 
3705fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_set_wakeups(ar, wowlan);
3715fd3ac3cSJanusz Dziedzic 	if (ret) {
3725fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
3735fd3ac3cSJanusz Dziedzic 			    ret);
3745fd3ac3cSJanusz Dziedzic 		goto cleanup;
3755fd3ac3cSJanusz Dziedzic 	}
3765fd3ac3cSJanusz Dziedzic 
377*828853acSWen Gong 	ath10k_mac_wait_tx_complete(ar);
378*828853acSWen Gong 
3795fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_enable(ar);
3805fd3ac3cSJanusz Dziedzic 	if (ret) {
3815fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to start wow: %d\n", ret);
3825fd3ac3cSJanusz Dziedzic 		goto cleanup;
3835fd3ac3cSJanusz Dziedzic 	}
3845fd3ac3cSJanusz Dziedzic 
3855fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_suspend(ar);
3865fd3ac3cSJanusz Dziedzic 	if (ret) {
3875fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
3885fd3ac3cSJanusz Dziedzic 		goto wakeup;
3895fd3ac3cSJanusz Dziedzic 	}
3905fd3ac3cSJanusz Dziedzic 
3915fd3ac3cSJanusz Dziedzic 	goto exit;
3925fd3ac3cSJanusz Dziedzic 
3935fd3ac3cSJanusz Dziedzic wakeup:
3945fd3ac3cSJanusz Dziedzic 	ath10k_wow_wakeup(ar);
3955fd3ac3cSJanusz Dziedzic 
3965fd3ac3cSJanusz Dziedzic cleanup:
3975fd3ac3cSJanusz Dziedzic 	ath10k_wow_cleanup(ar);
3985fd3ac3cSJanusz Dziedzic 
3995fd3ac3cSJanusz Dziedzic exit:
4005fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
4015fd3ac3cSJanusz Dziedzic 	return ret ? 1 : 0;
4025fd3ac3cSJanusz Dziedzic }
4035fd3ac3cSJanusz Dziedzic 
404393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
405393b706cSRyan Hsu {
406393b706cSRyan Hsu 	struct ath10k *ar = hw->priv;
407393b706cSRyan Hsu 
408393b706cSRyan Hsu 	mutex_lock(&ar->conf_mutex);
409393b706cSRyan Hsu 	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
410393b706cSRyan Hsu 		     ar->running_fw->fw_file.fw_features)) {
411393b706cSRyan Hsu 		device_set_wakeup_enable(ar->dev, enabled);
412393b706cSRyan Hsu 	}
413393b706cSRyan Hsu 	mutex_unlock(&ar->conf_mutex);
414393b706cSRyan Hsu }
415393b706cSRyan Hsu 
4165fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw)
4175fd3ac3cSJanusz Dziedzic {
4185fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
4195fd3ac3cSJanusz Dziedzic 	int ret;
4205fd3ac3cSJanusz Dziedzic 
4215fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
4225fd3ac3cSJanusz Dziedzic 
4235fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
424c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
4255fd3ac3cSJanusz Dziedzic 		ret = 1;
4265fd3ac3cSJanusz Dziedzic 		goto exit;
4275fd3ac3cSJanusz Dziedzic 	}
4285fd3ac3cSJanusz Dziedzic 
4295fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_resume(ar);
4305fd3ac3cSJanusz Dziedzic 	if (ret) {
4315fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
4325fd3ac3cSJanusz Dziedzic 		goto exit;
4335fd3ac3cSJanusz Dziedzic 	}
4345fd3ac3cSJanusz Dziedzic 
4355fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_wakeup(ar);
4365fd3ac3cSJanusz Dziedzic 	if (ret)
4375fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
4385fd3ac3cSJanusz Dziedzic 
4395fd3ac3cSJanusz Dziedzic exit:
4406f7429c2SMichal Kazior 	if (ret) {
4416f7429c2SMichal Kazior 		switch (ar->state) {
4426f7429c2SMichal Kazior 		case ATH10K_STATE_ON:
4436f7429c2SMichal Kazior 			ar->state = ATH10K_STATE_RESTARTING;
4446f7429c2SMichal Kazior 			ret = 1;
4456f7429c2SMichal Kazior 			break;
4466f7429c2SMichal Kazior 		case ATH10K_STATE_OFF:
4476f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTING:
4486f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTED:
4496f7429c2SMichal Kazior 		case ATH10K_STATE_UTF:
4506f7429c2SMichal Kazior 		case ATH10K_STATE_WEDGED:
4516f7429c2SMichal Kazior 			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
4526f7429c2SMichal Kazior 				    ar->state);
4536f7429c2SMichal Kazior 			ret = -EIO;
4546f7429c2SMichal Kazior 			break;
4556f7429c2SMichal Kazior 		}
4566f7429c2SMichal Kazior 	}
4576f7429c2SMichal Kazior 
4585fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
4596f7429c2SMichal Kazior 	return ret;
4605fd3ac3cSJanusz Dziedzic }
4615fd3ac3cSJanusz Dziedzic 
4625fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar)
4635fd3ac3cSJanusz Dziedzic {
464c4cdf753SKalle Valo 	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
465c4cdf753SKalle Valo 		      ar->running_fw->fw_file.fw_features))
4665fd3ac3cSJanusz Dziedzic 		return 0;
4675fd3ac3cSJanusz Dziedzic 
4685fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
4695fd3ac3cSJanusz Dziedzic 		return -EINVAL;
4705fd3ac3cSJanusz Dziedzic 
47125c86619SJanusz Dziedzic 	ar->wow.wowlan_support = ath10k_wowlan_support;
472fa3440faSWen Gong 
473fa3440faSWen Gong 	if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
474fa3440faSWen Gong 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
475fa3440faSWen Gong 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
476fa3440faSWen Gong 	}
477fa3440faSWen Gong 
47825c86619SJanusz Dziedzic 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
47925c86619SJanusz Dziedzic 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
4805fd3ac3cSJanusz Dziedzic 
481393b706cSRyan Hsu 	device_set_wakeup_capable(ar->dev, true);
482393b706cSRyan Hsu 
4835fd3ac3cSJanusz Dziedzic 	return 0;
4845fd3ac3cSJanusz Dziedzic }
485