xref: /wlan-driver/qcacld-3.0/core/hdd/src/wlan_hdd_wowl.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name  * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name  * above copyright notice and this permission notice appear in all
8*5113495bSYour Name  * copies.
9*5113495bSYour Name  *
10*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name  * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name  */
19*5113495bSYour Name 
20*5113495bSYour Name /**
21*5113495bSYour Name  * DOC: wlan_hdd_wowl.c
22*5113495bSYour Name  *
23*5113495bSYour Name  * wake up on WLAN API file
24*5113495bSYour Name  */
25*5113495bSYour Name 
26*5113495bSYour Name /* Include Files */
27*5113495bSYour Name 
28*5113495bSYour Name #include "qdf_str.h"
29*5113495bSYour Name #include <wlan_hdd_includes.h>
30*5113495bSYour Name #include <wlan_hdd_wowl.h>
31*5113495bSYour Name #include <wlan_pmo_wow_public_struct.h>
32*5113495bSYour Name #include "wlan_hdd_object_manager.h"
33*5113495bSYour Name 
34*5113495bSYour Name /* Preprocessor Definitions and Constants */
35*5113495bSYour Name #define WOWL_INTER_PTRN_TOKENIZER   ';'
36*5113495bSYour Name #define WOWL_INTRA_PTRN_TOKENIZER   ':'
37*5113495bSYour Name 
38*5113495bSYour Name /* Type Declarations */
39*5113495bSYour Name 
40*5113495bSYour Name static char *g_hdd_wowl_ptrns[WOWL_MAX_PTRNS_ALLOWED];
41*5113495bSYour Name static bool g_hdd_wowl_ptrns_debugfs[WOWL_MAX_PTRNS_ALLOWED] = { 0 };
42*5113495bSYour Name 
43*5113495bSYour Name static uint8_t g_hdd_wowl_ptrns_count;
44*5113495bSYour Name 
find_ptrn_len(const char * ptrn)45*5113495bSYour Name static inline int find_ptrn_len(const char *ptrn)
46*5113495bSYour Name {
47*5113495bSYour Name 	int len = 0;
48*5113495bSYour Name 
49*5113495bSYour Name 	while (*ptrn != '\0' && *ptrn != WOWL_INTER_PTRN_TOKENIZER) {
50*5113495bSYour Name 		len++;
51*5113495bSYour Name 		ptrn++;
52*5113495bSYour Name 	}
53*5113495bSYour Name 	return len;
54*5113495bSYour Name }
55*5113495bSYour Name 
56*5113495bSYour Name /**
57*5113495bSYour Name  * dump_hdd_wowl_ptrn() - log wow patterns
58*5113495bSYour Name  * @ptrn: pointer to wow pattern
59*5113495bSYour Name  *
60*5113495bSYour Name  * Return: none
61*5113495bSYour Name  */
dump_hdd_wowl_ptrn(struct pmo_wow_add_pattern * ptrn)62*5113495bSYour Name static void dump_hdd_wowl_ptrn(struct pmo_wow_add_pattern *ptrn)
63*5113495bSYour Name {
64*5113495bSYour Name 	hdd_debug("Dumping WOW pattern");
65*5113495bSYour Name 	hdd_nofl_debug("Pattern Id = 0x%x", ptrn->pattern_id);
66*5113495bSYour Name 	hdd_nofl_debug("Pattern Byte Offset = 0x%x", ptrn->pattern_byte_offset);
67*5113495bSYour Name 	hdd_nofl_debug("Pattern_size = 0x%x", ptrn->pattern_size);
68*5113495bSYour Name 	hdd_nofl_debug("Pattern_mask_size = 0x%x", ptrn->pattern_mask_size);
69*5113495bSYour Name 	hdd_nofl_debug("Pattern: ");
70*5113495bSYour Name 	qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
71*5113495bSYour Name 			   ptrn->pattern, ptrn->pattern_size);
72*5113495bSYour Name 	hdd_nofl_debug("pattern_mask: ");
73*5113495bSYour Name 	qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
74*5113495bSYour Name 			   ptrn->pattern_mask, ptrn->pattern_mask_size);
75*5113495bSYour Name }
76*5113495bSYour Name 
77*5113495bSYour Name static QDF_STATUS
hdd_get_num_wow_filters(struct hdd_context * hdd_ctx,uint8_t * num_filters)78*5113495bSYour Name hdd_get_num_wow_filters(struct hdd_context *hdd_ctx, uint8_t *num_filters)
79*5113495bSYour Name {
80*5113495bSYour Name 	QDF_STATUS status;
81*5113495bSYour Name 	struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc;
82*5113495bSYour Name 
83*5113495bSYour Name 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_HDD_ID_OBJ_MGR);
84*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status))
85*5113495bSYour Name 		return status;
86*5113495bSYour Name 
87*5113495bSYour Name 	if (cds_get_conparam() == QDF_GLOBAL_FTM_MODE)
88*5113495bSYour Name 		*num_filters =  0;
89*5113495bSYour Name 	else
90*5113495bSYour Name 		*num_filters = ucfg_pmo_get_num_wow_filters(hdd_ctx->psoc);
91*5113495bSYour Name 
92*5113495bSYour Name 	wlan_objmgr_psoc_release_ref(psoc, WLAN_HDD_ID_OBJ_MGR);
93*5113495bSYour Name 
94*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
95*5113495bSYour Name }
96*5113495bSYour Name 
97*5113495bSYour Name /**
98*5113495bSYour Name  * hdd_add_wowl_ptrn() - Function which will add the WoWL pattern to be
99*5113495bSYour Name  *			 used when PBM filtering is enabled
100*5113495bSYour Name  * @adapter: pointer to the adapter
101*5113495bSYour Name  * @ptrn: pointer to the pattern string to be added
102*5113495bSYour Name  *
103*5113495bSYour Name  * Return: false if any errors encountered, true otherwise
104*5113495bSYour Name  */
hdd_add_wowl_ptrn(struct hdd_adapter * adapter,const char * ptrn)105*5113495bSYour Name bool hdd_add_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn)
106*5113495bSYour Name {
107*5113495bSYour Name 	struct pmo_wow_add_pattern wow_pattern;
108*5113495bSYour Name 	int i, empty_slot, len, offset;
109*5113495bSYour Name 	QDF_STATUS status;
110*5113495bSYour Name 	const char *temp;
111*5113495bSYour Name 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
112*5113495bSYour Name 	uint8_t num_filters;
113*5113495bSYour Name 	bool invalid_ptrn = false;
114*5113495bSYour Name 	struct wlan_objmgr_vdev *vdev;
115*5113495bSYour Name 
116*5113495bSYour Name 	status = hdd_get_num_wow_filters(hdd_ctx, &num_filters);
117*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status))
118*5113495bSYour Name 		return false;
119*5113495bSYour Name 
120*5113495bSYour Name 	/* There has to have at least 1 byte for each field (pattern
121*5113495bSYour Name 	 * size, mask size, pattern, mask) e.g. PP:QQ:RR:SS ==> 11
122*5113495bSYour Name 	 * chars
123*5113495bSYour Name 	 */
124*5113495bSYour Name 	len = find_ptrn_len(ptrn);
125*5113495bSYour Name 	while (len >= 11) {
126*5113495bSYour Name 		empty_slot = -1;
127*5113495bSYour Name 
128*5113495bSYour Name 		/* check if pattern is already configured */
129*5113495bSYour Name 		for (i = num_filters - 1; i >= 0; i--) {
130*5113495bSYour Name 			if (!g_hdd_wowl_ptrns[i]) {
131*5113495bSYour Name 				empty_slot = i;
132*5113495bSYour Name 				continue;
133*5113495bSYour Name 			}
134*5113495bSYour Name 
135*5113495bSYour Name 			if (strlen(g_hdd_wowl_ptrns[i]) == len) {
136*5113495bSYour Name 				if (!memcmp(ptrn, g_hdd_wowl_ptrns[i], len)) {
137*5113495bSYour Name 					hdd_err("WoWL pattern '%s' already configured",
138*5113495bSYour Name 						g_hdd_wowl_ptrns[i]);
139*5113495bSYour Name 					ptrn += len;
140*5113495bSYour Name 					goto next_ptrn;
141*5113495bSYour Name 				}
142*5113495bSYour Name 			}
143*5113495bSYour Name 		}
144*5113495bSYour Name 
145*5113495bSYour Name 		/* Maximum number of patterns have been configured already */
146*5113495bSYour Name 		if (empty_slot == -1) {
147*5113495bSYour Name 			hdd_err("Max WoW patterns (%u) reached", num_filters);
148*5113495bSYour Name 			return false;
149*5113495bSYour Name 		}
150*5113495bSYour Name 
151*5113495bSYour Name 		/* Validate the pattern */
152*5113495bSYour Name 		if (ptrn[2] != WOWL_INTRA_PTRN_TOKENIZER ||
153*5113495bSYour Name 		    ptrn[5] != WOWL_INTRA_PTRN_TOKENIZER) {
154*5113495bSYour Name 			hdd_err("Malformed pattern string. Skip!");
155*5113495bSYour Name 			invalid_ptrn = true;
156*5113495bSYour Name 			ptrn += len;
157*5113495bSYour Name 			goto next_ptrn;
158*5113495bSYour Name 		}
159*5113495bSYour Name 
160*5113495bSYour Name 		/* Extract the pattern size */
161*5113495bSYour Name 		wow_pattern.pattern_size =
162*5113495bSYour Name 			(hex_to_bin(ptrn[0]) * 0x10) + hex_to_bin(ptrn[1]);
163*5113495bSYour Name 
164*5113495bSYour Name 		/* Extract the pattern mask size */
165*5113495bSYour Name 		wow_pattern.pattern_mask_size =
166*5113495bSYour Name 			(hex_to_bin(ptrn[3]) * 0x10) + hex_to_bin(ptrn[4]);
167*5113495bSYour Name 
168*5113495bSYour Name 		if (wow_pattern.pattern_size > PMO_WOWL_BCAST_PATTERN_MAX_SIZE
169*5113495bSYour Name 		    || wow_pattern.pattern_mask_size >
170*5113495bSYour Name 		    WOWL_PTRN_MASK_MAX_SIZE) {
171*5113495bSYour Name 			hdd_err("Invalid length specified. Skip!");
172*5113495bSYour Name 			invalid_ptrn = true;
173*5113495bSYour Name 			ptrn += len;
174*5113495bSYour Name 			goto next_ptrn;
175*5113495bSYour Name 		}
176*5113495bSYour Name 
177*5113495bSYour Name 		/* compute the offset of tokenizer after the pattern */
178*5113495bSYour Name 		offset = 5 + 2 * wow_pattern.pattern_size + 1;
179*5113495bSYour Name 		if ((offset >= len) ||
180*5113495bSYour Name 		    (ptrn[offset] != WOWL_INTRA_PTRN_TOKENIZER)) {
181*5113495bSYour Name 			hdd_err("Malformed pattern string..skip!");
182*5113495bSYour Name 			invalid_ptrn = true;
183*5113495bSYour Name 			ptrn += len;
184*5113495bSYour Name 			goto next_ptrn;
185*5113495bSYour Name 		}
186*5113495bSYour Name 
187*5113495bSYour Name 		/* compute the end of pattern string */
188*5113495bSYour Name 		offset = offset + 2 * wow_pattern.pattern_mask_size;
189*5113495bSYour Name 		if (offset + 1 != len) {
190*5113495bSYour Name 			/* offset begins with 0 */
191*5113495bSYour Name 			hdd_err("Malformed pattern string...skip!");
192*5113495bSYour Name 			invalid_ptrn = true;
193*5113495bSYour Name 			ptrn += len;
194*5113495bSYour Name 			goto next_ptrn;
195*5113495bSYour Name 		}
196*5113495bSYour Name 
197*5113495bSYour Name 		temp = ptrn;
198*5113495bSYour Name 
199*5113495bSYour Name 		/* Now advance to where pattern begins */
200*5113495bSYour Name 		ptrn += 6;
201*5113495bSYour Name 
202*5113495bSYour Name 		/* Extract the pattern */
203*5113495bSYour Name 		for (i = 0; i < wow_pattern.pattern_size; i++) {
204*5113495bSYour Name 			wow_pattern.pattern[i] =
205*5113495bSYour Name 				(hex_to_bin(ptrn[0]) * 0x10) +
206*5113495bSYour Name 				hex_to_bin(ptrn[1]);
207*5113495bSYour Name 			ptrn += 2;      /* skip to next byte */
208*5113495bSYour Name 		}
209*5113495bSYour Name 
210*5113495bSYour Name 		/* Skip over the ':' separator after the pattern */
211*5113495bSYour Name 		ptrn++;
212*5113495bSYour Name 
213*5113495bSYour Name 		/* Extract the pattern Mask */
214*5113495bSYour Name 		for (i = 0; i < wow_pattern.pattern_mask_size; i++) {
215*5113495bSYour Name 			wow_pattern.pattern_mask[i] =
216*5113495bSYour Name 				(hex_to_bin(ptrn[0]) * 0x10) +
217*5113495bSYour Name 				hex_to_bin(ptrn[1]);
218*5113495bSYour Name 			ptrn += 2;      /* skip to next byte */
219*5113495bSYour Name 		}
220*5113495bSYour Name 
221*5113495bSYour Name 		/* All is good. Store the pattern locally */
222*5113495bSYour Name 		g_hdd_wowl_ptrns[empty_slot] = qdf_mem_malloc(len + 1);
223*5113495bSYour Name 		if (!g_hdd_wowl_ptrns[empty_slot])
224*5113495bSYour Name 			return false;
225*5113495bSYour Name 
226*5113495bSYour Name 		memcpy(g_hdd_wowl_ptrns[empty_slot], temp, len);
227*5113495bSYour Name 		g_hdd_wowl_ptrns[empty_slot][len] = '\0';
228*5113495bSYour Name 		wow_pattern.pattern_id = empty_slot;
229*5113495bSYour Name 		wow_pattern.pattern_byte_offset = 0;
230*5113495bSYour Name 
231*5113495bSYour Name 		vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
232*5113495bSYour Name 						   WLAN_OSIF_POWER_ID);
233*5113495bSYour Name 		if (!vdev) {
234*5113495bSYour Name 			hdd_err("vdev is null");
235*5113495bSYour Name 			qdf_mem_free(g_hdd_wowl_ptrns[empty_slot]);
236*5113495bSYour Name 			g_hdd_wowl_ptrns[empty_slot] = NULL;
237*5113495bSYour Name 			return false;
238*5113495bSYour Name 		}
239*5113495bSYour Name 		/* Register the pattern downstream */
240*5113495bSYour Name 		status = ucfg_pmo_add_wow_user_pattern(vdev, &wow_pattern);
241*5113495bSYour Name 		if (QDF_IS_STATUS_ERROR(status)) {
242*5113495bSYour Name 			/* Add failed, so invalidate the local storage */
243*5113495bSYour Name 			hdd_err("sme_wowl_add_bcast_pattern failed with error code (%d)",
244*5113495bSYour Name 				status);
245*5113495bSYour Name 			qdf_mem_free(g_hdd_wowl_ptrns[empty_slot]);
246*5113495bSYour Name 			g_hdd_wowl_ptrns[empty_slot] = NULL;
247*5113495bSYour Name 		}
248*5113495bSYour Name 		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
249*5113495bSYour Name 		dump_hdd_wowl_ptrn(&wow_pattern);
250*5113495bSYour Name 
251*5113495bSYour Name next_ptrn:
252*5113495bSYour Name 		if (*ptrn == WOWL_INTER_PTRN_TOKENIZER) {
253*5113495bSYour Name 			/* move past the tokenizer */
254*5113495bSYour Name 			ptrn += 1;
255*5113495bSYour Name 			len = find_ptrn_len(ptrn);
256*5113495bSYour Name 			continue;
257*5113495bSYour Name 		} else {
258*5113495bSYour Name 			break;
259*5113495bSYour Name 		}
260*5113495bSYour Name 	}
261*5113495bSYour Name 
262*5113495bSYour Name 	if (invalid_ptrn)
263*5113495bSYour Name 		return false;
264*5113495bSYour Name 
265*5113495bSYour Name 	return true;
266*5113495bSYour Name }
267*5113495bSYour Name 
268*5113495bSYour Name /**
269*5113495bSYour Name  * hdd_del_wowl_ptrn() - Function which will remove a WoWL pattern
270*5113495bSYour Name  * @adapter: pointer to the adapter
271*5113495bSYour Name  * @ptrn: pointer to the pattern string to be removed
272*5113495bSYour Name  *
273*5113495bSYour Name  * Return: false if any errors encountered, true otherwise
274*5113495bSYour Name  */
hdd_del_wowl_ptrn(struct hdd_adapter * adapter,const char * ptrn)275*5113495bSYour Name bool hdd_del_wowl_ptrn(struct hdd_adapter *adapter, const char *ptrn)
276*5113495bSYour Name {
277*5113495bSYour Name 	uint8_t id;
278*5113495bSYour Name 	bool patternFound = false;
279*5113495bSYour Name 	QDF_STATUS status;
280*5113495bSYour Name 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
281*5113495bSYour Name 	uint8_t num_filters;
282*5113495bSYour Name 	struct wlan_objmgr_vdev *vdev;
283*5113495bSYour Name 
284*5113495bSYour Name 	status = hdd_get_num_wow_filters(hdd_ctx, &num_filters);
285*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status))
286*5113495bSYour Name 		return false;
287*5113495bSYour Name 
288*5113495bSYour Name 	/* lookup pattern */
289*5113495bSYour Name 	for (id = 0; id < num_filters; id++) {
290*5113495bSYour Name 		if (!g_hdd_wowl_ptrns[id])
291*5113495bSYour Name 			continue;
292*5113495bSYour Name 
293*5113495bSYour Name 		if (qdf_str_eq(ptrn, g_hdd_wowl_ptrns[id])) {
294*5113495bSYour Name 			patternFound = true;
295*5113495bSYour Name 			break;
296*5113495bSYour Name 		}
297*5113495bSYour Name 	}
298*5113495bSYour Name 
299*5113495bSYour Name 	/* If pattern present, remove it from downstream */
300*5113495bSYour Name 	if (!patternFound)
301*5113495bSYour Name 		return false;
302*5113495bSYour Name 
303*5113495bSYour Name 	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
304*5113495bSYour Name 					   WLAN_OSIF_POWER_ID);
305*5113495bSYour Name 	if (!vdev)
306*5113495bSYour Name 		return false;
307*5113495bSYour Name 
308*5113495bSYour Name 	status = ucfg_pmo_del_wow_user_pattern(vdev, id);
309*5113495bSYour Name 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
310*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status))
311*5113495bSYour Name 		return false;
312*5113495bSYour Name 
313*5113495bSYour Name 	/* Remove from local storage as well */
314*5113495bSYour Name 	hdd_err("Deleted pattern with id %d [%s]", id, g_hdd_wowl_ptrns[id]);
315*5113495bSYour Name 
316*5113495bSYour Name 	qdf_mem_free(g_hdd_wowl_ptrns[id]);
317*5113495bSYour Name 	g_hdd_wowl_ptrns[id] = NULL;
318*5113495bSYour Name 
319*5113495bSYour Name 	return true;
320*5113495bSYour Name }
321*5113495bSYour Name 
322*5113495bSYour Name /**
323*5113495bSYour Name  * hdd_add_wowl_ptrn_debugfs() - Function which will add a WoW pattern
324*5113495bSYour Name  *				 sent from debugfs interface
325*5113495bSYour Name  * @adapter: pointer to the adapter
326*5113495bSYour Name  * @pattern_idx: index of the pattern to be added
327*5113495bSYour Name  * @pattern_offset: offset of the pattern in the frame payload
328*5113495bSYour Name  * @pattern_buf: pointer to the pattern hex string to be added
329*5113495bSYour Name  * @pattern_mask: pointer to the pattern mask hex string
330*5113495bSYour Name  *
331*5113495bSYour Name  * Return: false if any errors encountered, true otherwise
332*5113495bSYour Name  */
hdd_add_wowl_ptrn_debugfs(struct hdd_adapter * adapter,uint8_t pattern_idx,uint8_t pattern_offset,char * pattern_buf,char * pattern_mask)333*5113495bSYour Name bool hdd_add_wowl_ptrn_debugfs(struct hdd_adapter *adapter, uint8_t pattern_idx,
334*5113495bSYour Name 			       uint8_t pattern_offset, char *pattern_buf,
335*5113495bSYour Name 			       char *pattern_mask)
336*5113495bSYour Name {
337*5113495bSYour Name 	struct pmo_wow_add_pattern wow_pattern;
338*5113495bSYour Name 	QDF_STATUS qdf_ret_status;
339*5113495bSYour Name 	uint16_t pattern_len, mask_len, i;
340*5113495bSYour Name 	struct wlan_objmgr_vdev *vdev;
341*5113495bSYour Name 
342*5113495bSYour Name 	if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1)) {
343*5113495bSYour Name 		hdd_err("WoW pattern index %d is out of range (0 ~ %d)",
344*5113495bSYour Name 			pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1);
345*5113495bSYour Name 
346*5113495bSYour Name 		return false;
347*5113495bSYour Name 	}
348*5113495bSYour Name 
349*5113495bSYour Name 	pattern_len = strlen(pattern_buf);
350*5113495bSYour Name 
351*5113495bSYour Name 	/* Since the pattern is a hex string, 2 characters represent 1 byte. */
352*5113495bSYour Name 	if (pattern_len % 2) {
353*5113495bSYour Name 		hdd_err("Malformed WoW pattern!");
354*5113495bSYour Name 
355*5113495bSYour Name 		return false;
356*5113495bSYour Name 	}
357*5113495bSYour Name 
358*5113495bSYour Name 	pattern_len >>= 1;
359*5113495bSYour Name 	if (!pattern_len || pattern_len > WOWL_PTRN_MAX_SIZE) {
360*5113495bSYour Name 		hdd_err("WoW pattern length %d is out of range (1 ~ %d).",
361*5113495bSYour Name 			pattern_len, WOWL_PTRN_MAX_SIZE);
362*5113495bSYour Name 
363*5113495bSYour Name 		return false;
364*5113495bSYour Name 	}
365*5113495bSYour Name 
366*5113495bSYour Name 	wow_pattern.pattern_id = pattern_idx;
367*5113495bSYour Name 	wow_pattern.pattern_byte_offset = pattern_offset;
368*5113495bSYour Name 	wow_pattern.pattern_size = pattern_len;
369*5113495bSYour Name 
370*5113495bSYour Name 	if (wow_pattern.pattern_size > PMO_WOWL_BCAST_PATTERN_MAX_SIZE) {
371*5113495bSYour Name 		hdd_err("WoW pattern size (%d) greater than max (%d)",
372*5113495bSYour Name 			wow_pattern.pattern_size,
373*5113495bSYour Name 			PMO_WOWL_BCAST_PATTERN_MAX_SIZE);
374*5113495bSYour Name 		return false;
375*5113495bSYour Name 	}
376*5113495bSYour Name 	/* Extract the pattern */
377*5113495bSYour Name 	for (i = 0; i < wow_pattern.pattern_size; i++) {
378*5113495bSYour Name 		wow_pattern.pattern[i] =
379*5113495bSYour Name 			(hex_to_bin(pattern_buf[0]) << 4) +
380*5113495bSYour Name 			hex_to_bin(pattern_buf[1]);
381*5113495bSYour Name 
382*5113495bSYour Name 		/* Skip to next byte */
383*5113495bSYour Name 		pattern_buf += 2;
384*5113495bSYour Name 	}
385*5113495bSYour Name 
386*5113495bSYour Name 	/* Get pattern mask size by pattern length */
387*5113495bSYour Name 	wow_pattern.pattern_mask_size = pattern_len >> 3;
388*5113495bSYour Name 	if (pattern_len % 8)
389*5113495bSYour Name 		wow_pattern.pattern_mask_size += 1;
390*5113495bSYour Name 
391*5113495bSYour Name 	mask_len = strlen(pattern_mask);
392*5113495bSYour Name 	if ((mask_len % 2)
393*5113495bSYour Name 	    || (wow_pattern.pattern_mask_size != (mask_len >> 1))) {
394*5113495bSYour Name 		hdd_err("Malformed WoW pattern mask!");
395*5113495bSYour Name 
396*5113495bSYour Name 		return false;
397*5113495bSYour Name 	}
398*5113495bSYour Name 	if (wow_pattern.pattern_mask_size > WOWL_PTRN_MASK_MAX_SIZE) {
399*5113495bSYour Name 		hdd_err("WoW pattern mask size (%d) greater than max (%d)",
400*5113495bSYour Name 			wow_pattern.pattern_mask_size,
401*5113495bSYour Name 			WOWL_PTRN_MASK_MAX_SIZE);
402*5113495bSYour Name 		return false;
403*5113495bSYour Name 	}
404*5113495bSYour Name 	/* Extract the pattern mask */
405*5113495bSYour Name 	for (i = 0; i < wow_pattern.pattern_mask_size; i++) {
406*5113495bSYour Name 		wow_pattern.pattern_mask[i] =
407*5113495bSYour Name 			(hex_to_bin(pattern_mask[0]) << 4) +
408*5113495bSYour Name 			hex_to_bin(pattern_mask[1]);
409*5113495bSYour Name 
410*5113495bSYour Name 		/* Skip to next byte */
411*5113495bSYour Name 		pattern_mask += 2;
412*5113495bSYour Name 	}
413*5113495bSYour Name 
414*5113495bSYour Name 	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
415*5113495bSYour Name 					   WLAN_OSIF_POWER_ID);
416*5113495bSYour Name 	if (!vdev)
417*5113495bSYour Name 		return false;
418*5113495bSYour Name 
419*5113495bSYour Name 	/* Register the pattern downstream */
420*5113495bSYour Name 	qdf_ret_status = ucfg_pmo_add_wow_user_pattern(vdev, &wow_pattern);
421*5113495bSYour Name 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
422*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) {
423*5113495bSYour Name 		hdd_err("pmo_wow_user_pattern failed with error code (%d).",
424*5113495bSYour Name 			  qdf_ret_status);
425*5113495bSYour Name 
426*5113495bSYour Name 		return false;
427*5113495bSYour Name 	}
428*5113495bSYour Name 
429*5113495bSYour Name 	/* All is good. */
430*5113495bSYour Name 	if (!g_hdd_wowl_ptrns_debugfs[pattern_idx]) {
431*5113495bSYour Name 		g_hdd_wowl_ptrns_debugfs[pattern_idx] = 1;
432*5113495bSYour Name 		g_hdd_wowl_ptrns_count++;
433*5113495bSYour Name 	}
434*5113495bSYour Name 
435*5113495bSYour Name 	dump_hdd_wowl_ptrn(&wow_pattern);
436*5113495bSYour Name 
437*5113495bSYour Name 	return true;
438*5113495bSYour Name }
439*5113495bSYour Name 
440*5113495bSYour Name /**
441*5113495bSYour Name  * hdd_del_wowl_ptrn_debugfs() - Function which will remove a WoW pattern
442*5113495bSYour Name  *				 sent from debugfs interface
443*5113495bSYour Name  * @adapter: pointer to the adapter
444*5113495bSYour Name  * @pattern_idx: index of the pattern to be removed
445*5113495bSYour Name  *
446*5113495bSYour Name  * Return: false if any errors encountered, true otherwise
447*5113495bSYour Name  */
hdd_del_wowl_ptrn_debugfs(struct hdd_adapter * adapter,uint8_t pattern_idx)448*5113495bSYour Name bool hdd_del_wowl_ptrn_debugfs(struct hdd_adapter *adapter,
449*5113495bSYour Name 			       uint8_t pattern_idx)
450*5113495bSYour Name {
451*5113495bSYour Name 	struct wlan_objmgr_vdev *vdev;
452*5113495bSYour Name 	QDF_STATUS qdf_ret_status;
453*5113495bSYour Name 
454*5113495bSYour Name 	if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1)) {
455*5113495bSYour Name 		hdd_err("WoW pattern index %d is not in the range (0 ~ %d).",
456*5113495bSYour Name 			pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1);
457*5113495bSYour Name 
458*5113495bSYour Name 		return false;
459*5113495bSYour Name 	}
460*5113495bSYour Name 
461*5113495bSYour Name 	if (!g_hdd_wowl_ptrns_debugfs[pattern_idx]) {
462*5113495bSYour Name 		hdd_err("WoW pattern %d is not in the table.",
463*5113495bSYour Name 			pattern_idx);
464*5113495bSYour Name 
465*5113495bSYour Name 		return false;
466*5113495bSYour Name 	}
467*5113495bSYour Name 
468*5113495bSYour Name 	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
469*5113495bSYour Name 					   WLAN_OSIF_POWER_ID);
470*5113495bSYour Name 	if (!vdev)
471*5113495bSYour Name 		return false;
472*5113495bSYour Name 
473*5113495bSYour Name 	qdf_ret_status = ucfg_pmo_del_wow_user_pattern(vdev, pattern_idx);
474*5113495bSYour Name 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
475*5113495bSYour Name 	if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) {
476*5113495bSYour Name 		hdd_err("sme_wowl_del_bcast_pattern failed with error code (%d).",
477*5113495bSYour Name 			 qdf_ret_status);
478*5113495bSYour Name 
479*5113495bSYour Name 		return false;
480*5113495bSYour Name 	}
481*5113495bSYour Name 
482*5113495bSYour Name 	g_hdd_wowl_ptrns_debugfs[pattern_idx] = 0;
483*5113495bSYour Name 	g_hdd_wowl_ptrns_count--;
484*5113495bSYour Name 
485*5113495bSYour Name 	return true;
486*5113495bSYour Name }
487*5113495bSYour Name 
hdd_free_user_wowl_ptrns(void)488*5113495bSYour Name void hdd_free_user_wowl_ptrns(void)
489*5113495bSYour Name {
490*5113495bSYour Name 	int i;
491*5113495bSYour Name 
492*5113495bSYour Name 	for (i = 0; i < WOWL_MAX_PTRNS_ALLOWED; ++i) {
493*5113495bSYour Name 		if (g_hdd_wowl_ptrns[i]) {
494*5113495bSYour Name 			qdf_mem_free(g_hdd_wowl_ptrns[i]);
495*5113495bSYour Name 			g_hdd_wowl_ptrns[i] = NULL;
496*5113495bSYour Name 		}
497*5113495bSYour Name 	}
498*5113495bSYour Name }
499