xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/wow.c (revision fa3440fa2fa1531fb2aafb6d51f2bc5ac70a6247)
15fd3ac3cSJanusz Dziedzic /*
28b1083d6SKalle Valo  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
3*fa3440faSWen 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 
80*fa3440faSWen Gong /**
81*fa3440faSWen Gong  * Convert a 802.3 format to a 802.11 format.
82*fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
83*fa3440faSWen Gong  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
84*fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
85*fa3440faSWen Gong  *                |__         |_______    |____________  |________
86*fa3440faSWen Gong  *                   |                |                |          |
87*fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
88*fa3440faSWen Gong  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
89*fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
90*fa3440faSWen Gong  */
91*fa3440faSWen Gong static void ath10k_wow_convert_8023_to_80211
92*fa3440faSWen Gong 					(struct cfg80211_pkt_pattern *new,
93*fa3440faSWen Gong 					const struct cfg80211_pkt_pattern *old)
94*fa3440faSWen Gong {
95*fa3440faSWen Gong 	u8 hdr_8023_pattern[ETH_HLEN] = {};
96*fa3440faSWen Gong 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
97*fa3440faSWen Gong 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
98*fa3440faSWen Gong 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
99*fa3440faSWen Gong 
100*fa3440faSWen Gong 	int total_len = old->pkt_offset + old->pattern_len;
101*fa3440faSWen Gong 	int hdr_80211_end_offset;
102*fa3440faSWen Gong 
103*fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_pattern =
104*fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
105*fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_mask =
106*fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
107*fa3440faSWen Gong 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
108*fa3440faSWen Gong 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
109*fa3440faSWen Gong 	int hdr_len = sizeof(*new_hdr_pattern);
110*fa3440faSWen Gong 
111*fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_pattern =
112*fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
113*fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_mask =
114*fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
115*fa3440faSWen Gong 	int rfc_len = sizeof(*new_rfc_pattern);
116*fa3440faSWen Gong 
117*fa3440faSWen Gong 	memcpy(hdr_8023_pattern + old->pkt_offset,
118*fa3440faSWen Gong 	       old->pattern, ETH_HLEN - old->pkt_offset);
119*fa3440faSWen Gong 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
120*fa3440faSWen Gong 	       old->mask, ETH_HLEN - old->pkt_offset);
121*fa3440faSWen Gong 
122*fa3440faSWen Gong 	/* Copy destination address */
123*fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
124*fa3440faSWen Gong 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
125*fa3440faSWen Gong 
126*fa3440faSWen Gong 	/* Copy source address */
127*fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
128*fa3440faSWen Gong 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
129*fa3440faSWen Gong 
130*fa3440faSWen Gong 	/* Copy logic link type */
131*fa3440faSWen Gong 	memcpy(&new_rfc_pattern->snap_type,
132*fa3440faSWen Gong 	       &old_hdr_pattern->h_proto,
133*fa3440faSWen Gong 	       sizeof(old_hdr_pattern->h_proto));
134*fa3440faSWen Gong 	memcpy(&new_rfc_mask->snap_type,
135*fa3440faSWen Gong 	       &old_hdr_mask->h_proto,
136*fa3440faSWen Gong 	       sizeof(old_hdr_mask->h_proto));
137*fa3440faSWen Gong 
138*fa3440faSWen Gong 	/* Caculate new pkt_offset */
139*fa3440faSWen Gong 	if (old->pkt_offset < ETH_ALEN)
140*fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
141*fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
142*fa3440faSWen Gong 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
143*fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
144*fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3) -
145*fa3440faSWen Gong 			offsetof(struct ethhdr, h_source);
146*fa3440faSWen Gong 	else
147*fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
148*fa3440faSWen Gong 
149*fa3440faSWen Gong 	/* Caculate new hdr end offset */
150*fa3440faSWen Gong 	if (total_len > ETH_HLEN)
151*fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len;
152*fa3440faSWen Gong 	else if (total_len > offsetof(struct ethhdr, h_proto))
153*fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
154*fa3440faSWen Gong 	else if (total_len > ETH_ALEN)
155*fa3440faSWen Gong 		hdr_80211_end_offset = total_len - ETH_ALEN +
156*fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3);
157*fa3440faSWen Gong 	else
158*fa3440faSWen Gong 		hdr_80211_end_offset = total_len +
159*fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
160*fa3440faSWen Gong 
161*fa3440faSWen Gong 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
162*fa3440faSWen Gong 
163*fa3440faSWen Gong 	memcpy((u8 *)new->pattern,
164*fa3440faSWen Gong 	       hdr_80211_pattern + new->pkt_offset,
165*fa3440faSWen Gong 	       new->pattern_len);
166*fa3440faSWen Gong 	memcpy((u8 *)new->mask,
167*fa3440faSWen Gong 	       hdr_80211_bit_mask + new->pkt_offset,
168*fa3440faSWen Gong 	       new->pattern_len);
169*fa3440faSWen Gong 
170*fa3440faSWen Gong 	if (total_len > ETH_HLEN) {
171*fa3440faSWen Gong 		/* Copy frame body */
172*fa3440faSWen Gong 		memcpy((u8 *)new->pattern + new->pattern_len,
173*fa3440faSWen Gong 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
174*fa3440faSWen Gong 		       total_len - ETH_HLEN);
175*fa3440faSWen Gong 		memcpy((u8 *)new->mask + new->pattern_len,
176*fa3440faSWen Gong 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
177*fa3440faSWen Gong 		       total_len - ETH_HLEN);
178*fa3440faSWen Gong 
179*fa3440faSWen Gong 		new->pattern_len += total_len - ETH_HLEN;
180*fa3440faSWen Gong 	}
181*fa3440faSWen Gong }
182*fa3440faSWen 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] = {};
223*fa3440faSWen Gong 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
224*fa3440faSWen Gong 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
225*fa3440faSWen Gong 		struct cfg80211_pkt_pattern new_pattern = {};
226*fa3440faSWen Gong 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
22725c86619SJanusz Dziedzic 		int j;
22825c86619SJanusz Dziedzic 
229*fa3440faSWen Gong 		new_pattern.pattern = ath_pattern;
230*fa3440faSWen 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;
237*fa3440faSWen Gong 		old_pattern.mask = bitmask;
238*fa3440faSWen Gong 		new_pattern = old_pattern;
239*fa3440faSWen Gong 
240*fa3440faSWen Gong 		if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
241*fa3440faSWen Gong 			if (patterns[i].pkt_offset < ETH_HLEN)
242*fa3440faSWen Gong 				ath10k_wow_convert_8023_to_80211(&new_pattern,
243*fa3440faSWen Gong 								 &old_pattern);
244*fa3440faSWen Gong 			else
245*fa3440faSWen Gong 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
246*fa3440faSWen Gong 		}
247*fa3440faSWen Gong 
248*fa3440faSWen Gong 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
249*fa3440faSWen Gong 			return -EINVAL;
25025c86619SJanusz Dziedzic 
25125c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
25225c86619SJanusz Dziedzic 						 pattern_id,
253*fa3440faSWen Gong 						 new_pattern.pattern,
254*fa3440faSWen Gong 						 new_pattern.mask,
255*fa3440faSWen Gong 						 new_pattern.pattern_len,
256*fa3440faSWen 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 
3775fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_enable(ar);
3785fd3ac3cSJanusz Dziedzic 	if (ret) {
3795fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to start wow: %d\n", ret);
3805fd3ac3cSJanusz Dziedzic 		goto cleanup;
3815fd3ac3cSJanusz Dziedzic 	}
3825fd3ac3cSJanusz Dziedzic 
3835fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_suspend(ar);
3845fd3ac3cSJanusz Dziedzic 	if (ret) {
3855fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
3865fd3ac3cSJanusz Dziedzic 		goto wakeup;
3875fd3ac3cSJanusz Dziedzic 	}
3885fd3ac3cSJanusz Dziedzic 
3895fd3ac3cSJanusz Dziedzic 	goto exit;
3905fd3ac3cSJanusz Dziedzic 
3915fd3ac3cSJanusz Dziedzic wakeup:
3925fd3ac3cSJanusz Dziedzic 	ath10k_wow_wakeup(ar);
3935fd3ac3cSJanusz Dziedzic 
3945fd3ac3cSJanusz Dziedzic cleanup:
3955fd3ac3cSJanusz Dziedzic 	ath10k_wow_cleanup(ar);
3965fd3ac3cSJanusz Dziedzic 
3975fd3ac3cSJanusz Dziedzic exit:
3985fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
3995fd3ac3cSJanusz Dziedzic 	return ret ? 1 : 0;
4005fd3ac3cSJanusz Dziedzic }
4015fd3ac3cSJanusz Dziedzic 
402393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
403393b706cSRyan Hsu {
404393b706cSRyan Hsu 	struct ath10k *ar = hw->priv;
405393b706cSRyan Hsu 
406393b706cSRyan Hsu 	mutex_lock(&ar->conf_mutex);
407393b706cSRyan Hsu 	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
408393b706cSRyan Hsu 		     ar->running_fw->fw_file.fw_features)) {
409393b706cSRyan Hsu 		device_set_wakeup_enable(ar->dev, enabled);
410393b706cSRyan Hsu 	}
411393b706cSRyan Hsu 	mutex_unlock(&ar->conf_mutex);
412393b706cSRyan Hsu }
413393b706cSRyan Hsu 
4145fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw)
4155fd3ac3cSJanusz Dziedzic {
4165fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
4175fd3ac3cSJanusz Dziedzic 	int ret;
4185fd3ac3cSJanusz Dziedzic 
4195fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
4205fd3ac3cSJanusz Dziedzic 
4215fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
422c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
4235fd3ac3cSJanusz Dziedzic 		ret = 1;
4245fd3ac3cSJanusz Dziedzic 		goto exit;
4255fd3ac3cSJanusz Dziedzic 	}
4265fd3ac3cSJanusz Dziedzic 
4275fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_resume(ar);
4285fd3ac3cSJanusz Dziedzic 	if (ret) {
4295fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
4305fd3ac3cSJanusz Dziedzic 		goto exit;
4315fd3ac3cSJanusz Dziedzic 	}
4325fd3ac3cSJanusz Dziedzic 
4335fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_wakeup(ar);
4345fd3ac3cSJanusz Dziedzic 	if (ret)
4355fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
4365fd3ac3cSJanusz Dziedzic 
4375fd3ac3cSJanusz Dziedzic exit:
4386f7429c2SMichal Kazior 	if (ret) {
4396f7429c2SMichal Kazior 		switch (ar->state) {
4406f7429c2SMichal Kazior 		case ATH10K_STATE_ON:
4416f7429c2SMichal Kazior 			ar->state = ATH10K_STATE_RESTARTING;
4426f7429c2SMichal Kazior 			ret = 1;
4436f7429c2SMichal Kazior 			break;
4446f7429c2SMichal Kazior 		case ATH10K_STATE_OFF:
4456f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTING:
4466f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTED:
4476f7429c2SMichal Kazior 		case ATH10K_STATE_UTF:
4486f7429c2SMichal Kazior 		case ATH10K_STATE_WEDGED:
4496f7429c2SMichal Kazior 			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
4506f7429c2SMichal Kazior 				    ar->state);
4516f7429c2SMichal Kazior 			ret = -EIO;
4526f7429c2SMichal Kazior 			break;
4536f7429c2SMichal Kazior 		}
4546f7429c2SMichal Kazior 	}
4556f7429c2SMichal Kazior 
4565fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
4576f7429c2SMichal Kazior 	return ret;
4585fd3ac3cSJanusz Dziedzic }
4595fd3ac3cSJanusz Dziedzic 
4605fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar)
4615fd3ac3cSJanusz Dziedzic {
462c4cdf753SKalle Valo 	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
463c4cdf753SKalle Valo 		      ar->running_fw->fw_file.fw_features))
4645fd3ac3cSJanusz Dziedzic 		return 0;
4655fd3ac3cSJanusz Dziedzic 
4665fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
4675fd3ac3cSJanusz Dziedzic 		return -EINVAL;
4685fd3ac3cSJanusz Dziedzic 
46925c86619SJanusz Dziedzic 	ar->wow.wowlan_support = ath10k_wowlan_support;
470*fa3440faSWen Gong 
471*fa3440faSWen Gong 	if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
472*fa3440faSWen Gong 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
473*fa3440faSWen Gong 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
474*fa3440faSWen Gong 	}
475*fa3440faSWen Gong 
47625c86619SJanusz Dziedzic 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
47725c86619SJanusz Dziedzic 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
4785fd3ac3cSJanusz Dziedzic 
479393b706cSRyan Hsu 	device_set_wakeup_capable(ar->dev, true);
480393b706cSRyan Hsu 
4815fd3ac3cSJanusz Dziedzic 	return 0;
4825fd3ac3cSJanusz Dziedzic }
483