xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_debugfs.c (revision 4042de592dee0f571b75f68aea9f3e97378f146d)
1 /*
2  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 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: qdf_debugfs
22  * This file provides QDF debug file system APIs
23  */
24 
25 #include <qdf_debugfs.h>
26 #include <i_qdf_debugfs.h>
27 #include <qdf_mem.h>
28 #include <qdf_trace.h>
29 #include <qdf_module.h>
30 
31 /* A private structure definition to qdf sequence */
32 struct qdf_debugfs_seq_priv {
33 	bool stop;
34 };
35 
36 /* entry for root debugfs directory*/
37 static qdf_dentry_t qdf_debugfs_root;
38 
39 QDF_STATUS qdf_debugfs_init(void)
40 {
41 	qdf_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
42 
43 	if (!qdf_debugfs_root)
44 		return QDF_STATUS_E_FAILURE;
45 
46 	return QDF_STATUS_SUCCESS;
47 }
48 qdf_export_symbol(qdf_debugfs_init);
49 
50 void qdf_debugfs_exit(void)
51 {
52 	if (!qdf_debugfs_root)
53 		return;
54 
55 	debugfs_remove_recursive(qdf_debugfs_root);
56 	qdf_debugfs_root = NULL;
57 }
58 qdf_export_symbol(qdf_debugfs_exit);
59 
60 qdf_dentry_t qdf_debugfs_get_root(void)
61 {
62 	return qdf_debugfs_root;
63 }
64 qdf_export_symbol(qdf_debugfs_get_root);
65 
66 umode_t qdf_debugfs_get_filemode(uint16_t mode)
67 {
68 	umode_t ret = 0;
69 
70 	if (mode & QDF_FILE_USR_READ)
71 		ret |= 0400;
72 	if (mode & QDF_FILE_USR_WRITE)
73 		ret |= 0200;
74 
75 	if (mode & QDF_FILE_GRP_READ)
76 		ret |= 0040;
77 	if (mode & QDF_FILE_GRP_WRITE)
78 		ret |= 0020;
79 
80 	if (mode & QDF_FILE_OTH_READ)
81 		ret |= 0004;
82 	if (mode & QDF_FILE_OTH_WRITE)
83 		ret |= 0002;
84 
85 	return ret;
86 }
87 
88 /*
89  * ---------------------- Implementation note ---------------------------------
90  *
91  * A read in debugfs file triggers seq_read() which calls seq_read api. A
92  * sequence begins with the call of the function start(). If the return is a non
93  * NULL value, the function next() is called. This function is an iterator, the
94  * goal is to go though all the data. Each time next() is called, the function
95  * show() is also called. It writes data values in the buffer read by the user.
96  * The function next() is called until it returns NULL. The sequence ends when
97  * next() returns NULL, then the function stop() is called.
98  *
99  * NOTE: When a sequence is finished, another one starts. That means that
100  * at the end of function stop(), the function start() is called again. This
101  * loop finishes when the function start() returns NULL.
102  * ----------------------------------------------------------------------------
103  */
104 
105 /* .seq_start() */
106 static void *qdf_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
107 {
108 	struct qdf_debugfs_seq_priv *priv;
109 
110 	priv = qdf_mem_malloc(sizeof(*priv));
111 	if (!priv)
112 		return NULL;
113 
114 	priv->stop = false;
115 
116 	return priv;
117 }
118 
119 /* .seq_next() */
120 static void *qdf_debugfs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
121 {
122 	struct qdf_debugfs_seq_priv *priv = v;
123 
124 	if (priv)
125 		++*pos;
126 
127 	if (priv && priv->stop) {
128 		qdf_mem_free(priv);
129 		priv = NULL;
130 	}
131 
132 	return priv;
133 }
134 
135 /* .seq_stop() */
136 static void qdf_debugfs_seq_stop(struct seq_file *seq, void *v)
137 {
138 	qdf_mem_free(v);
139 }
140 
141 /* .seq_show() */
142 static int qdf_debugfs_seq_show(struct seq_file *seq, void *v)
143 {
144 	struct qdf_debugfs_seq_priv *priv = v;
145 	struct qdf_debugfs_fops *fops;
146 	QDF_STATUS status;
147 
148 	fops = seq->private;
149 
150 	if (fops && fops->show) {
151 		status = fops->show(seq, fops->priv);
152 
153 		if (priv && (status != QDF_STATUS_E_AGAIN))
154 			priv->stop = true;
155 	}
156 
157 	return 0;
158 }
159 
160 void qdf_debugfs_printf(qdf_debugfs_file_t file, const char *f, ...)
161 {
162 	va_list args;
163 
164 	va_start(args, f);
165 	seq_vprintf(file, f, args);
166 	va_end(args);
167 }
168 
169 qdf_export_symbol(qdf_debugfs_printf);
170 
171 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
172 
173 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
174 			 qdf_size_t len, int rowsize, int groupsize)
175 {
176 	seq_hex_dump(file, "", DUMP_PREFIX_OFFSET, rowsize, groupsize, buf, len,
177 		     false);
178 }
179 
180 qdf_export_symbol(qdf_debugfs_hexdump);
181 #else
182 
183 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
184 			 qdf_size_t len, int rowsize, int groupsize)
185 {
186 	char *dst;
187 	size_t dstlen, readlen, remaining = len;
188 	int prefix = 0;
189 	size_t commitlen;
190 
191 	while (remaining > 0 && (file->size > file->count)) {
192 		seq_printf(file, "%.8x: ", prefix);
193 
194 		readlen = qdf_min(remaining, (qdf_size_t)rowsize);
195 		dstlen = seq_get_buf(file, &dst);
196 		hex_dump_to_buffer(buf, readlen, rowsize, groupsize, dst,
197 				   dstlen, false);
198 		commitlen = strnlen(dst, dstlen);
199 		seq_commit(file, commitlen);
200 		seq_putc(file, '\n');
201 
202 		remaining = (remaining > rowsize) ? remaining - rowsize : 0;
203 		buf += readlen;
204 		prefix += rowsize;
205 	}
206 }
207 
208 qdf_export_symbol(qdf_debugfs_hexdump);
209 #endif
210 
211 bool qdf_debugfs_overflow(qdf_debugfs_file_t file)
212 {
213 	return seq_has_overflowed(file);
214 }
215 
216 qdf_export_symbol(qdf_debugfs_overflow);
217 
218 void qdf_debugfs_write(qdf_debugfs_file_t file, const uint8_t *buf,
219 		       qdf_size_t len)
220 {
221 	seq_write(file, buf, len);
222 }
223 
224 /* sequential file operation table */
225 static const struct seq_operations __qdf_debugfs_seq_ops = {
226 	.start = qdf_debugfs_seq_start,
227 	.next = qdf_debugfs_seq_next,
228 	.stop = qdf_debugfs_seq_stop,
229 	.show = qdf_debugfs_seq_show,
230 };
231 
232 /* .open() */
233 static int qdf_seq_open(struct inode *inode, struct file *file)
234 {
235 	void *private = inode->i_private;
236 	struct seq_file *seq;
237 	int rc;
238 
239 	/**
240 	 * Note: seq_open() will allocate a struct seq_file and store its
241 	 * pointer in @file->private_data. It warns if private_data is not NULL.
242 	 */
243 
244 	rc = seq_open(file, &__qdf_debugfs_seq_ops);
245 
246 	if (rc == 0) {
247 		seq = file->private_data;
248 		seq->private = private;
249 	}
250 
251 	return rc;
252 }
253 
254 /* .write() */
255 static ssize_t qdf_seq_write(struct file *filp, const char __user *ubuf,
256 			     size_t len, loff_t *ppos)
257 {
258 	struct qdf_debugfs_fops *fops;
259 	struct seq_file *seq;
260 	u8 *buf;
261 	ssize_t rc = 0;
262 
263 	if (len == 0)
264 		return 0;
265 
266 	seq = filp->private_data;
267 	fops = seq->private;
268 	if (fops && fops->write) {
269 		buf = qdf_mem_malloc(len + 1);
270 		if (buf) {
271 			buf[len] = '\0';
272 			rc = simple_write_to_buffer(buf, len, ppos, ubuf, len);
273 			fops->write(fops->priv, buf, len + 1);
274 			qdf_mem_free(buf);
275 		}
276 	}
277 
278 	return rc;
279 }
280 
281 /* debugfs file operation table */
282 static const struct file_operations __qdf_debugfs_fops = {
283 	.owner = THIS_MODULE,
284 	.open = qdf_seq_open,
285 	.read = seq_read,
286 	.llseek = seq_lseek,
287 	.release = seq_release,
288 	.write = qdf_seq_write,
289 };
290 
291 qdf_dentry_t qdf_debugfs_create_dir(const char *name, qdf_dentry_t parent)
292 {
293 	qdf_dentry_t dir;
294 
295 	if (!name)
296 		return NULL;
297 	if (!parent)
298 		parent = qdf_debugfs_get_root();
299 
300 	dir = debugfs_create_dir(name, parent);
301 
302 	if (IS_ERR_OR_NULL(dir)) {
303 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
304 			  "%s creation failed", name);
305 		dir = NULL;
306 	}
307 
308 	return dir;
309 }
310 qdf_export_symbol(qdf_debugfs_create_dir);
311 
312 qdf_dentry_t qdf_debugfs_create_file(const char *name, uint16_t mode,
313 				     qdf_dentry_t parent,
314 				     struct qdf_debugfs_fops *fops)
315 {
316 	qdf_dentry_t file;
317 	umode_t filemode;
318 
319 	if (!name || !fops)
320 		return NULL;
321 
322 	if (!parent)
323 		parent = qdf_debugfs_get_root();
324 
325 	filemode = qdf_debugfs_get_filemode(mode);
326 	file = debugfs_create_file(name, filemode, parent, fops,
327 				   &__qdf_debugfs_fops);
328 
329 	if (IS_ERR_OR_NULL(file)) {
330 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
331 			  "%s creation failed 0x%pK", name, file);
332 		file = NULL;
333 	}
334 
335 	return file;
336 }
337 qdf_export_symbol(qdf_debugfs_create_file);
338 
339 void qdf_debugfs_create_u8(const char *name, uint16_t mode,
340 			   qdf_dentry_t parent, u8 *value)
341 {
342 	umode_t filemode;
343 
344 	if (!name)
345 		return;
346 
347 	if (!parent)
348 		parent = qdf_debugfs_get_root();
349 
350 	filemode = qdf_debugfs_get_filemode(mode);
351 	debugfs_create_u8(name, filemode, parent, value);
352 }
353 
354 void qdf_debugfs_create_u16(const char *name, uint16_t mode,
355 			    qdf_dentry_t parent, u16 *value)
356 {
357 	umode_t filemode;
358 
359 	if (!name)
360 		return;
361 
362 	if (!parent)
363 		parent = qdf_debugfs_get_root();
364 
365 	filemode = qdf_debugfs_get_filemode(mode);
366 	debugfs_create_u16(name, filemode, parent, value);
367 }
368 
369 qdf_export_symbol(qdf_debugfs_create_u16);
370 
371 void qdf_debugfs_create_u32(const char *name,
372 			    uint16_t mode,
373 			    qdf_dentry_t parent, u32 *value)
374 {
375 	umode_t filemode;
376 
377 	if (!name)
378 		return;
379 
380 	if (!parent)
381 		parent = qdf_debugfs_get_root();
382 
383 	filemode = qdf_debugfs_get_filemode(mode);
384 	debugfs_create_u32(name, filemode, parent, value);
385 }
386 
387 qdf_export_symbol(qdf_debugfs_create_u32);
388 
389 void qdf_debugfs_create_u64(const char *name, uint16_t mode,
390 			    qdf_dentry_t parent, u64 *value)
391 {
392 	umode_t filemode;
393 
394 	if (!name)
395 		return;
396 
397 	if (!parent)
398 		parent = qdf_debugfs_get_root();
399 
400 	filemode = qdf_debugfs_get_filemode(mode);
401 	debugfs_create_u64(name, filemode, parent, value);
402 }
403 
404 qdf_export_symbol(qdf_debugfs_create_u64);
405 
406 void qdf_debugfs_create_atomic(const char *name, uint16_t mode,
407 			       qdf_dentry_t parent, qdf_atomic_t *value)
408 {
409 	umode_t filemode;
410 
411 	if (!name)
412 		return;
413 
414 	if (!parent)
415 		parent = qdf_debugfs_get_root();
416 
417 	filemode = qdf_debugfs_get_filemode(mode);
418 	debugfs_create_atomic_t(name, filemode, parent, value);
419 }
420 
421 qdf_export_symbol(qdf_debugfs_create_atomic);
422 
423 static int qdf_debugfs_string_show(struct seq_file *seq, void *pos)
424 {
425 	char *str = seq->private;
426 
427 	seq_puts(seq, str);
428 	seq_putc(seq, '\n');
429 
430 	return 0;
431 }
432 
433 static int qdf_debugfs_string_open(struct inode *inode, struct file *file)
434 {
435 	return single_open(file, qdf_debugfs_string_show, inode->i_private);
436 }
437 
438 static const struct file_operations qdf_string_fops = {
439 	.owner = THIS_MODULE,
440 	.open = qdf_debugfs_string_open,
441 	.read = seq_read,
442 	.llseek = seq_lseek,
443 	.release = single_release
444 };
445 
446 qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode,
447 				       qdf_dentry_t parent, char *str)
448 {
449 	umode_t filemode;
450 
451 	if (!name)
452 		return NULL;
453 
454 	if (!parent)
455 		parent = qdf_debugfs_get_root();
456 
457 	filemode = qdf_debugfs_get_filemode(mode);
458 	return debugfs_create_file(name, filemode, parent, str,
459 				   &qdf_string_fops);
460 }
461 qdf_export_symbol(qdf_debugfs_create_string);
462 
463 void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)
464 {
465 	debugfs_remove_recursive(d);
466 }
467 qdf_export_symbol(qdf_debugfs_remove_dir_recursive);
468 
469 void qdf_debugfs_remove_dir(qdf_dentry_t d)
470 {
471 	debugfs_remove(d);
472 }
473 qdf_export_symbol(qdf_debugfs_remove_dir);
474 
475 void qdf_debugfs_remove_file(qdf_dentry_t d)
476 {
477 	debugfs_remove(d);
478 }
479 qdf_export_symbol(qdf_debugfs_remove_file);
480 
481 static int qdf_debugfs_single_show(struct seq_file *seq, void *v)
482 {
483 	struct qdf_debugfs_fops *fops = seq->private;
484 
485 	if (fops && fops->show)
486 		fops->show(seq, fops->priv);
487 
488 	return 0;
489 }
490 
491 /* .open() */
492 static int qdf_debugfs_single_open(struct inode *inode, struct file *file)
493 {
494 	return single_open(file, qdf_debugfs_single_show,
495 			   inode->i_private);
496 }
497 
498 /* File operations for the simplified version */
499 static const struct file_operations qdf_debugfs_fops_simple = {
500 	.owner          = THIS_MODULE,
501 	.open           = qdf_debugfs_single_open,
502 	.release        = single_release,
503 	.read           = seq_read,
504 	.llseek         = seq_lseek,
505 };
506 
507 qdf_dentry_t qdf_debugfs_create_file_simplified(
508 	const char *name, uint16_t mode,
509 	qdf_dentry_t parent, struct qdf_debugfs_fops *fops)
510 {
511 	qdf_dentry_t file;
512 	umode_t filemode;
513 
514 	if (!name || !fops)
515 		return NULL;
516 
517 	if (!parent)
518 		parent = qdf_debugfs_get_root();
519 
520 	filemode = qdf_debugfs_get_filemode(mode);
521 	file = debugfs_create_file(name, filemode, parent, fops,
522 				   &qdf_debugfs_fops_simple);
523 
524 	if (IS_ERR_OR_NULL(file)) {
525 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
526 			  "%s creation failed 0x%pK", name, file);
527 		file = NULL;
528 	}
529 
530 	return file;
531 }
532 qdf_export_symbol(qdf_debugfs_create_file_simplified);
533 
534 int qdf_debugfs_printer(void *priv, const char *fmt, ...)
535 {
536 	struct seq_file *file = priv;
537 	va_list args;
538 
539 	va_start(args, fmt);
540 	seq_vprintf(file, fmt, args);
541 	seq_puts(file, "\n");
542 	va_end(args);
543 
544 	return 0;
545 }
546 qdf_export_symbol(qdf_debugfs_printer);
547 
548 qdf_dentry_t qdf_debugfs_create_blob(const char *name, umode_t mode,
549 				     qdf_dentry_t parent,
550 				     qdf_debugfs_blob_wrap_t blob)
551 {
552 	return debugfs_create_blob(name, mode, parent, blob);
553 }
554 
555 qdf_export_symbol(qdf_debugfs_create_blob);
556 
557 qdf_dentry_t qdf_debugfs_create_entry(const char *name, uint16_t mode,
558 				      qdf_dentry_t parent,
559 				      qdf_entry_t data,
560 				      const qdf_file_ops_t fops)
561 {
562 	qdf_dentry_t file;
563 	umode_t filemode;
564 
565 	if (!name || !fops)
566 		return NULL;
567 
568 	if (!parent)
569 		parent = qdf_debugfs_get_root();
570 
571 	filemode = qdf_debugfs_get_filemode(mode);
572 	file = debugfs_create_file(name, filemode, parent, data, fops);
573 
574 	if (IS_ERR_OR_NULL(file)) {
575 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
576 			  "%s creation failed 0x%pK", name, file);
577 		file = NULL;
578 	}
579 
580 	return file;
581 }
582 
583 qdf_export_symbol(qdf_debugfs_create_entry);
584