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