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