1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Ultra Wide Band
4  * Debug support
5  *
6  * Copyright (C) 2005-2006 Intel Corporation
7  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
8  * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
9  *
10  * FIXME: doc
11  */
12 
13 #include <linux/spinlock.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/notifier.h>
17 #include <linux/device.h>
18 #include <linux/debugfs.h>
19 #include <linux/uaccess.h>
20 #include <linux/seq_file.h>
21 
22 #include "include/debug-cmd.h"
23 #include "uwb-internal.h"
24 
25 /*
26  * Debug interface
27  *
28  * Per radio controller debugfs files (in uwb/uwbN/):
29  *
30  * command: Flexible command interface (see <linux/uwb/debug-cmd.h>).
31  *
32  * reservations: information on reservations.
33  *
34  * accept: Set to true (Y or 1) to accept reservation requests from
35  * peers.
36  *
37  * drp_avail: DRP availability information.
38  */
39 
40 struct uwb_dbg {
41 	struct uwb_pal pal;
42 
43 	bool accept;
44 	struct list_head rsvs;
45 
46 	struct dentry *root_d;
47 	struct dentry *command_f;
48 	struct dentry *reservations_f;
49 	struct dentry *accept_f;
50 	struct dentry *drp_avail_f;
51 	spinlock_t list_lock;
52 };
53 
54 static struct dentry *root_dir;
55 
uwb_dbg_rsv_cb(struct uwb_rsv * rsv)56 static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)
57 {
58 	struct uwb_dbg *dbg = rsv->pal_priv;
59 
60 	uwb_rsv_dump("debug", rsv);
61 
62 	if (rsv->state == UWB_RSV_STATE_NONE) {
63 		spin_lock(&dbg->list_lock);
64 		list_del(&rsv->pal_node);
65 		spin_unlock(&dbg->list_lock);
66 		uwb_rsv_destroy(rsv);
67 	}
68 }
69 
cmd_rsv_establish(struct uwb_rc * rc,struct uwb_dbg_cmd_rsv_establish * cmd)70 static int cmd_rsv_establish(struct uwb_rc *rc,
71 			     struct uwb_dbg_cmd_rsv_establish *cmd)
72 {
73 	struct uwb_mac_addr macaddr;
74 	struct uwb_rsv *rsv;
75 	struct uwb_dev *target;
76 	int ret;
77 
78 	memcpy(&macaddr, cmd->target, sizeof(macaddr));
79 	target = uwb_dev_get_by_macaddr(rc, &macaddr);
80 	if (target == NULL)
81 		return -ENODEV;
82 
83 	rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, rc->dbg);
84 	if (rsv == NULL) {
85 		uwb_dev_put(target);
86 		return -ENOMEM;
87 	}
88 
89 	rsv->target.type  = UWB_RSV_TARGET_DEV;
90 	rsv->target.dev   = target;
91 	rsv->type         = cmd->type;
92 	rsv->max_mas      = cmd->max_mas;
93 	rsv->min_mas      = cmd->min_mas;
94 	rsv->max_interval = cmd->max_interval;
95 
96 	ret = uwb_rsv_establish(rsv);
97 	if (ret)
98 		uwb_rsv_destroy(rsv);
99 	else {
100 		spin_lock(&(rc->dbg)->list_lock);
101 		list_add_tail(&rsv->pal_node, &rc->dbg->rsvs);
102 		spin_unlock(&(rc->dbg)->list_lock);
103 	}
104 	return ret;
105 }
106 
cmd_rsv_terminate(struct uwb_rc * rc,struct uwb_dbg_cmd_rsv_terminate * cmd)107 static int cmd_rsv_terminate(struct uwb_rc *rc,
108 			     struct uwb_dbg_cmd_rsv_terminate *cmd)
109 {
110 	struct uwb_rsv *rsv, *found = NULL;
111 	int i = 0;
112 
113 	spin_lock(&(rc->dbg)->list_lock);
114 
115 	list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {
116 		if (i == cmd->index) {
117 			found = rsv;
118 			uwb_rsv_get(found);
119 			break;
120 		}
121 		i++;
122 	}
123 
124 	spin_unlock(&(rc->dbg)->list_lock);
125 
126 	if (!found)
127 		return -EINVAL;
128 
129 	uwb_rsv_terminate(found);
130 	uwb_rsv_put(found);
131 
132 	return 0;
133 }
134 
cmd_ie_add(struct uwb_rc * rc,struct uwb_dbg_cmd_ie * ie_to_add)135 static int cmd_ie_add(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_add)
136 {
137 	return uwb_rc_ie_add(rc,
138 			     (const struct uwb_ie_hdr *) ie_to_add->data,
139 			     ie_to_add->len);
140 }
141 
cmd_ie_rm(struct uwb_rc * rc,struct uwb_dbg_cmd_ie * ie_to_rm)142 static int cmd_ie_rm(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_rm)
143 {
144 	return uwb_rc_ie_rm(rc, ie_to_rm->data[0]);
145 }
146 
command_write(struct file * file,const char __user * buf,size_t len,loff_t * off)147 static ssize_t command_write(struct file *file, const char __user *buf,
148 			 size_t len, loff_t *off)
149 {
150 	struct uwb_rc *rc = file->private_data;
151 	struct uwb_dbg_cmd cmd;
152 	int ret = 0;
153 
154 	if (len != sizeof(struct uwb_dbg_cmd))
155 		return -EINVAL;
156 
157 	if (copy_from_user(&cmd, buf, len) != 0)
158 		return -EFAULT;
159 
160 	switch (cmd.type) {
161 	case UWB_DBG_CMD_RSV_ESTABLISH:
162 		ret = cmd_rsv_establish(rc, &cmd.rsv_establish);
163 		break;
164 	case UWB_DBG_CMD_RSV_TERMINATE:
165 		ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate);
166 		break;
167 	case UWB_DBG_CMD_IE_ADD:
168 		ret = cmd_ie_add(rc, &cmd.ie_add);
169 		break;
170 	case UWB_DBG_CMD_IE_RM:
171 		ret = cmd_ie_rm(rc, &cmd.ie_rm);
172 		break;
173 	case UWB_DBG_CMD_RADIO_START:
174 		ret = uwb_radio_start(&rc->dbg->pal);
175 		break;
176 	case UWB_DBG_CMD_RADIO_STOP:
177 		uwb_radio_stop(&rc->dbg->pal);
178 		break;
179 	default:
180 		return -EINVAL;
181 	}
182 
183 	return ret < 0 ? ret : len;
184 }
185 
186 static const struct file_operations command_fops = {
187 	.open	= simple_open,
188 	.write  = command_write,
189 	.read   = NULL,
190 	.llseek = no_llseek,
191 	.owner  = THIS_MODULE,
192 };
193 
reservations_show(struct seq_file * s,void * p)194 static int reservations_show(struct seq_file *s, void *p)
195 {
196 	struct uwb_rc *rc = s->private;
197 	struct uwb_rsv *rsv;
198 
199 	mutex_lock(&rc->rsvs_mutex);
200 
201 	list_for_each_entry(rsv, &rc->reservations, rc_node) {
202 		struct uwb_dev_addr devaddr;
203 		char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
204 		bool is_owner;
205 
206 		uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
207 		if (rsv->target.type == UWB_RSV_TARGET_DEV) {
208 			devaddr = rsv->target.dev->dev_addr;
209 			is_owner = &rc->uwb_dev == rsv->owner;
210 		} else {
211 			devaddr = rsv->target.devaddr;
212 			is_owner = true;
213 		}
214 		uwb_dev_addr_print(target, sizeof(target), &devaddr);
215 
216 		seq_printf(s, "%c %s -> %s: %s\n",
217 			   is_owner ? 'O' : 'T',
218 			   owner, target, uwb_rsv_state_str(rsv->state));
219 		seq_printf(s, "  stream: %d  type: %s\n",
220 			   rsv->stream, uwb_rsv_type_str(rsv->type));
221 		seq_printf(s, "  %*pb\n", UWB_NUM_MAS, rsv->mas.bm);
222 	}
223 
224 	mutex_unlock(&rc->rsvs_mutex);
225 
226 	return 0;
227 }
228 DEFINE_SHOW_ATTRIBUTE(reservations);
229 
drp_avail_show(struct seq_file * s,void * p)230 static int drp_avail_show(struct seq_file *s, void *p)
231 {
232 	struct uwb_rc *rc = s->private;
233 
234 	seq_printf(s, "global:  %*pb\n", UWB_NUM_MAS, rc->drp_avail.global);
235 	seq_printf(s, "local:   %*pb\n", UWB_NUM_MAS, rc->drp_avail.local);
236 	seq_printf(s, "pending: %*pb\n", UWB_NUM_MAS, rc->drp_avail.pending);
237 
238 	return 0;
239 }
240 DEFINE_SHOW_ATTRIBUTE(drp_avail);
241 
uwb_dbg_channel_changed(struct uwb_pal * pal,int channel)242 static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel)
243 {
244 	struct device *dev = &pal->rc->uwb_dev.dev;
245 
246 	if (channel > 0)
247 		dev_info(dev, "debug: channel %d started\n", channel);
248 	else
249 		dev_info(dev, "debug: channel stopped\n");
250 }
251 
uwb_dbg_new_rsv(struct uwb_pal * pal,struct uwb_rsv * rsv)252 static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv)
253 {
254 	struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);
255 
256 	if (dbg->accept) {
257 		spin_lock(&dbg->list_lock);
258 		list_add_tail(&rsv->pal_node, &dbg->rsvs);
259 		spin_unlock(&dbg->list_lock);
260 		uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg);
261 	}
262 }
263 
264 /**
265  * uwb_dbg_add_rc - add a debug interface for a radio controller
266  * @rc: the radio controller
267  */
uwb_dbg_add_rc(struct uwb_rc * rc)268 void uwb_dbg_add_rc(struct uwb_rc *rc)
269 {
270 	rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL);
271 	if (rc->dbg == NULL)
272 		return;
273 
274 	INIT_LIST_HEAD(&rc->dbg->rsvs);
275 	spin_lock_init(&(rc->dbg)->list_lock);
276 
277 	uwb_pal_init(&rc->dbg->pal);
278 	rc->dbg->pal.rc = rc;
279 	rc->dbg->pal.channel_changed = uwb_dbg_channel_changed;
280 	rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
281 	uwb_pal_register(&rc->dbg->pal);
282 
283 	if (root_dir) {
284 		rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
285 						     root_dir);
286 		rc->dbg->command_f = debugfs_create_file("command", 0200,
287 							 rc->dbg->root_d, rc,
288 							 &command_fops);
289 		rc->dbg->reservations_f = debugfs_create_file("reservations", 0444,
290 							      rc->dbg->root_d, rc,
291 							      &reservations_fops);
292 		rc->dbg->accept_f = debugfs_create_bool("accept", 0644,
293 							rc->dbg->root_d,
294 							&rc->dbg->accept);
295 		rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444,
296 							   rc->dbg->root_d, rc,
297 							   &drp_avail_fops);
298 	}
299 }
300 
301 /**
302  * uwb_dbg_del_rc - remove a radio controller's debug interface
303  * @rc: the radio controller
304  */
uwb_dbg_del_rc(struct uwb_rc * rc)305 void uwb_dbg_del_rc(struct uwb_rc *rc)
306 {
307 	struct uwb_rsv *rsv, *t;
308 
309 	if (rc->dbg == NULL)
310 		return;
311 
312 	list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) {
313 		uwb_rsv_terminate(rsv);
314 	}
315 
316 	uwb_pal_unregister(&rc->dbg->pal);
317 
318 	if (root_dir) {
319 		debugfs_remove(rc->dbg->drp_avail_f);
320 		debugfs_remove(rc->dbg->accept_f);
321 		debugfs_remove(rc->dbg->reservations_f);
322 		debugfs_remove(rc->dbg->command_f);
323 		debugfs_remove(rc->dbg->root_d);
324 	}
325 }
326 
327 /**
328  * uwb_dbg_exit - initialize the debug interface sub-module
329  */
uwb_dbg_init(void)330 void uwb_dbg_init(void)
331 {
332 	root_dir = debugfs_create_dir("uwb", NULL);
333 }
334 
335 /**
336  * uwb_dbg_exit - clean-up the debug interface sub-module
337  */
uwb_dbg_exit(void)338 void uwb_dbg_exit(void)
339 {
340 	debugfs_remove(root_dir);
341 }
342 
343 /**
344  * uwb_dbg_create_pal_dir - create a debugfs directory for a PAL
345  * @pal: The PAL.
346  */
uwb_dbg_create_pal_dir(struct uwb_pal * pal)347 struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal)
348 {
349 	struct uwb_rc *rc = pal->rc;
350 
351 	if (root_dir && rc->dbg && rc->dbg->root_d && pal->name)
352 		return debugfs_create_dir(pal->name, rc->dbg->root_d);
353 	return NULL;
354 }
355