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