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