xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_debugfs.c (revision 6ecd284e5a94a1c96e26d571dd47419ac305990d)
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 QDF_STATUS qdf_debugfs_exit(void)
50 {
51 	if (!qdf_debugfs_root)
52 		return QDF_STATUS_SUCCESS;
53 
54 	debugfs_remove_recursive(qdf_debugfs_root);
55 	qdf_debugfs_root = NULL;
56 
57 	return QDF_STATUS_SUCCESS;
58 }
59 qdf_export_symbol(qdf_debugfs_exit);
60 
61 qdf_dentry_t qdf_debugfs_get_root(void)
62 {
63 	return qdf_debugfs_root;
64 }
65 qdf_export_symbol(qdf_debugfs_get_root);
66 
67 umode_t qdf_debugfs_get_filemode(uint16_t mode)
68 {
69 	umode_t ret = 0;
70 
71 	if (mode & QDF_FILE_USR_READ)
72 		ret |= 0400;
73 	if (mode & QDF_FILE_USR_WRITE)
74 		ret |= 0200;
75 
76 	if (mode & QDF_FILE_GRP_READ)
77 		ret |= 0040;
78 	if (mode & QDF_FILE_GRP_WRITE)
79 		ret |= 0020;
80 
81 	if (mode & QDF_FILE_OTH_READ)
82 		ret |= 0004;
83 	if (mode & QDF_FILE_OTH_WRITE)
84 		ret |= 0002;
85 
86 	return ret;
87 }
88 
89 /**
90  * ---------------------- Implementation note ---------------------------------
91  *
92  * A read in debugfs file triggers seq_read() which calls seq_read api. A
93  * sequence begins with the call of the function start(). If the return is a non
94  * NULL value, the function next() is called. This function is an iterator, the
95  * goal is to go though all the data. Each time next() is called, the function
96  * show() is also called. It writes data values in the buffer read by the user.
97  * The function next() is called until it returns NULL. The sequence ends when
98  * next() returns NULL, then the function stop() is called.
99  *
100  * NOTE: When a sequence is finished, another one starts. That means that
101  * at the end of function stop(), the function start() is called again. This
102  * loop finishes when the function start() returns NULL.
103  * ----------------------------------------------------------------------------
104  */
105 
106 /* .seq_start() */
107 static void *qdf_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
108 {
109 	struct qdf_debugfs_seq_priv *priv;
110 
111 	priv = qdf_mem_malloc(sizeof(*priv));
112 	if (!priv)
113 		return NULL;
114 
115 	priv->stop = false;
116 
117 	return priv;
118 }
119 
120 /* .seq_next() */
121 static void *qdf_debugfs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
122 {
123 	struct qdf_debugfs_seq_priv *priv = v;
124 
125 	if (priv)
126 		++*pos;
127 
128 	if (priv && priv->stop) {
129 		qdf_mem_free(priv);
130 		priv = NULL;
131 	}
132 
133 	return priv;
134 }
135 
136 /* .seq_stop() */
137 static void qdf_debugfs_seq_stop(struct seq_file *seq, void *v)
138 {
139 	qdf_mem_free(v);
140 }
141 
142 /* .seq_show() */
143 static int qdf_debugfs_seq_show(struct seq_file *seq, void *v)
144 {
145 	struct qdf_debugfs_seq_priv *priv = v;
146 	struct qdf_debugfs_fops *fops;
147 	QDF_STATUS status;
148 
149 	fops = seq->private;
150 
151 	if (fops && fops->show) {
152 		status = fops->show(seq, fops->priv);
153 
154 		if (priv && (status != QDF_STATUS_E_AGAIN))
155 			priv->stop = true;
156 	}
157 
158 	return 0;
159 }
160 
161 void qdf_debugfs_printf(qdf_debugfs_file_t file, const char *f, ...)
162 {
163 	va_list args;
164 
165 	va_start(args, f);
166 	seq_vprintf(file, f, args);
167 	va_end(args);
168 }
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)
174 {
175 	seq_hex_dump(file, "", DUMP_PREFIX_OFFSET, 16, 4, buf, len, false);
176 }
177 
178 #else
179 
180 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
181 			 qdf_size_t len)
182 {
183 	const size_t rowsize = 16;
184 	const size_t groupsize = 4;
185 	char *dst;
186 	size_t dstlen, readlen;
187 	int prefix = 0;
188 	size_t commitlen;
189 
190 	while (len > 0 && (file->size > file->count)) {
191 		seq_printf(file, "%.8x: ", prefix);
192 
193 		readlen = min(len, 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 		len = (len > rowsize) ? len - rowsize : 0;
202 		buf += readlen;
203 		prefix += rowsize;
204 	}
205 }
206 
207 #endif
208 
209 void qdf_debugfs_write(qdf_debugfs_file_t file, const uint8_t *buf,
210 		       qdf_size_t len)
211 {
212 	seq_write(file, buf, len);
213 }
214 
215 /* sequential file operation table */
216 static const struct seq_operations __qdf_debugfs_seq_ops = {
217 	.start = qdf_debugfs_seq_start,
218 	.next = qdf_debugfs_seq_next,
219 	.stop = qdf_debugfs_seq_stop,
220 	.show = qdf_debugfs_seq_show,
221 };
222 
223 /* .open() */
224 static int qdf_seq_open(struct inode *inode, struct file *file)
225 {
226 	void *private = inode->i_private;
227 	struct seq_file *seq;
228 	int rc;
229 
230 	/**
231 	 * Note: seq_open() will allocate a struct seq_file and store its
232 	 * pointer in @file->private_data. It warns if private_data is not NULL.
233 	 */
234 
235 	rc = seq_open(file, &__qdf_debugfs_seq_ops);
236 
237 	if (rc == 0) {
238 		seq = file->private_data;
239 		seq->private = private;
240 	}
241 
242 	return rc;
243 }
244 
245 /* .write() */
246 static ssize_t qdf_seq_write(struct file *filp, const char __user *ubuf,
247 			     size_t len, loff_t *ppos)
248 {
249 	struct qdf_debugfs_fops *fops;
250 	struct seq_file *seq;
251 	u8 *buf;
252 	ssize_t rc = 0;
253 
254 	if (len == 0)
255 		return 0;
256 
257 	seq = filp->private_data;
258 	fops = seq->private;
259 	if (fops && fops->write) {
260 		buf = qdf_mem_malloc(len + 1);
261 		if (buf) {
262 			buf[len] = '\0';
263 			rc = simple_write_to_buffer(buf, len, ppos, ubuf, len);
264 			fops->write(fops->priv, buf, len + 1);
265 			qdf_mem_free(buf);
266 		}
267 	}
268 
269 	return rc;
270 }
271 
272 /* debugfs file operation table */
273 static const struct file_operations __qdf_debugfs_fops = {
274 	.owner = THIS_MODULE,
275 	.open = qdf_seq_open,
276 	.read = seq_read,
277 	.llseek = seq_lseek,
278 	.release = seq_release,
279 	.write = qdf_seq_write,
280 };
281 
282 qdf_dentry_t qdf_debugfs_create_dir(const char *name, qdf_dentry_t parent)
283 {
284 	qdf_dentry_t dir;
285 
286 	if (!name)
287 		return NULL;
288 	if (!parent)
289 		parent = qdf_debugfs_get_root();
290 
291 	dir = debugfs_create_dir(name, parent);
292 
293 	if (IS_ERR_OR_NULL(dir)) {
294 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
295 			  "%s creation failed", name);
296 		dir = NULL;
297 	}
298 
299 	return dir;
300 }
301 qdf_export_symbol(qdf_debugfs_create_dir);
302 
303 qdf_dentry_t qdf_debugfs_create_file(const char *name, uint16_t mode,
304 				     qdf_dentry_t parent,
305 				     struct qdf_debugfs_fops *fops)
306 {
307 	qdf_dentry_t file;
308 	umode_t filemode;
309 
310 	if (!name || !fops)
311 		return NULL;
312 
313 	if (!parent)
314 		parent = qdf_debugfs_get_root();
315 
316 	filemode = qdf_debugfs_get_filemode(mode);
317 	file = debugfs_create_file(name, filemode, parent, fops,
318 				   &__qdf_debugfs_fops);
319 
320 	if (IS_ERR_OR_NULL(file)) {
321 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
322 			  "%s creation failed 0x%pK", name, file);
323 		file = NULL;
324 	}
325 
326 	return file;
327 }
328 qdf_export_symbol(qdf_debugfs_create_file);
329 
330 qdf_dentry_t qdf_debugfs_create_u8(const char *name, uint16_t mode,
331 				   qdf_dentry_t parent, u8 *value)
332 {
333 	umode_t filemode;
334 
335 	if (!name)
336 		return NULL;
337 
338 	if (!parent)
339 		parent = qdf_debugfs_get_root();
340 
341 	filemode = qdf_debugfs_get_filemode(mode);
342 	return debugfs_create_u8(name, filemode, parent, value);
343 }
344 
345 qdf_dentry_t qdf_debugfs_create_u16(const char *name, uint16_t mode,
346 				    qdf_dentry_t parent, u16 *value)
347 {
348 	umode_t filemode;
349 
350 	if (!name)
351 		return NULL;
352 
353 	if (!parent)
354 		parent = qdf_debugfs_get_root();
355 
356 	filemode = qdf_debugfs_get_filemode(mode);
357 	return debugfs_create_u16(name, filemode, parent, value);
358 }
359 qdf_export_symbol(qdf_debugfs_create_u16);
360 
361 qdf_dentry_t qdf_debugfs_create_u32(const char *name,
362 				    uint16_t mode,
363 				    qdf_dentry_t parent, u32 *value)
364 {
365 	umode_t filemode;
366 
367 	if (!name)
368 		return NULL;
369 
370 	if (!parent)
371 		parent = qdf_debugfs_get_root();
372 
373 	filemode = qdf_debugfs_get_filemode(mode);
374 	return debugfs_create_u32(name, filemode, parent, value);
375 }
376 qdf_export_symbol(qdf_debugfs_create_u32);
377 
378 qdf_dentry_t qdf_debugfs_create_u64(const char *name, uint16_t mode,
379 				    qdf_dentry_t parent, u64 *value)
380 {
381 	umode_t filemode;
382 
383 	if (!name)
384 		return NULL;
385 
386 	if (!parent)
387 		parent = qdf_debugfs_get_root();
388 
389 	filemode = qdf_debugfs_get_filemode(mode);
390 	return debugfs_create_u64(name, filemode, parent, value);
391 }
392 qdf_export_symbol(qdf_debugfs_create_u64);
393 
394 qdf_dentry_t qdf_debugfs_create_atomic(const char *name, uint16_t mode,
395 				       qdf_dentry_t parent, qdf_atomic_t *value)
396 {
397 	umode_t filemode;
398 
399 	if (!name)
400 		return NULL;
401 
402 	if (!parent)
403 		parent = qdf_debugfs_get_root();
404 
405 	filemode = qdf_debugfs_get_filemode(mode);
406 	return debugfs_create_atomic_t(name, filemode, parent, value);
407 }
408 qdf_export_symbol(qdf_debugfs_create_atomic);
409 
410 static int qdf_debugfs_string_show(struct seq_file *seq, void *pos)
411 {
412 	char *str = seq->private;
413 
414 	seq_puts(seq, str);
415 	seq_putc(seq, '\n');
416 
417 	return 0;
418 }
419 
420 static int qdf_debugfs_string_open(struct inode *inode, struct file *file)
421 {
422 	return single_open(file, qdf_debugfs_string_show, inode->i_private);
423 }
424 
425 static const struct file_operations qdf_string_fops = {
426 	.owner = THIS_MODULE,
427 	.open = qdf_debugfs_string_open,
428 	.read = seq_read,
429 	.llseek = seq_lseek,
430 	.release = single_release
431 };
432 
433 qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode,
434 				       qdf_dentry_t parent, char *str)
435 {
436 	umode_t filemode;
437 
438 	if (!name)
439 		return NULL;
440 
441 	if (!parent)
442 		parent = qdf_debugfs_get_root();
443 
444 	filemode = qdf_debugfs_get_filemode(mode);
445 	return debugfs_create_file(name, filemode, parent, str,
446 				   &qdf_string_fops);
447 }
448 qdf_export_symbol(qdf_debugfs_create_string);
449 
450 void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)
451 {
452 	debugfs_remove_recursive(d);
453 }
454 qdf_export_symbol(qdf_debugfs_remove_dir_recursive);
455 
456 void qdf_debugfs_remove_dir(qdf_dentry_t d)
457 {
458 	debugfs_remove(d);
459 }
460 qdf_export_symbol(qdf_debugfs_remove_dir);
461 
462 void qdf_debugfs_remove_file(qdf_dentry_t d)
463 {
464 	debugfs_remove(d);
465 }
466 qdf_export_symbol(qdf_debugfs_remove_file);
467