diff options
Diffstat (limited to 'drivers/usb/gadget/debug.c')
-rw-r--r-- | drivers/usb/gadget/debug.c | 133 |
1 files changed, 133 insertions, 0 deletions
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; +} |