Arun Kumar Neelakantam | cb1ee8d | 2013-05-10 13:05:37 +0530 | [diff] [blame] | 1 | /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | #include <linux/slab.h> |
| 15 | #include <linux/uaccess.h> |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/fs.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <linux/errno.h> |
| 20 | #include <linux/jiffies.h> |
| 21 | #include <linux/debugfs.h> |
| 22 | #include <linux/io.h> |
| 23 | #include <linux/idr.h> |
| 24 | #include <linux/string.h> |
| 25 | #include <linux/sched.h> |
| 26 | #include <linux/wait.h> |
| 27 | #include <linux/delay.h> |
| 28 | #include <linux/completion.h> |
| 29 | |
| 30 | #include <mach/msm_ipc_logging.h> |
| 31 | |
| 32 | #include "ipc_logging.h" |
| 33 | |
| 34 | static DEFINE_MUTEX(ipc_log_debugfs_init_lock); |
| 35 | static struct dentry *root_dent; |
| 36 | #define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4) |
| 37 | |
| 38 | static void *get_deserialization_func(struct ipc_log_context *ilctxt, |
| 39 | int type) |
| 40 | { |
| 41 | struct dfunc_info *df_info = NULL; |
| 42 | |
| 43 | if (!ilctxt) |
| 44 | return NULL; |
| 45 | |
| 46 | list_for_each_entry(df_info, &ilctxt->dfunc_info_list, list) { |
| 47 | if (df_info->type == type) |
| 48 | return df_info->dfunc; |
| 49 | } |
| 50 | return NULL; |
| 51 | } |
| 52 | |
| 53 | static int deserialize_log(struct ipc_log_context *ilctxt, |
| 54 | char *buff, int size) |
| 55 | { |
| 56 | struct encode_context ectxt; |
| 57 | struct decode_context dctxt; |
| 58 | void (*deserialize_func)(struct encode_context *ectxt, |
| 59 | struct decode_context *dctxt); |
| 60 | unsigned long flags; |
| 61 | |
| 62 | dctxt.output_format = OUTPUT_DEBUGFS; |
| 63 | dctxt.buff = buff; |
| 64 | dctxt.size = size; |
Arun Kumar Neelakantam | cb1ee8d | 2013-05-10 13:05:37 +0530 | [diff] [blame] | 65 | read_lock_irqsave(&ipc_log_context_list_lock, flags); |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 66 | spin_lock(&ilctxt->ipc_log_context_lock); |
| 67 | while (dctxt.size >= MAX_MSG_DECODED_SIZE && |
| 68 | !is_ilctxt_empty(ilctxt)) { |
| 69 | msg_read(ilctxt, &ectxt); |
| 70 | deserialize_func = get_deserialization_func(ilctxt, |
| 71 | ectxt.hdr.type); |
| 72 | spin_unlock(&ilctxt->ipc_log_context_lock); |
Arun Kumar Neelakantam | cb1ee8d | 2013-05-10 13:05:37 +0530 | [diff] [blame] | 73 | read_unlock_irqrestore(&ipc_log_context_list_lock, flags); |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 74 | if (deserialize_func) |
| 75 | deserialize_func(&ectxt, &dctxt); |
| 76 | else |
| 77 | pr_err("%s: unknown message 0x%x\n", |
| 78 | __func__, ectxt.hdr.type); |
Arun Kumar Neelakantam | cb1ee8d | 2013-05-10 13:05:37 +0530 | [diff] [blame] | 79 | read_lock_irqsave(&ipc_log_context_list_lock, flags); |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 80 | spin_lock(&ilctxt->ipc_log_context_lock); |
| 81 | } |
| 82 | if ((size - dctxt.size) == 0) |
| 83 | init_completion(&ilctxt->read_avail); |
| 84 | spin_unlock(&ilctxt->ipc_log_context_lock); |
Arun Kumar Neelakantam | cb1ee8d | 2013-05-10 13:05:37 +0530 | [diff] [blame] | 85 | read_unlock_irqrestore(&ipc_log_context_list_lock, flags); |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 86 | return size - dctxt.size; |
| 87 | } |
| 88 | |
| 89 | static int debug_log(struct ipc_log_context *ilctxt, |
| 90 | char *buff, int size, int cont) |
| 91 | { |
| 92 | int i = 0; |
| 93 | |
| 94 | if (size < MAX_MSG_DECODED_SIZE) { |
| 95 | pr_err("%s: buffer size %d < %d\n", __func__, size, |
| 96 | MAX_MSG_DECODED_SIZE); |
| 97 | return -ENOMEM; |
| 98 | } |
| 99 | do { |
| 100 | i = deserialize_log(ilctxt, buff, size - 1); |
| 101 | if (cont && i == 0) { |
| 102 | wait_for_completion_interruptible(&ilctxt->read_avail); |
| 103 | if (signal_pending(current)) |
| 104 | break; |
| 105 | } |
| 106 | } while (cont && i == 0); |
| 107 | |
| 108 | return i; |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | * VFS Read operation helper which dispatches the call to the debugfs |
| 113 | * read command stored in file->private_data. |
| 114 | * |
| 115 | * @file File structure |
| 116 | * @buff user buffer |
| 117 | * @count size of user buffer |
| 118 | * @ppos file position to read from (only a value of 0 is accepted) |
| 119 | * @cont 1 = continuous mode (don't return 0 to signal end-of-file) |
| 120 | * |
| 121 | * @returns ==0 end of file |
| 122 | * >0 number of bytes read |
| 123 | * <0 error |
| 124 | */ |
| 125 | static ssize_t debug_read_helper(struct file *file, char __user *buff, |
| 126 | size_t count, loff_t *ppos, int cont) |
| 127 | { |
| 128 | struct ipc_log_context *ilctxt = file->private_data; |
| 129 | char *buffer; |
| 130 | int bsize; |
| 131 | |
| 132 | buffer = kmalloc(count, GFP_KERNEL); |
| 133 | if (!buffer) |
| 134 | return -ENOMEM; |
| 135 | |
| 136 | bsize = debug_log(ilctxt, buffer, count, cont); |
| 137 | if (bsize > 0) { |
| 138 | if (copy_to_user(buff, buffer, bsize)) { |
| 139 | kfree(buffer); |
| 140 | return -EFAULT; |
| 141 | } |
| 142 | *ppos += bsize; |
| 143 | } |
| 144 | kfree(buffer); |
| 145 | return bsize; |
| 146 | } |
| 147 | |
| 148 | static ssize_t debug_read(struct file *file, char __user *buff, |
| 149 | size_t count, loff_t *ppos) |
| 150 | { |
| 151 | return debug_read_helper(file, buff, count, ppos, 0); |
| 152 | } |
| 153 | |
| 154 | static ssize_t debug_read_cont(struct file *file, char __user *buff, |
| 155 | size_t count, loff_t *ppos) |
| 156 | { |
| 157 | return debug_read_helper(file, buff, count, ppos, 1); |
| 158 | } |
| 159 | |
| 160 | static int debug_open(struct inode *inode, struct file *file) |
| 161 | { |
| 162 | file->private_data = inode->i_private; |
| 163 | return 0; |
| 164 | } |
| 165 | |
| 166 | static const struct file_operations debug_ops = { |
| 167 | .read = debug_read, |
| 168 | .open = debug_open, |
| 169 | }; |
| 170 | |
| 171 | static const struct file_operations debug_ops_cont = { |
| 172 | .read = debug_read_cont, |
| 173 | .open = debug_open, |
| 174 | }; |
| 175 | |
| 176 | static void debug_create(const char *name, mode_t mode, |
| 177 | struct dentry *dent, |
| 178 | struct ipc_log_context *ilctxt, |
| 179 | const struct file_operations *fops) |
| 180 | { |
| 181 | debugfs_create_file(name, mode, dent, ilctxt, fops); |
| 182 | } |
| 183 | |
| 184 | static void dfunc_string(struct encode_context *ectxt, |
| 185 | struct decode_context *dctxt) |
| 186 | { |
| 187 | tsv_timestamp_read(ectxt, dctxt, " "); |
| 188 | tsv_byte_array_read(ectxt, dctxt, ""); |
Eric Holmberg | 06ed50a | 2012-08-07 13:23:31 -0600 | [diff] [blame] | 189 | |
| 190 | /* add trailing \n if necessary */ |
| 191 | if (*(dctxt->buff - 1) != '\n') { |
| 192 | if (dctxt->size) { |
| 193 | ++dctxt->buff; |
| 194 | --dctxt->size; |
| 195 | } |
| 196 | *(dctxt->buff - 1) = '\n'; |
| 197 | } |
Karthikeyan Ramasubramanian | e1f4f73 | 2011-08-08 13:34:47 -0600 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | void check_and_create_debugfs(void) |
| 201 | { |
| 202 | mutex_lock(&ipc_log_debugfs_init_lock); |
| 203 | if (!root_dent) { |
| 204 | root_dent = debugfs_create_dir("ipc_logging", 0); |
| 205 | |
| 206 | if (IS_ERR(root_dent)) { |
| 207 | pr_err("%s: unable to create debugfs %ld\n", |
| 208 | __func__, IS_ERR(root_dent)); |
| 209 | root_dent = NULL; |
| 210 | } |
| 211 | } |
| 212 | mutex_unlock(&ipc_log_debugfs_init_lock); |
| 213 | } |
| 214 | EXPORT_SYMBOL(check_and_create_debugfs); |
| 215 | |
| 216 | void create_ctx_debugfs(struct ipc_log_context *ctxt, |
| 217 | const char *mod_name) |
| 218 | { |
| 219 | if (!root_dent) |
| 220 | check_and_create_debugfs(); |
| 221 | |
| 222 | if (root_dent) { |
| 223 | ctxt->dent = debugfs_create_dir(mod_name, root_dent); |
| 224 | if (!IS_ERR(ctxt->dent)) { |
| 225 | debug_create("log", 0444, ctxt->dent, |
| 226 | ctxt, &debug_ops); |
| 227 | debug_create("log_cont", 0444, ctxt->dent, |
| 228 | ctxt, &debug_ops_cont); |
| 229 | } |
| 230 | } |
| 231 | add_deserialization_func((void *)ctxt, |
| 232 | TSV_TYPE_STRING, dfunc_string); |
| 233 | } |
| 234 | EXPORT_SYMBOL(create_ctx_debugfs); |