xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_debugfs.c (revision a86b23ee68a2491aede2e03991f3fb37046f4e41)
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 #else
180 
181 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
182 			 qdf_size_t len, int rowsize, int groupsize)
183 {
184 	char *dst;
185 	size_t dstlen, readlen, remaining = len;
186 	int prefix = 0;
187 	size_t commitlen;
188 
189 	while (remaining > 0 && (file->size > file->count)) {
190 		seq_printf(file, "%.8x: ", prefix);
191 
192 		readlen = qdf_min(remaining, (qdf_size_t)rowsize);
193 		dstlen = seq_get_buf(file, &dst);
194 		hex_dump_to_buffer(buf, readlen, rowsize, groupsize, dst,
195 				   dstlen, false);
196 		commitlen = strnlen(dst, dstlen);
197 		seq_commit(file, commitlen);
198 		seq_putc(file, '\n');
199 
200 		remaining = (remaining > rowsize) ? remaining - rowsize : 0;
201 		buf += readlen;
202 		prefix += rowsize;
203 	}
204 }
205 
206 #endif
207 
208 bool qdf_debugfs_overflow(qdf_debugfs_file_t file)
209 {
210 	return seq_has_overflowed(file);
211 }
212 
213 void qdf_debugfs_write(qdf_debugfs_file_t file, const uint8_t *buf,
214 		       qdf_size_t len)
215 {
216 	seq_write(file, buf, len);
217 }
218 
219 /* sequential file operation table */
220 static const struct seq_operations __qdf_debugfs_seq_ops = {
221 	.start = qdf_debugfs_seq_start,
222 	.next = qdf_debugfs_seq_next,
223 	.stop = qdf_debugfs_seq_stop,
224 	.show = qdf_debugfs_seq_show,
225 };
226 
227 /* .open() */
228 static int qdf_seq_open(struct inode *inode, struct file *file)
229 {
230 	void *private = inode->i_private;
231 	struct seq_file *seq;
232 	int rc;
233 
234 	/**
235 	 * Note: seq_open() will allocate a struct seq_file and store its
236 	 * pointer in @file->private_data. It warns if private_data is not NULL.
237 	 */
238 
239 	rc = seq_open(file, &__qdf_debugfs_seq_ops);
240 
241 	if (rc == 0) {
242 		seq = file->private_data;
243 		seq->private = private;
244 	}
245 
246 	return rc;
247 }
248 
249 /* .write() */
250 static ssize_t qdf_seq_write(struct file *filp, const char __user *ubuf,
251 			     size_t len, loff_t *ppos)
252 {
253 	struct qdf_debugfs_fops *fops;
254 	struct seq_file *seq;
255 	u8 *buf;
256 	ssize_t rc = 0;
257 
258 	if (len == 0)
259 		return 0;
260 
261 	seq = filp->private_data;
262 	fops = seq->private;
263 	if (fops && fops->write) {
264 		buf = qdf_mem_malloc(len + 1);
265 		if (buf) {
266 			buf[len] = '\0';
267 			rc = simple_write_to_buffer(buf, len, ppos, ubuf, len);
268 			fops->write(fops->priv, buf, len + 1);
269 			qdf_mem_free(buf);
270 		}
271 	}
272 
273 	return rc;
274 }
275 
276 /* debugfs file operation table */
277 static const struct file_operations __qdf_debugfs_fops = {
278 	.owner = THIS_MODULE,
279 	.open = qdf_seq_open,
280 	.read = seq_read,
281 	.llseek = seq_lseek,
282 	.release = seq_release,
283 	.write = qdf_seq_write,
284 };
285 
286 qdf_dentry_t qdf_debugfs_create_dir(const char *name, qdf_dentry_t parent)
287 {
288 	qdf_dentry_t dir;
289 
290 	if (!name)
291 		return NULL;
292 	if (!parent)
293 		parent = qdf_debugfs_get_root();
294 
295 	dir = debugfs_create_dir(name, parent);
296 
297 	if (IS_ERR_OR_NULL(dir)) {
298 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
299 			  "%s creation failed", name);
300 		dir = NULL;
301 	}
302 
303 	return dir;
304 }
305 qdf_export_symbol(qdf_debugfs_create_dir);
306 
307 qdf_dentry_t qdf_debugfs_create_file(const char *name, uint16_t mode,
308 				     qdf_dentry_t parent,
309 				     struct qdf_debugfs_fops *fops)
310 {
311 	qdf_dentry_t file;
312 	umode_t filemode;
313 
314 	if (!name || !fops)
315 		return NULL;
316 
317 	if (!parent)
318 		parent = qdf_debugfs_get_root();
319 
320 	filemode = qdf_debugfs_get_filemode(mode);
321 	file = debugfs_create_file(name, filemode, parent, fops,
322 				   &__qdf_debugfs_fops);
323 
324 	if (IS_ERR_OR_NULL(file)) {
325 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
326 			  "%s creation failed 0x%pK", name, file);
327 		file = NULL;
328 	}
329 
330 	return file;
331 }
332 qdf_export_symbol(qdf_debugfs_create_file);
333 
334 void qdf_debugfs_create_u8(const char *name, uint16_t mode,
335 			   qdf_dentry_t parent, u8 *value)
336 {
337 	umode_t filemode;
338 
339 	if (!name)
340 		return;
341 
342 	if (!parent)
343 		parent = qdf_debugfs_get_root();
344 
345 	filemode = qdf_debugfs_get_filemode(mode);
346 	debugfs_create_u8(name, filemode, parent, value);
347 }
348 
349 void qdf_debugfs_create_u16(const char *name, uint16_t mode,
350 			    qdf_dentry_t parent, u16 *value)
351 {
352 	umode_t filemode;
353 
354 	if (!name)
355 		return;
356 
357 	if (!parent)
358 		parent = qdf_debugfs_get_root();
359 
360 	filemode = qdf_debugfs_get_filemode(mode);
361 	debugfs_create_u16(name, filemode, parent, value);
362 }
363 
364 qdf_export_symbol(qdf_debugfs_create_u16);
365 
366 void qdf_debugfs_create_u32(const char *name,
367 			    uint16_t mode,
368 			    qdf_dentry_t parent, u32 *value)
369 {
370 	umode_t filemode;
371 
372 	if (!name)
373 		return;
374 
375 	if (!parent)
376 		parent = qdf_debugfs_get_root();
377 
378 	filemode = qdf_debugfs_get_filemode(mode);
379 	debugfs_create_u32(name, filemode, parent, value);
380 }
381 
382 qdf_export_symbol(qdf_debugfs_create_u32);
383 
384 void qdf_debugfs_create_u64(const char *name, uint16_t mode,
385 			    qdf_dentry_t parent, u64 *value)
386 {
387 	umode_t filemode;
388 
389 	if (!name)
390 		return;
391 
392 	if (!parent)
393 		parent = qdf_debugfs_get_root();
394 
395 	filemode = qdf_debugfs_get_filemode(mode);
396 	debugfs_create_u64(name, filemode, parent, value);
397 }
398 
399 qdf_export_symbol(qdf_debugfs_create_u64);
400 
401 void qdf_debugfs_create_atomic(const char *name, uint16_t mode,
402 			       qdf_dentry_t parent, qdf_atomic_t *value)
403 {
404 	umode_t filemode;
405 
406 	if (!name)
407 		return;
408 
409 	if (!parent)
410 		parent = qdf_debugfs_get_root();
411 
412 	filemode = qdf_debugfs_get_filemode(mode);
413 	debugfs_create_atomic_t(name, filemode, parent, value);
414 }
415 
416 qdf_export_symbol(qdf_debugfs_create_atomic);
417 
418 static int qdf_debugfs_string_show(struct seq_file *seq, void *pos)
419 {
420 	char *str = seq->private;
421 
422 	seq_puts(seq, str);
423 	seq_putc(seq, '\n');
424 
425 	return 0;
426 }
427 
428 static int qdf_debugfs_string_open(struct inode *inode, struct file *file)
429 {
430 	return single_open(file, qdf_debugfs_string_show, inode->i_private);
431 }
432 
433 static const struct file_operations qdf_string_fops = {
434 	.owner = THIS_MODULE,
435 	.open = qdf_debugfs_string_open,
436 	.read = seq_read,
437 	.llseek = seq_lseek,
438 	.release = single_release
439 };
440 
441 qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode,
442 				       qdf_dentry_t parent, char *str)
443 {
444 	umode_t filemode;
445 
446 	if (!name)
447 		return NULL;
448 
449 	if (!parent)
450 		parent = qdf_debugfs_get_root();
451 
452 	filemode = qdf_debugfs_get_filemode(mode);
453 	return debugfs_create_file(name, filemode, parent, str,
454 				   &qdf_string_fops);
455 }
456 qdf_export_symbol(qdf_debugfs_create_string);
457 
458 void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)
459 {
460 	debugfs_remove_recursive(d);
461 }
462 qdf_export_symbol(qdf_debugfs_remove_dir_recursive);
463 
464 void qdf_debugfs_remove_dir(qdf_dentry_t d)
465 {
466 	debugfs_remove(d);
467 }
468 qdf_export_symbol(qdf_debugfs_remove_dir);
469 
470 void qdf_debugfs_remove_file(qdf_dentry_t d)
471 {
472 	debugfs_remove(d);
473 }
474 qdf_export_symbol(qdf_debugfs_remove_file);
475 
476 static int qdf_debugfs_single_show(struct seq_file *seq, void *v)
477 {
478 	struct qdf_debugfs_fops *fops = seq->private;
479 
480 	if (fops && fops->show)
481 		fops->show(seq, fops->priv);
482 
483 	return 0;
484 }
485 
486 /* .open() */
487 static int qdf_debugfs_single_open(struct inode *inode, struct file *file)
488 {
489 	return single_open(file, qdf_debugfs_single_show,
490 			   inode->i_private);
491 }
492 
493 /* File operations for the simplified version */
494 static const struct file_operations qdf_debugfs_fops_simple = {
495 	.owner          = THIS_MODULE,
496 	.open           = qdf_debugfs_single_open,
497 	.release        = single_release,
498 	.read           = seq_read,
499 	.llseek         = seq_lseek,
500 };
501 
502 qdf_dentry_t qdf_debugfs_create_file_simplified(
503 	const char *name, uint16_t mode,
504 	qdf_dentry_t parent, struct qdf_debugfs_fops *fops)
505 {
506 	qdf_dentry_t file;
507 	umode_t filemode;
508 
509 	if (!name || !fops)
510 		return NULL;
511 
512 	if (!parent)
513 		parent = qdf_debugfs_get_root();
514 
515 	filemode = qdf_debugfs_get_filemode(mode);
516 	file = debugfs_create_file(name, filemode, parent, fops,
517 				   &qdf_debugfs_fops_simple);
518 
519 	if (IS_ERR_OR_NULL(file)) {
520 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
521 			  "%s creation failed 0x%pK", name, file);
522 		file = NULL;
523 	}
524 
525 	return file;
526 }
527 qdf_export_symbol(qdf_debugfs_create_file_simplified);
528 
529 int qdf_debugfs_printer(void *priv, const char *fmt, ...)
530 {
531 	struct seq_file *file = priv;
532 	va_list args;
533 
534 	va_start(args, fmt);
535 	seq_vprintf(file, fmt, args);
536 	seq_puts(file, "\n");
537 	va_end(args);
538 
539 	return 0;
540 }
541 qdf_export_symbol(qdf_debugfs_printer);
542