xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_debugfs.c (revision bea437e2293c3d4fb1b5704fcf633aedac996962)
1 /*
2  * Copyright (c) 2017-2019 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 qdf_dentry_t 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 NULL;
341 
342 	if (!parent)
343 		parent = qdf_debugfs_get_root();
344 
345 	filemode = qdf_debugfs_get_filemode(mode);
346 	return debugfs_create_u8(name, filemode, parent, value);
347 }
348 
349 qdf_dentry_t 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 NULL;
356 
357 	if (!parent)
358 		parent = qdf_debugfs_get_root();
359 
360 	filemode = qdf_debugfs_get_filemode(mode);
361 	return debugfs_create_u16(name, filemode, parent, value);
362 }
363 qdf_export_symbol(qdf_debugfs_create_u16);
364 
365 qdf_dentry_t qdf_debugfs_create_u32(const char *name,
366 				    uint16_t mode,
367 				    qdf_dentry_t parent, u32 *value)
368 {
369 	umode_t filemode;
370 
371 	if (!name)
372 		return NULL;
373 
374 	if (!parent)
375 		parent = qdf_debugfs_get_root();
376 
377 	filemode = qdf_debugfs_get_filemode(mode);
378 	return debugfs_create_u32(name, filemode, parent, value);
379 }
380 qdf_export_symbol(qdf_debugfs_create_u32);
381 
382 qdf_dentry_t qdf_debugfs_create_u64(const char *name, uint16_t mode,
383 				    qdf_dentry_t parent, u64 *value)
384 {
385 	umode_t filemode;
386 
387 	if (!name)
388 		return NULL;
389 
390 	if (!parent)
391 		parent = qdf_debugfs_get_root();
392 
393 	filemode = qdf_debugfs_get_filemode(mode);
394 	return debugfs_create_u64(name, filemode, parent, value);
395 }
396 qdf_export_symbol(qdf_debugfs_create_u64);
397 
398 qdf_dentry_t qdf_debugfs_create_atomic(const char *name, uint16_t mode,
399 				       qdf_dentry_t parent, qdf_atomic_t *value)
400 {
401 	umode_t filemode;
402 
403 	if (!name)
404 		return NULL;
405 
406 	if (!parent)
407 		parent = qdf_debugfs_get_root();
408 
409 	filemode = qdf_debugfs_get_filemode(mode);
410 	return debugfs_create_atomic_t(name, filemode, parent, value);
411 }
412 qdf_export_symbol(qdf_debugfs_create_atomic);
413 
414 static int qdf_debugfs_string_show(struct seq_file *seq, void *pos)
415 {
416 	char *str = seq->private;
417 
418 	seq_puts(seq, str);
419 	seq_putc(seq, '\n');
420 
421 	return 0;
422 }
423 
424 static int qdf_debugfs_string_open(struct inode *inode, struct file *file)
425 {
426 	return single_open(file, qdf_debugfs_string_show, inode->i_private);
427 }
428 
429 static const struct file_operations qdf_string_fops = {
430 	.owner = THIS_MODULE,
431 	.open = qdf_debugfs_string_open,
432 	.read = seq_read,
433 	.llseek = seq_lseek,
434 	.release = single_release
435 };
436 
437 qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode,
438 				       qdf_dentry_t parent, char *str)
439 {
440 	umode_t filemode;
441 
442 	if (!name)
443 		return NULL;
444 
445 	if (!parent)
446 		parent = qdf_debugfs_get_root();
447 
448 	filemode = qdf_debugfs_get_filemode(mode);
449 	return debugfs_create_file(name, filemode, parent, str,
450 				   &qdf_string_fops);
451 }
452 qdf_export_symbol(qdf_debugfs_create_string);
453 
454 void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)
455 {
456 	debugfs_remove_recursive(d);
457 }
458 qdf_export_symbol(qdf_debugfs_remove_dir_recursive);
459 
460 void qdf_debugfs_remove_dir(qdf_dentry_t d)
461 {
462 	debugfs_remove(d);
463 }
464 qdf_export_symbol(qdf_debugfs_remove_dir);
465 
466 void qdf_debugfs_remove_file(qdf_dentry_t d)
467 {
468 	debugfs_remove(d);
469 }
470 qdf_export_symbol(qdf_debugfs_remove_file);
471 
472 static int qdf_debugfs_single_show(struct seq_file *seq, void *v)
473 {
474 	struct qdf_debugfs_fops *fops = seq->private;
475 
476 	if (fops && fops->show)
477 		fops->show(seq, fops->priv);
478 
479 	return 0;
480 }
481 
482 /* .open() */
483 static int qdf_debugfs_single_open(struct inode *inode, struct file *file)
484 {
485 	return single_open(file, qdf_debugfs_single_show,
486 			   inode->i_private);
487 }
488 
489 /* File operations for the simplified version */
490 static const struct file_operations qdf_debugfs_fops_simple = {
491 	.owner          = THIS_MODULE,
492 	.open           = qdf_debugfs_single_open,
493 	.release        = single_release,
494 	.read           = seq_read,
495 	.llseek         = seq_lseek,
496 };
497 
498 qdf_dentry_t qdf_debugfs_create_file_simplified(
499 	const char *name, uint16_t mode,
500 	qdf_dentry_t parent, struct qdf_debugfs_fops *fops)
501 {
502 	qdf_dentry_t file;
503 	umode_t filemode;
504 
505 	if (!name || !fops)
506 		return NULL;
507 
508 	if (!parent)
509 		parent = qdf_debugfs_get_root();
510 
511 	filemode = qdf_debugfs_get_filemode(mode);
512 	file = debugfs_create_file(name, filemode, parent, fops,
513 				   &qdf_debugfs_fops_simple);
514 
515 	if (IS_ERR_OR_NULL(file)) {
516 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
517 			  "%s creation failed 0x%pK", name, file);
518 		file = NULL;
519 	}
520 
521 	return file;
522 }
523 qdf_export_symbol(qdf_debugfs_create_file_simplified);
524 
525 int qdf_debugfs_printer(void *priv, const char *fmt, ...)
526 {
527 	struct seq_file *file = priv;
528 	va_list args;
529 
530 	va_start(args, fmt);
531 	seq_vprintf(file, fmt, args);
532 	seq_puts(file, "\n");
533 	va_end(args);
534 
535 	return 0;
536 }
537 qdf_export_symbol(qdf_debugfs_printer);
538