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