1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved.
4 * Copyright (c) 2010-2012 Broadcom. All rights reserved.
5 */
6
7 #include <linux/debugfs.h>
8 #include "vchiq_core.h"
9 #include "vchiq_arm.h"
10 #include "vchiq_debugfs.h"
11
12 #ifdef CONFIG_DEBUG_FS
13
14 #define DEBUGFS_WRITE_BUF_SIZE 256
15
16 #define VCHIQ_LOG_ERROR_STR "error"
17 #define VCHIQ_LOG_WARNING_STR "warning"
18 #define VCHIQ_LOG_INFO_STR "info"
19 #define VCHIQ_LOG_TRACE_STR "trace"
20
21 /* Global 'vchiq' debugfs and clients entry used by all instances */
22 static struct dentry *vchiq_dbg_dir;
23 static struct dentry *vchiq_dbg_clients;
24
25 /* Log category debugfs entries */
26 struct vchiq_debugfs_log_entry {
27 const char *name;
28 void *plevel;
29 };
30
31 static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = {
32 { "core", &vchiq_core_log_level },
33 { "msg", &vchiq_core_msg_log_level },
34 { "sync", &vchiq_sync_log_level },
35 { "susp", &vchiq_susp_log_level },
36 { "arm", &vchiq_arm_log_level },
37 };
38
debugfs_log_show(struct seq_file * f,void * offset)39 static int debugfs_log_show(struct seq_file *f, void *offset)
40 {
41 int *levp = f->private;
42 char *log_value = NULL;
43
44 switch (*levp) {
45 case VCHIQ_LOG_ERROR:
46 log_value = VCHIQ_LOG_ERROR_STR;
47 break;
48 case VCHIQ_LOG_WARNING:
49 log_value = VCHIQ_LOG_WARNING_STR;
50 break;
51 case VCHIQ_LOG_INFO:
52 log_value = VCHIQ_LOG_INFO_STR;
53 break;
54 case VCHIQ_LOG_TRACE:
55 log_value = VCHIQ_LOG_TRACE_STR;
56 break;
57 default:
58 break;
59 }
60
61 seq_printf(f, "%s\n", log_value ? log_value : "(null)");
62
63 return 0;
64 }
65
debugfs_log_open(struct inode * inode,struct file * file)66 static int debugfs_log_open(struct inode *inode, struct file *file)
67 {
68 return single_open(file, debugfs_log_show, inode->i_private);
69 }
70
debugfs_log_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)71 static ssize_t debugfs_log_write(struct file *file,
72 const char __user *buffer,
73 size_t count, loff_t *ppos)
74 {
75 struct seq_file *f = (struct seq_file *)file->private_data;
76 int *levp = f->private;
77 char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1];
78
79 memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1);
80 if (count >= DEBUGFS_WRITE_BUF_SIZE)
81 count = DEBUGFS_WRITE_BUF_SIZE;
82
83 if (copy_from_user(kbuf, buffer, count))
84 return -EFAULT;
85 kbuf[count - 1] = 0;
86
87 if (strncmp("error", kbuf, strlen("error")) == 0)
88 *levp = VCHIQ_LOG_ERROR;
89 else if (strncmp("warning", kbuf, strlen("warning")) == 0)
90 *levp = VCHIQ_LOG_WARNING;
91 else if (strncmp("info", kbuf, strlen("info")) == 0)
92 *levp = VCHIQ_LOG_INFO;
93 else if (strncmp("trace", kbuf, strlen("trace")) == 0)
94 *levp = VCHIQ_LOG_TRACE;
95 else
96 *levp = VCHIQ_LOG_DEFAULT;
97
98 *ppos += count;
99
100 return count;
101 }
102
103 static const struct file_operations debugfs_log_fops = {
104 .owner = THIS_MODULE,
105 .open = debugfs_log_open,
106 .write = debugfs_log_write,
107 .read = seq_read,
108 .llseek = seq_lseek,
109 .release = single_release,
110 };
111
debugfs_usecount_show(struct seq_file * f,void * offset)112 static int debugfs_usecount_show(struct seq_file *f, void *offset)
113 {
114 struct vchiq_instance *instance = f->private;
115 int use_count;
116
117 use_count = vchiq_instance_get_use_count(instance);
118 seq_printf(f, "%d\n", use_count);
119
120 return 0;
121 }
122 DEFINE_SHOW_ATTRIBUTE(debugfs_usecount);
123
debugfs_trace_show(struct seq_file * f,void * offset)124 static int debugfs_trace_show(struct seq_file *f, void *offset)
125 {
126 struct vchiq_instance *instance = f->private;
127 int trace;
128
129 trace = vchiq_instance_get_trace(instance);
130 seq_printf(f, "%s\n", trace ? "Y" : "N");
131
132 return 0;
133 }
134
debugfs_trace_open(struct inode * inode,struct file * file)135 static int debugfs_trace_open(struct inode *inode, struct file *file)
136 {
137 return single_open(file, debugfs_trace_show, inode->i_private);
138 }
139
debugfs_trace_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)140 static ssize_t debugfs_trace_write(struct file *file,
141 const char __user *buffer,
142 size_t count, loff_t *ppos)
143 {
144 struct seq_file *f = (struct seq_file *)file->private_data;
145 struct vchiq_instance *instance = f->private;
146 char firstchar;
147
148 if (copy_from_user(&firstchar, buffer, 1))
149 return -EFAULT;
150
151 switch (firstchar) {
152 case 'Y':
153 case 'y':
154 case '1':
155 vchiq_instance_set_trace(instance, 1);
156 break;
157 case 'N':
158 case 'n':
159 case '0':
160 vchiq_instance_set_trace(instance, 0);
161 break;
162 default:
163 break;
164 }
165
166 *ppos += count;
167
168 return count;
169 }
170
171 static const struct file_operations debugfs_trace_fops = {
172 .owner = THIS_MODULE,
173 .open = debugfs_trace_open,
174 .write = debugfs_trace_write,
175 .read = seq_read,
176 .llseek = seq_lseek,
177 .release = single_release,
178 };
179
180 /* add an instance (process) to the debugfs entries */
vchiq_debugfs_add_instance(struct vchiq_instance * instance)181 void vchiq_debugfs_add_instance(struct vchiq_instance *instance)
182 {
183 char pidstr[16];
184 struct dentry *top;
185
186 snprintf(pidstr, sizeof(pidstr), "%d",
187 vchiq_instance_get_pid(instance));
188
189 top = debugfs_create_dir(pidstr, vchiq_dbg_clients);
190
191 debugfs_create_file("use_count", 0444, top, instance,
192 &debugfs_usecount_fops);
193 debugfs_create_file("trace", 0644, top, instance, &debugfs_trace_fops);
194
195 vchiq_instance_get_debugfs_node(instance)->dentry = top;
196 }
197
vchiq_debugfs_remove_instance(struct vchiq_instance * instance)198 void vchiq_debugfs_remove_instance(struct vchiq_instance *instance)
199 {
200 struct vchiq_debugfs_node *node =
201 vchiq_instance_get_debugfs_node(instance);
202
203 debugfs_remove_recursive(node->dentry);
204 }
205
vchiq_debugfs_init(void)206 void vchiq_debugfs_init(void)
207 {
208 struct dentry *dir;
209 int i;
210
211 vchiq_dbg_dir = debugfs_create_dir("vchiq", NULL);
212 vchiq_dbg_clients = debugfs_create_dir("clients", vchiq_dbg_dir);
213
214 /* create an entry under <debugfs>/vchiq/log for each log category */
215 dir = debugfs_create_dir("log", vchiq_dbg_dir);
216
217 for (i = 0; i < ARRAY_SIZE(vchiq_debugfs_log_entries); i++)
218 debugfs_create_file(vchiq_debugfs_log_entries[i].name, 0644,
219 dir, vchiq_debugfs_log_entries[i].plevel,
220 &debugfs_log_fops);
221 }
222
223 /* remove all the debugfs entries */
vchiq_debugfs_deinit(void)224 void vchiq_debugfs_deinit(void)
225 {
226 debugfs_remove_recursive(vchiq_dbg_dir);
227 }
228
229 #else /* CONFIG_DEBUG_FS */
230
vchiq_debugfs_init(void)231 void vchiq_debugfs_init(void)
232 {
233 }
234
vchiq_debugfs_deinit(void)235 void vchiq_debugfs_deinit(void)
236 {
237 }
238
vchiq_debugfs_add_instance(struct vchiq_instance * instance)239 void vchiq_debugfs_add_instance(struct vchiq_instance *instance)
240 {
241 }
242
vchiq_debugfs_remove_instance(struct vchiq_instance * instance)243 void vchiq_debugfs_remove_instance(struct vchiq_instance *instance)
244 {
245 }
246
247 #endif /* CONFIG_DEBUG_FS */
248