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
qdf_debugfs_init(void)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
qdf_debugfs_exit(void)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
qdf_debugfs_get_root(void)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
qdf_debugfs_get_filemode(uint16_t mode)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() */
qdf_debugfs_seq_start(struct seq_file * seq,loff_t * pos)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() */
qdf_debugfs_seq_next(struct seq_file * seq,void * v,loff_t * pos)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() */
qdf_debugfs_seq_stop(struct seq_file * seq,void * v)136 static void qdf_debugfs_seq_stop(struct seq_file *seq, void *v)
137 {
138 qdf_mem_free(v);
139 }
140
141 /* .seq_show() */
qdf_debugfs_seq_show(struct seq_file * seq,void * v)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
qdf_debugfs_printf(qdf_debugfs_file_t file,const char * f,...)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
qdf_debugfs_hexdump(qdf_debugfs_file_t file,const uint8_t * buf,qdf_size_t len,int rowsize,int groupsize)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
qdf_debugfs_hexdump(qdf_debugfs_file_t file,const uint8_t * buf,qdf_size_t len,int rowsize,int groupsize)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
qdf_debugfs_overflow(qdf_debugfs_file_t file)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
qdf_debugfs_write(qdf_debugfs_file_t file,const uint8_t * buf,qdf_size_t len)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() */
qdf_seq_open(struct inode * inode,struct file * file)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() */
qdf_seq_write(struct file * filp,const char __user * ubuf,size_t len,loff_t * ppos)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
qdf_debugfs_create_dir(const char * name,qdf_dentry_t parent)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
qdf_debugfs_create_file(const char * name,uint16_t mode,qdf_dentry_t parent,struct qdf_debugfs_fops * fops)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
qdf_debugfs_create_u8(const char * name,uint16_t mode,qdf_dentry_t parent,u8 * value)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
qdf_debugfs_create_u16(const char * name,uint16_t mode,qdf_dentry_t parent,u16 * value)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
qdf_debugfs_create_u32(const char * name,uint16_t mode,qdf_dentry_t parent,u32 * value)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
qdf_debugfs_create_u64(const char * name,uint16_t mode,qdf_dentry_t parent,u64 * value)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
qdf_debugfs_create_atomic(const char * name,uint16_t mode,qdf_dentry_t parent,qdf_atomic_t * value)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
qdf_debugfs_string_show(struct seq_file * seq,void * pos)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
qdf_debugfs_string_open(struct inode * inode,struct file * file)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
qdf_debugfs_create_string(const char * name,uint16_t mode,qdf_dentry_t parent,char * str)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
qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)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
qdf_debugfs_remove_dir(qdf_dentry_t d)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
qdf_debugfs_remove_file(qdf_dentry_t d)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
qdf_debugfs_single_show(struct seq_file * seq,void * v)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() */
qdf_debugfs_single_open(struct inode * inode,struct file * file)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
qdf_debugfs_create_file_simplified(const char * name,uint16_t mode,qdf_dentry_t parent,struct qdf_debugfs_fops * fops)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
qdf_debugfs_printer(void * priv,const char * fmt,...)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
qdf_debugfs_create_blob(const char * name,umode_t mode,qdf_dentry_t parent,qdf_debugfs_blob_wrap_t blob)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
qdf_debugfs_create_entry(const char * name,uint16_t mode,qdf_dentry_t parent,qdf_entry_t data,const qdf_file_ops_t fops)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