1 /*
2 * Copyright (c) 2013-2021 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_debugfs.c
22 *
23 * This driver currently supports the following debugfs files:
24 * wlan_wcnss/wow_enable to enable/disable WoWL.
25 * wlan_wcnss/wow_pattern to configure WoWL patterns.
26 * wlan_wcnss/pattern_gen to configure periodic TX patterns.
27 */
28
29 #include "osif_sync.h"
30 #include <wlan_hdd_includes.h>
31 #include <wlan_hdd_debugfs.h>
32 #include <wlan_osif_request_manager.h>
33 #include <wlan_hdd_wowl.h>
34 #include <cds_sched.h>
35 #include <wlan_hdd_debugfs_llstat.h>
36 #include <wlan_hdd_debugfs_mibstat.h>
37 #include "wlan_hdd_debugfs_unit_test.h"
38
39
40 #define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8
41 #define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
42 #define MAX_USER_COMMAND_SIZE_FRAME 4096
43
44 #define MAX_DEBUGFS_WAIT_ITERATIONS 20
45 #define DEBUGFS_WAIT_SLEEP_TIME 100
46
47 static bool hdd_periodic_pattern_map[MAXNUM_PERIODIC_TX_PTRNS];
48
49 static qdf_atomic_t debugfs_thread_count;
50
hdd_debugfs_thread_increment(void)51 void hdd_debugfs_thread_increment(void)
52 {
53 qdf_atomic_inc(&debugfs_thread_count);
54 }
55
hdd_debugfs_thread_decrement(void)56 void hdd_debugfs_thread_decrement(void)
57 {
58 qdf_atomic_dec(&debugfs_thread_count);
59 }
60
hdd_return_debugfs_threads_count(void)61 int hdd_return_debugfs_threads_count(void)
62 {
63 return qdf_atomic_read(&debugfs_thread_count);
64 }
65
hdd_wait_for_debugfs_threads_completion(void)66 bool hdd_wait_for_debugfs_threads_completion(void)
67 {
68 int count = MAX_DEBUGFS_WAIT_ITERATIONS;
69 int r;
70
71 while (count) {
72 r = hdd_return_debugfs_threads_count();
73 if (!r)
74 break;
75
76 if (--count) {
77 hdd_debug("Waiting for %d debugfs threads to exit", r);
78 qdf_sleep(DEBUGFS_WAIT_SLEEP_TIME);
79 }
80 }
81
82 /* at least one debugfs thread is executing */
83 if (!count) {
84 hdd_err("Timed-out waiting for debugfs threads");
85 return false;
86 }
87
88 return true;
89 }
90
91 /**
92 * __wcnss_wowpattern_write() - wow_pattern debugfs handler
93 * @net_dev: net_device context used to register the debugfs file
94 * @buf: text being written to the debugfs
95 * @count: size of @buf
96 * @ppos: (unused) offset into the virtual file system
97 *
98 * Return: number of bytes processed
99 */
__wcnss_wowpattern_write(struct net_device * net_dev,const char __user * buf,size_t count,loff_t * ppos)100 static ssize_t __wcnss_wowpattern_write(struct net_device *net_dev,
101 const char __user *buf, size_t count,
102 loff_t *ppos)
103 {
104 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
105 struct hdd_context *hdd_ctx;
106 char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN + 1];
107 char *sptr, *token;
108 uint8_t pattern_idx = 0;
109 uint8_t pattern_offset = 0;
110 char *pattern_buf;
111 char *pattern_mask;
112 int ret;
113
114 hdd_enter();
115
116 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
117 hdd_err("Invalid adapter or adapter has invalid magic");
118 return -EINVAL;
119 }
120
121 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
122 ret = wlan_hdd_validate_context(hdd_ctx);
123 if (0 != ret)
124 return ret;
125
126 if (!wlan_hdd_validate_modules_state(hdd_ctx))
127 return -EINVAL;
128
129 if (!sme_is_feature_supported_by_fw(WOW)) {
130 hdd_err("Wake-on-Wireless feature is not supported in firmware!");
131 return -EINVAL;
132 }
133
134 if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN) {
135 hdd_err("Command length is larger than %d bytes",
136 MAX_USER_COMMAND_SIZE_WOWL_PATTERN);
137 return -EINVAL;
138 }
139
140 /* Get command from user */
141 if (copy_from_user(cmd, buf, count))
142 return -EFAULT;
143 cmd[count] = '\0';
144 sptr = cmd;
145
146 /* Get pattern idx */
147 token = strsep(&sptr, " ");
148 if (!token)
149 return -EINVAL;
150
151 if (kstrtou8(token, 0, &pattern_idx))
152 return -EINVAL;
153
154 /* Get pattern offset */
155 token = strsep(&sptr, " ");
156
157 /* Delete pattern if no further argument */
158 if (!token) {
159 hdd_del_wowl_ptrn_debugfs(adapter, pattern_idx);
160
161 return count;
162 }
163
164 if (kstrtou8(token, 0, &pattern_offset))
165 return -EINVAL;
166
167 /* Get pattern */
168 token = strsep(&sptr, " ");
169 if (!token)
170 return -EINVAL;
171
172 pattern_buf = token;
173
174 /* Get pattern mask */
175 token = strsep(&sptr, " ");
176 if (!token)
177 return -EINVAL;
178
179 pattern_mask = token;
180 pattern_mask[strlen(pattern_mask) - 1] = '\0';
181
182 hdd_add_wowl_ptrn_debugfs(adapter, pattern_idx, pattern_offset,
183 pattern_buf, pattern_mask);
184 hdd_exit();
185 return count;
186 }
187
188 /**
189 * wcnss_wowpattern_write() - SSR wrapper for __wcnss_wowpattern_write
190 * @file: file pointer
191 * @buf: buffer
192 * @count: count
193 * @ppos: position pointer
194 *
195 * Return: 0 on success, error number otherwise
196 */
wcnss_wowpattern_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)197 static ssize_t wcnss_wowpattern_write(struct file *file,
198 const char __user *buf,
199 size_t count, loff_t *ppos)
200 {
201 struct net_device *net_dev = file_inode(file)->i_private;
202 struct osif_vdev_sync *vdev_sync;
203 ssize_t err_size;
204
205 err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync);
206 if (err_size)
207 return err_size;
208
209 err_size = __wcnss_wowpattern_write(net_dev, buf, count, ppos);
210
211 osif_vdev_sync_op_stop(vdev_sync);
212
213 return err_size;
214 }
215
216 /**
217 * __wcnss_patterngen_write() - pattern_gen debugfs handler
218 * @net_dev: net_device context used to register the debugfs file
219 * @buf: text being written to the debugfs
220 * @count: size of @buf
221 * @ppos: (unused) offset into the virtual file system
222 *
223 * Return: number of bytes processed
224 */
__wcnss_patterngen_write(struct net_device * net_dev,const char __user * buf,size_t count,loff_t * ppos)225 static ssize_t __wcnss_patterngen_write(struct net_device *net_dev,
226 const char __user *buf, size_t count,
227 loff_t *ppos)
228 {
229 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
230 struct hdd_context *hdd_ctx;
231 tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams;
232 tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams;
233
234 char *cmd, *sptr, *token;
235 uint8_t pattern_idx = 0;
236 uint8_t pattern_duration = 0;
237 char *pattern_buf;
238 uint16_t pattern_len = 0;
239 uint16_t i = 0;
240 QDF_STATUS status;
241 int ret;
242
243 hdd_enter();
244
245 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
246 hdd_err("Invalid adapter or adapter has invalid magic");
247 return -EINVAL;
248 }
249
250 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
251 ret = wlan_hdd_validate_context(hdd_ctx);
252 if (0 != ret)
253 return ret;
254
255 if (!wlan_hdd_validate_modules_state(hdd_ctx))
256 return -EINVAL;
257
258 if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) {
259 hdd_err("Periodic Tx Pattern Offload feature is not supported in firmware!");
260 return -EINVAL;
261 }
262
263 /* Get command from user */
264 if (count <= MAX_USER_COMMAND_SIZE_FRAME)
265 cmd = qdf_mem_malloc(count + 1);
266 else {
267 hdd_err("Command length is larger than %d bytes",
268 MAX_USER_COMMAND_SIZE_FRAME);
269
270 return -EINVAL;
271 }
272
273 if (!cmd) {
274 hdd_err("Memory allocation for cmd failed!");
275 return -ENOMEM;
276 }
277
278 if (copy_from_user(cmd, buf, count)) {
279 qdf_mem_free(cmd);
280 return -EFAULT;
281 }
282 cmd[count] = '\0';
283 sptr = cmd;
284
285 /* Get pattern idx */
286 token = strsep(&sptr, " ");
287 if (!token)
288 goto failure;
289 if (kstrtou8(token, 0, &pattern_idx))
290 goto failure;
291
292 if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1)) {
293 hdd_err("Pattern index: %d is not in the range (0 ~ %d)",
294 pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1);
295
296 goto failure;
297 }
298
299 /* Get pattern duration */
300 token = strsep(&sptr, " ");
301 if (!token)
302 goto failure;
303 if (kstrtou8(token, 0, &pattern_duration))
304 goto failure;
305
306 /* Delete pattern using index if duration is 0 */
307 if (!pattern_duration) {
308 if (!hdd_periodic_pattern_map[pattern_idx]) {
309 hdd_debug_rl("WoW pattern %d is not in the table.",
310 pattern_idx);
311
312 qdf_mem_free(cmd);
313 return -EINVAL;
314 }
315
316 delPeriodicTxPtrnParams =
317 qdf_mem_malloc(sizeof(tSirDelPeriodicTxPtrn));
318 if (!delPeriodicTxPtrnParams) {
319 qdf_mem_free(cmd);
320 return -ENOMEM;
321 }
322
323 delPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
324 qdf_copy_macaddr(&delPeriodicTxPtrnParams->mac_address,
325 &adapter->mac_addr);
326
327 /* Delete pattern */
328 status = sme_del_periodic_tx_ptrn(hdd_ctx->mac_handle,
329 delPeriodicTxPtrnParams);
330 if (QDF_STATUS_SUCCESS != status) {
331 hdd_err("sme_del_periodic_tx_ptrn() failed!");
332
333 qdf_mem_free(delPeriodicTxPtrnParams);
334 goto failure;
335 }
336
337 hdd_periodic_pattern_map[pattern_idx] = false;
338
339 qdf_mem_free(cmd);
340 qdf_mem_free(delPeriodicTxPtrnParams);
341 return count;
342 }
343
344 /*
345 * In SAP mode allow configuration without any connection check
346 * In STA mode check if it's in connected state before adding
347 * patterns
348 */
349 hdd_debug("device mode %d", adapter->device_mode);
350 if ((QDF_STA_MODE == adapter->device_mode) &&
351 (!hdd_cm_is_vdev_associated(adapter->deflink))) {
352 hdd_err("Not in Connected state!");
353 goto failure;
354 }
355
356 /* Get pattern */
357 token = strsep(&sptr, " ");
358 if (!token)
359 goto failure;
360
361 pattern_buf = token;
362 pattern_buf[strlen(pattern_buf) - 1] = '\0';
363 pattern_len = strlen(pattern_buf);
364
365 /* Since the pattern is a hex string, 2 characters represent 1 byte. */
366 if (pattern_len % 2) {
367 hdd_err("Malformed pattern!");
368
369 goto failure;
370 } else
371 pattern_len >>= 1;
372
373 if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE) {
374 hdd_err("Not an 802.3 frame!");
375
376 goto failure;
377 }
378
379 addPeriodicTxPtrnParams = qdf_mem_malloc(sizeof(tSirAddPeriodicTxPtrn));
380 if (!addPeriodicTxPtrnParams) {
381 qdf_mem_free(cmd);
382 return -ENOMEM;
383 }
384
385 addPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
386 addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500;
387 addPeriodicTxPtrnParams->ucPtrnSize = pattern_len;
388 qdf_copy_macaddr(&addPeriodicTxPtrnParams->mac_address,
389 &adapter->mac_addr);
390
391 /* Extract the pattern */
392 for (i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++) {
393 addPeriodicTxPtrnParams->ucPattern[i] =
394 (hex_to_bin(pattern_buf[0]) << 4) +
395 hex_to_bin(pattern_buf[1]);
396
397 /* Skip to next byte */
398 pattern_buf += 2;
399 }
400
401 /* Add pattern */
402 status = sme_add_periodic_tx_ptrn(hdd_ctx->mac_handle,
403 addPeriodicTxPtrnParams);
404 if (QDF_STATUS_SUCCESS != status) {
405 hdd_err("sme_add_periodic_tx_ptrn() failed!");
406
407 qdf_mem_free(addPeriodicTxPtrnParams);
408 goto failure;
409 }
410
411 if (!hdd_periodic_pattern_map[pattern_idx])
412 hdd_periodic_pattern_map[pattern_idx] = true;
413
414 qdf_mem_free(cmd);
415 qdf_mem_free(addPeriodicTxPtrnParams);
416 hdd_exit();
417 return count;
418
419 failure:
420 hdd_err("Invalid input. Input format is: ptrn_idx duration pattern");
421 qdf_mem_free(cmd);
422 return -EINVAL;
423 }
424
425 /**
426 * wcnss_patterngen_write() - SSR wrapper for __wcnss_patterngen_write
427 * @file: file pointer
428 * @buf: buffer
429 * @count: count
430 * @ppos: position pointer
431 *
432 * Return: 0 on success, error number otherwise
433 */
wcnss_patterngen_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)434 static ssize_t wcnss_patterngen_write(struct file *file,
435 const char __user *buf,
436 size_t count, loff_t *ppos)
437 {
438 struct net_device *net_dev = file_inode(file)->i_private;
439 struct osif_vdev_sync *vdev_sync;
440 ssize_t err_size;
441
442 err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync);
443 if (err_size)
444 return err_size;
445
446 err_size = __wcnss_patterngen_write(net_dev, buf, count, ppos);
447
448 osif_vdev_sync_op_stop(vdev_sync);
449
450 return err_size;
451 }
452
453 /**
454 * __wcnss_debugfs_open() - Generic debugfs open() handler
455 * @net_dev: net_device context used to register the debugfs file
456 *
457 * Return: Errno
458 */
__wcnss_debugfs_open(struct net_device * net_dev)459 static int __wcnss_debugfs_open(struct net_device *net_dev)
460 {
461 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
462 struct hdd_context *hdd_ctx;
463 int errno;
464
465 hdd_enter();
466
467 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
468 hdd_err("Invalid adapter or adapter has invalid magic");
469 return -EINVAL;
470 }
471
472 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
473 errno = wlan_hdd_validate_context(hdd_ctx);
474
475 hdd_exit();
476
477 return errno;
478 }
479
480 /**
481 * wcnss_debugfs_open() - SSR wrapper for __wcnss_debugfs_open
482 * @inode: inode pointer
483 * @file: file pointer
484 *
485 * Return: 0 on success, error number otherwise
486 */
wcnss_debugfs_open(struct inode * inode,struct file * file)487 static int wcnss_debugfs_open(struct inode *inode, struct file *file)
488 {
489 struct net_device *net_dev = inode->i_private;
490 struct osif_vdev_sync *vdev_sync;
491 int errno;
492
493 errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
494 if (errno)
495 return errno;
496
497 errno = __wcnss_debugfs_open(net_dev);
498
499 osif_vdev_sync_op_stop(vdev_sync);
500
501 return errno;
502 }
503
504 static const struct file_operations fops_wowpattern = {
505 .write = wcnss_wowpattern_write,
506 .open = wcnss_debugfs_open,
507 .owner = THIS_MODULE,
508 .llseek = default_llseek,
509 };
510
511 static const struct file_operations fops_patterngen = {
512 .write = wcnss_patterngen_write,
513 .open = wcnss_debugfs_open,
514 .owner = THIS_MODULE,
515 .llseek = default_llseek,
516 };
517
518 /**
519 * hdd_debugfs_init() - Initialize debugfs interface
520 * @adapter: interface adapter pointer
521 *
522 * Register support for the debugfs files supported by the driver.
523 *
524 * NB: The current implementation only supports debugfs operations
525 * on the primary interface, i.e. wlan0
526 *
527 * Return: QDF_STATUS_SUCCESS if all files registered,
528 * QDF_STATUS_E_FAILURE on failure
529 */
hdd_debugfs_init(struct hdd_adapter * adapter)530 QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter)
531 {
532 struct net_device *net_dev = adapter->dev;
533
534 adapter->debugfs_phy = debugfs_create_dir(net_dev->name, 0);
535
536 if (!adapter->debugfs_phy) {
537 hdd_err("debugfs: create folder %s fail", net_dev->name);
538 return QDF_STATUS_E_FAILURE;
539 }
540
541 if (!debugfs_create_file("wow_pattern", 00400 | 00200,
542 adapter->debugfs_phy, net_dev,
543 &fops_wowpattern))
544 return QDF_STATUS_E_FAILURE;
545
546 if (!debugfs_create_file("pattern_gen", 00400 | 00200,
547 adapter->debugfs_phy, net_dev,
548 &fops_patterngen))
549 return QDF_STATUS_E_FAILURE;
550
551 if (wlan_hdd_create_mib_stats_file(adapter))
552 return QDF_STATUS_E_FAILURE;
553
554 if (wlan_hdd_create_ll_stats_file(adapter))
555 return QDF_STATUS_E_FAILURE;
556
557 return QDF_STATUS_SUCCESS;
558 }
559
560 /**
561 * hdd_debugfs_exit() - Shutdown debugfs interface
562 * @adapter: interface adapter pointer
563 *
564 * Unregister support for the debugfs files supported by the driver.
565 *
566 * Return: None
567 */
hdd_debugfs_exit(struct hdd_adapter * adapter)568 void hdd_debugfs_exit(struct hdd_adapter *adapter)
569 {
570 debugfs_remove_recursive(adapter->debugfs_phy);
571 }
572