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 @@ -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 +#include +#include +#include +#include + +#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_ */