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