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