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