summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/debug.c133
-rw-r--r--drivers/usb/gadget/debug.h55
4 files changed, 193 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 598a67d6ba05..32962896cf68 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -10,3 +10,4 @@ libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y += composite.o functions.o configfs.o u_f.o
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
+obj-y += debug.o
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index b27ce0747c18..3639890c0dc7 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -8,6 +8,7 @@
#include "configfs.h"
#include "u_f.h"
#include "u_os_desc.h"
+#include "debug.h"
#ifdef CONFIG_USB_CONFIGFS_UEVENT
#include <linux/platform_device.h>
@@ -1747,6 +1748,8 @@ static int __init gadget_cfs_init(void)
config_group_init(&gadget_subsys.su_group);
+ debug_debugfs_init();
+
ret = configfs_register_subsystem(&gadget_subsys);
#ifdef CONFIG_USB_CONFIGFS_UEVENT
@@ -1761,6 +1764,7 @@ module_init(gadget_cfs_init);
static void __exit gadget_cfs_exit(void)
{
+ debug_debugfs_exit();
configfs_unregister_subsystem(&gadget_subsys);
#ifdef CONFIG_USB_CONFIGFS_UEVENT
if (!IS_ERR(android_class))
diff --git a/drivers/usb/gadget/debug.c b/drivers/usb/gadget/debug.c
new file mode 100644
index 000000000000..32a53299446c
--- /dev/null
+++ b/drivers/usb/gadget/debug.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/rwlock.h>
+#include <linux/debugfs.h>
+
+#include "debug.h"
+
+#define dbg_inc(i) ((i+1) % DBG_MAX_MSG)
+
+#define ENABLE_EVENT_LOG 1
+unsigned int enable_event_log = ENABLE_EVENT_LOG;
+module_param(enable_event_log, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_event_log, "enable event logging in debug buffer");
+
+static struct {
+ char buf[DBG_MAX_MSG][DBG_MSG_LEN]; /* buffer */
+ unsigned idx; /* index */
+ rwlock_t lck; /* lock */
+ struct dentry *root;
+} __maybe_unused dbg_buffer = {
+ .idx = 0,
+ .lck = __RW_LOCK_UNLOCKED(lck),
+ .root = NULL
+};
+
+void __maybe_unused put_timestamp(char *tbuf)
+{
+ unsigned long long t;
+ unsigned long nanosec_rem;
+ unsigned long flags;
+
+ write_lock_irqsave(&dbg_buffer.lck, flags);
+ t = cpu_clock(smp_processor_id());
+ write_unlock_irqrestore(&dbg_buffer.lck, flags);
+ nanosec_rem = do_div(t, 1000000000)/1000;
+ snprintf(tbuf, TIME_BUF_LEN, "[%5lu.%06lu]: ", (unsigned long)t,
+ nanosec_rem);
+}
+
+void __maybe_unused add_event_to_buf(char *tbuf)
+{
+ unsigned long flags;
+ char *buf;
+ write_lock_irqsave(&dbg_buffer.lck, flags);
+ buf = dbg_buffer.buf[dbg_buffer.idx];
+ memcpy(buf, tbuf, DBG_MSG_LEN);
+ dbg_buffer.idx = (dbg_buffer.idx + 1) % DBG_MAX_MSG;
+ write_unlock_irqrestore(&dbg_buffer.lck, flags);
+}
+
+static int dbg_read_buf_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ unsigned i;
+
+ read_lock_irqsave(&dbg_buffer.lck, flags);
+
+ i = dbg_buffer.idx;
+ if (strnlen(dbg_buffer.buf[i], DBG_MSG_LEN))
+ seq_printf(s, "%s\n", dbg_buffer.buf[i]);
+ for (i = dbg_inc(i); i != dbg_buffer.idx; i = dbg_inc(i)) {
+ if (!strnlen(dbg_buffer.buf[i], DBG_MSG_LEN))
+ continue;
+ seq_printf(s, "%s\n", dbg_buffer.buf[i]);
+ }
+
+ read_unlock_irqrestore(&dbg_buffer.lck, flags);
+
+ return 0;
+}
+
+static int dbg_read_buf_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_read_buf_show, inode->i_private);
+}
+
+const struct file_operations dbg_read_buf_fops = {
+ .open = dbg_read_buf_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int debug_debugfs_init(void)
+{
+ struct dentry *root;
+ struct dentry *file;
+ int ret;
+
+ root = debugfs_create_dir("debug", NULL);
+ if (!root) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ dbg_buffer.root = root;
+
+ file = debugfs_create_file("read_buf", S_IRUGO, root,
+ NULL, &dbg_read_buf_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ debugfs_remove_recursive(root);
+
+err0:
+ return ret;
+}
+
+void debug_debugfs_exit(void)
+{
+ debugfs_remove_recursive(dbg_buffer.root);
+ dbg_buffer.root = NULL;
+}
diff --git a/drivers/usb/gadget/debug.h b/drivers/usb/gadget/debug.h
new file mode 100644
index 000000000000..8729aca0a69e
--- /dev/null
+++ b/drivers/usb/gadget/debug.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DEBUG_H_
+#define __DEBUG_H_
+
+#define DBG_MAX_MSG 1024UL
+#define DBG_MSG_LEN 80UL
+#define TIME_BUF_LEN 17
+#define DBG_EVENT_LEN (DBG_MSG_LEN - TIME_BUF_LEN)
+
+extern unsigned int enable_event_log;
+extern void put_timestamp(char *tbuf);
+extern void add_event_to_buf(char *tbuf);
+extern int debug_debugfs_init(void);
+extern void debug_debugfs_exit(void);
+
+#define LOGLEVEL_NONE 8
+#define LOGLEVEL_DEBUG 7
+#define LOGLEVEL_INFO 6
+#define LOGLEVEL_ERR 3
+
+#define log_event(log_level, x...) \
+do { \
+ char buf[DBG_MSG_LEN]; \
+ if (log_level == LOGLEVEL_DEBUG) \
+ pr_debug(x); \
+ else if (log_level == LOGLEVEL_ERR) \
+ pr_err(x); \
+ else if (log_level == LOGLEVEL_INFO) \
+ pr_info(x); \
+ if (enable_event_log) { \
+ put_timestamp(buf); \
+ snprintf(&buf[TIME_BUF_LEN - 1], DBG_EVENT_LEN, x); \
+ add_event_to_buf(buf); \
+ } \
+} while (0)
+
+#define log_event_none(x, ...) log_event(LOGLEVEL_NONE, x, ##__VA_ARGS__)
+#define log_event_dbg(x, ...) log_event(LOGLEVEL_DEBUG, x, ##__VA_ARGS__)
+#define log_event_err(x, ...) log_event(LOGLEVEL_ERR, x, ##__VA_ARGS__)
+#define log_event_info(x, ...) log_event(LOGLEVEL_INFO, x, ##__VA_ARGS__)
+
+#endif /* __DEBUG_H_ */