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