Browse Source

qcacmn: Add debugfs API definition

Implement debugfs APIs abstraction for Linux. If a particular operating
system is not supporting debugfs or similar feature, these API's will
be a no-op.

Change-Id: I51316012876fec258e546fce8fc2f877e4311343
CRs-Fixed: 2097140
Mahesh Kumar Kalikot Veetil 7 years ago
parent
commit
457b5a25c0
1 changed files with 390 additions and 5 deletions
  1. 390 5
      qdf/linux/src/qdf_debugfs.c

+ 390 - 5
qdf/linux/src/qdf_debugfs.c

@@ -23,20 +23,28 @@
 
 #include <qdf_debugfs.h>
 #include <i_qdf_debugfs.h>
+#include <qdf_mem.h>
+#include <qdf_trace.h>
+#include <qdf_module.h>
+
+/* A private structure definition to qdf sequence */
+struct qdf_debugfs_seq_priv {
+	bool stop;
+};
 
 /* entry for root debugfs directory*/
-static struct dentry *qdf_debugfs_root;
+static qdf_dentry_t qdf_debugfs_root;
 
 QDF_STATUS qdf_debugfs_init(void)
 {
-	qdf_debugfs_root = debugfs_create_dir(KBUILD_MODNAME"_qdf", NULL);
+	qdf_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
 
 	if (!qdf_debugfs_root)
 		return QDF_STATUS_E_FAILURE;
 
 	return QDF_STATUS_SUCCESS;
 }
-EXPORT_SYMBOL(qdf_debugfs_init);
+qdf_export_symbol(qdf_debugfs_init);
 
 QDF_STATUS qdf_debugfs_exit(void)
 {
@@ -45,9 +53,9 @@ QDF_STATUS qdf_debugfs_exit(void)
 
 	return QDF_STATUS_SUCCESS;
 }
-EXPORT_SYMBOL(qdf_debugfs_exit);
+qdf_export_symbol(qdf_debugfs_exit);
 
-struct dentry *qdf_debugfs_get_root(void)
+qdf_dentry_t qdf_debugfs_get_root(void)
 {
 	return qdf_debugfs_root;
 }
@@ -73,3 +81,380 @@ umode_t qdf_debugfs_get_filemode(uint16_t mode)
 
 	return ret;
 }
+
+/**
+ * ---------------------- Implementation note ---------------------------------
+ *
+ * A read in debugfs file triggers seq_read() which calls seq_read api. A
+ * sequence begins with the call of the function start(). If the return is a non
+ * NULL value, the function next() is called. This function is an iterator, the
+ * goal is to go though all the data. Each time next() is called, the function
+ * show() is also called. It writes data values in the buffer read by the user.
+ * The function next() is called until it returns NULL. The sequence ends when
+ * next() returns NULL, then the function stop() is called.
+ *
+ * NOTE: When a sequence is finished, another one starts. That means that
+ * at the end of function stop(), the function start() is called again. This
+ * loop finishes when the function start() returns NULL.
+ * ----------------------------------------------------------------------------
+ */
+
+/* .seq_start() */
+static void *qdf_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct qdf_debugfs_seq_priv *priv;
+
+	priv = qdf_mem_malloc(sizeof(*priv));
+	if (!priv)
+		return NULL;
+
+	priv->stop = false;
+
+	return priv;
+}
+
+/* .seq_next() */
+static void *qdf_debugfs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct qdf_debugfs_seq_priv *priv = v;
+
+	if (priv)
+		++*pos;
+
+	if (priv && priv->stop) {
+		qdf_mem_free(priv);
+		priv = NULL;
+	}
+
+	return priv;
+}
+
+/* .seq_stop() */
+static void qdf_debugfs_seq_stop(struct seq_file *seq, void *v)
+{
+	qdf_mem_free(v);
+}
+
+/* .seq_show() */
+static int qdf_debugfs_seq_show(struct seq_file *seq, void *v)
+{
+	struct qdf_debugfs_seq_priv *priv = v;
+	struct qdf_debugfs_fops *fops;
+	QDF_STATUS status;
+
+	fops = seq->private;
+
+	if (fops && fops->show) {
+		status = fops->show(seq, fops->priv);
+
+		if (priv && (status != QDF_STATUS_E_AGAIN))
+			priv->stop = true;
+	}
+
+	return 0;
+}
+
+void qdf_debugfs_printf(qdf_debugfs_file_t file, const char *f, ...)
+{
+	va_list args;
+
+	va_start(args, f);
+	seq_vprintf(file, f, args);
+	va_end(args);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
+
+void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
+			 qdf_size_t len)
+{
+	seq_hex_dump(file, "", DUMP_PREFIX_OFFSET, 16, 4, buf, len, false);
+}
+
+#else
+
+void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf,
+			 qdf_size_t len)
+{
+	const size_t rowsize = 16;
+	const size_t groupsize = 4;
+	char *dst;
+	size_t dstlen, readlen;
+	int prefix = 0;
+	size_t commitlen;
+
+	while (len > 0 && (file->size > file->count)) {
+		seq_printf(file, "%.8x: ", prefix);
+
+		readlen = min(len, rowsize);
+		dstlen = seq_get_buf(file, &dst);
+		hex_dump_to_buffer(buf, readlen, rowsize, groupsize, dst,
+				   dstlen, false);
+		commitlen = strnlen(dst, dstlen);
+		seq_commit(file, commitlen);
+		seq_putc(file, '\n');
+
+		len = (len > rowsize) ? len - rowsize : 0;
+		buf += readlen;
+		prefix += rowsize;
+	}
+}
+
+#endif
+
+void qdf_debugfs_write(qdf_debugfs_file_t file, const uint8_t *buf,
+		       qdf_size_t len)
+{
+	seq_write(file, buf, len);
+}
+
+/* sequential file operation table */
+static const struct seq_operations __qdf_debugfs_seq_ops = {
+	.start = qdf_debugfs_seq_start,
+	.next = qdf_debugfs_seq_next,
+	.stop = qdf_debugfs_seq_stop,
+	.show = qdf_debugfs_seq_show,
+};
+
+/* .open() */
+static int qdf_seq_open(struct inode *inode, struct file *file)
+{
+	void *private = inode->i_private;
+	struct seq_file *seq;
+	int rc;
+
+	/**
+	 * Note: seq_open() will allocate a struct seq_file and store its
+	 * pointer in @file->private_data. It warns if private_data is not NULL.
+	 */
+
+	rc = seq_open(file, &__qdf_debugfs_seq_ops);
+
+	if (rc == 0) {
+		seq = file->private_data;
+		seq->private = private;
+	}
+
+	return rc;
+}
+
+/* .write() */
+static ssize_t qdf_seq_write(struct file *filp, const char __user *ubuf,
+			     size_t len, loff_t *ppos)
+{
+	struct qdf_debugfs_fops *fops;
+	struct seq_file *seq;
+	u8 *buf;
+	ssize_t rc = 0;
+
+	if (len == 0)
+		return 0;
+
+	seq = filp->private_data;
+	fops = seq->private;
+	if (fops && fops->write) {
+		buf = qdf_mem_malloc(len + 1);
+		buf[len] = '\0';
+		rc = simple_write_to_buffer(buf, len, ppos, ubuf, len);
+		fops->write(fops->priv, buf, len + 1);
+		qdf_mem_free(buf);
+	}
+
+	return rc;
+}
+
+/* debugfs file operation table */
+static const struct file_operations __qdf_debugfs_fops = {
+	.owner = THIS_MODULE,
+	.open = qdf_seq_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+	.write = qdf_seq_write,
+};
+
+qdf_dentry_t qdf_debugfs_create_dir(const char *name, qdf_dentry_t parent)
+{
+	qdf_dentry_t dir;
+
+	if (!name)
+		return NULL;
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	dir = debugfs_create_dir(name, parent);
+
+	if (IS_ERR_OR_NULL(dir)) {
+		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+			  "%s creation failed", name);
+		dir = NULL;
+	}
+
+	return dir;
+}
+qdf_export_symbol(qdf_debugfs_create_dir);
+
+qdf_dentry_t qdf_debugfs_create_file(const char *name, uint16_t mode,
+				     qdf_dentry_t parent,
+				     struct qdf_debugfs_fops *fops)
+{
+	qdf_dentry_t file;
+	umode_t filemode;
+
+	if (!name || !fops)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	file = debugfs_create_file(name, filemode, parent, fops,
+				   &__qdf_debugfs_fops);
+
+	if (IS_ERR_OR_NULL(file)) {
+		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+			  "%s creation failed %p", name, file);
+		file = NULL;
+	}
+
+	return file;
+}
+qdf_export_symbol(qdf_debugfs_create_file);
+
+qdf_dentry_t qdf_debugfs_create_u8(const char *name, uint16_t mode,
+				   qdf_dentry_t parent, u8 *value)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_u8(name, filemode, parent, value);
+}
+
+qdf_dentry_t qdf_debugfs_create_u16(const char *name, uint16_t mode,
+				    qdf_dentry_t parent, u16 *value)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_u16(name, filemode, parent, value);
+}
+qdf_export_symbol(qdf_debugfs_create_u16);
+
+qdf_dentry_t qdf_debugfs_create_u32(const char *name,
+				    uint16_t mode,
+				    qdf_dentry_t parent, u32 *value)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_u32(name, filemode, parent, value);
+}
+qdf_export_symbol(qdf_debugfs_create_u32);
+
+qdf_dentry_t qdf_debugfs_create_u64(const char *name, uint16_t mode,
+				    qdf_dentry_t parent, u64 *value)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_u64(name, filemode, parent, value);
+}
+qdf_export_symbol(qdf_debugfs_create_u64);
+
+qdf_dentry_t qdf_debugfs_create_atomic(const char *name, uint16_t mode,
+				       qdf_dentry_t parent, qdf_atomic_t *value)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_atomic_t(name, filemode, parent, value);
+}
+qdf_export_symbol(qdf_debugfs_create_atomic);
+
+static int qdf_debugfs_string_show(struct seq_file *seq, void *pos)
+{
+	char *str = seq->private;
+
+	seq_puts(seq, str);
+	seq_putc(seq, '\n');
+
+	return 0;
+}
+
+static int qdf_debugfs_string_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, qdf_debugfs_string_show, inode->i_private);
+}
+
+static const struct file_operations qdf_string_fops = {
+	.owner = THIS_MODULE,
+	.open = qdf_debugfs_string_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release
+};
+
+qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode,
+				       qdf_dentry_t parent, char *str)
+{
+	umode_t filemode;
+
+	if (!name)
+		return NULL;
+
+	if (!parent)
+		parent = qdf_debugfs_get_root();
+
+	filemode = qdf_debugfs_get_filemode(mode);
+	return debugfs_create_file(name, filemode, parent, str,
+				   &qdf_string_fops);
+}
+qdf_export_symbol(qdf_debugfs_create_string);
+
+void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d)
+{
+	debugfs_remove_recursive(d);
+}
+qdf_export_symbol(qdf_debugfs_remove_dir_recursive);
+
+void qdf_debugfs_remove_dir(qdf_dentry_t d)
+{
+	debugfs_remove(d);
+}
+qdf_export_symbol(qdf_debugfs_remove_dir);
+
+void qdf_debugfs_remove_file(qdf_dentry_t d)
+{
+	debugfs_remove(d);
+}
+qdf_export_symbol(qdf_debugfs_remove_file);