blob: ee3672ed2e9c3eb4f3c693fc8425f1e115f34437 [file] [log] [blame]
Karthikeyan Ramasubramaniane1f4f732011-08-08 13:34:47 -06001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
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
34static DEFINE_MUTEX(ipc_log_debugfs_init_lock);
35static struct dentry *root_dent;
36#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
37
38static 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
53static 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;
65 spin_lock_irqsave(&ipc_log_context_list_lock, flags);
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);
73 spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
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);
79 spin_lock_irqsave(&ipc_log_context_list_lock, flags);
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);
85 spin_unlock_irqrestore(&ipc_log_context_list_lock, flags);
86 return size - dctxt.size;
87}
88
89static 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 */
125static 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
148static 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
154static 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
160static int debug_open(struct inode *inode, struct file *file)
161{
162 file->private_data = inode->i_private;
163 return 0;
164}
165
166static const struct file_operations debug_ops = {
167 .read = debug_read,
168 .open = debug_open,
169};
170
171static const struct file_operations debug_ops_cont = {
172 .read = debug_read_cont,
173 .open = debug_open,
174};
175
176static 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
184static 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 Holmberg06ed50a2012-08-07 13:23:31 -0600189
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 Ramasubramaniane1f4f732011-08-08 13:34:47 -0600198}
199
200void 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}
214EXPORT_SYMBOL(check_and_create_debugfs);
215
216void 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}
234EXPORT_SYMBOL(create_ctx_debugfs);