blob: a6debb9baf38e93321915cb6dcfefb3acc98c8f2 [file] [log] [blame]
David Vrabel599e8d82008-09-17 16:34:10 +01001/*
2 * Ultra Wide Band
3 * Debug support
4 *
5 * Copyright (C) 2005-2006 Intel Corporation
6 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version
10 * 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 *
23 * FIXME: doc
24 */
25
26#include <linux/spinlock.h>
27#include <linux/module.h>
28#include <linux/slab.h>
29#include <linux/notifier.h>
30#include <linux/device.h>
31#include <linux/debugfs.h>
32#include <linux/uaccess.h>
33#include <linux/seq_file.h>
34
35#include <linux/uwb/debug-cmd.h>
David Vrabel599e8d82008-09-17 16:34:10 +010036
37#include "uwb-internal.h"
38
39void dump_bytes(struct device *dev, const void *_buf, size_t rsize)
40{
41 const char *buf = _buf;
42 char line[32];
43 size_t offset = 0;
44 int cnt, cnt2;
45 for (cnt = 0; cnt < rsize; cnt += 8) {
46 size_t rtop = rsize - cnt < 8 ? rsize - cnt : 8;
47 for (offset = cnt2 = 0; cnt2 < rtop; cnt2++) {
48 offset += scnprintf(line + offset, sizeof(line) - offset,
49 "%02x ", buf[cnt + cnt2] & 0xff);
50 }
51 if (dev)
52 dev_info(dev, "%s\n", line);
53 else
54 printk(KERN_INFO "%s\n", line);
55 }
56}
57EXPORT_SYMBOL_GPL(dump_bytes);
58
59/*
60 * Debug interface
61 *
62 * Per radio controller debugfs files (in uwb/uwbN/):
63 *
64 * command: Flexible command interface (see <linux/uwb/debug-cmd.h>).
65 *
66 * reservations: information on reservations.
67 *
68 * accept: Set to true (Y or 1) to accept reservation requests from
69 * peers.
70 *
71 * drp_avail: DRP availability information.
72 */
73
74struct uwb_dbg {
75 struct uwb_pal pal;
76
77 u32 accept;
78 struct list_head rsvs;
79
80 struct dentry *root_d;
81 struct dentry *command_f;
82 struct dentry *reservations_f;
83 struct dentry *accept_f;
84 struct dentry *drp_avail_f;
85};
86
87static struct dentry *root_dir;
88
89static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)
90{
91 struct uwb_rc *rc = rsv->rc;
92 struct device *dev = &rc->uwb_dev.dev;
93 struct uwb_dev_addr devaddr;
94 char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
95
96 uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
97 if (rsv->target.type == UWB_RSV_TARGET_DEV)
98 devaddr = rsv->target.dev->dev_addr;
99 else
100 devaddr = rsv->target.devaddr;
101 uwb_dev_addr_print(target, sizeof(target), &devaddr);
102
103 dev_dbg(dev, "debug: rsv %s -> %s: %s\n",
104 owner, target, uwb_rsv_state_str(rsv->state));
David Vrabelcae1c112008-10-27 15:22:46 +0000105
106 if (rsv->state == UWB_RSV_STATE_NONE) {
107 list_del(&rsv->pal_node);
108 uwb_rsv_destroy(rsv);
109 }
David Vrabel599e8d82008-09-17 16:34:10 +0100110}
111
112static int cmd_rsv_establish(struct uwb_rc *rc,
113 struct uwb_dbg_cmd_rsv_establish *cmd)
114{
115 struct uwb_mac_addr macaddr;
116 struct uwb_rsv *rsv;
117 struct uwb_dev *target;
118 int ret;
119
120 memcpy(&macaddr, cmd->target, sizeof(macaddr));
121 target = uwb_dev_get_by_macaddr(rc, &macaddr);
122 if (target == NULL)
123 return -ENODEV;
124
David Vrabele4b49580f2008-11-26 12:47:05 +0000125 rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, rc->dbg);
David Vrabel599e8d82008-09-17 16:34:10 +0100126 if (rsv == NULL) {
127 uwb_dev_put(target);
128 return -ENOMEM;
129 }
130
131 rsv->owner = &rc->uwb_dev;
132 rsv->target.type = UWB_RSV_TARGET_DEV;
133 rsv->target.dev = target;
134 rsv->type = cmd->type;
135 rsv->max_mas = cmd->max_mas;
136 rsv->min_mas = cmd->min_mas;
137 rsv->sparsity = cmd->sparsity;
138
139 ret = uwb_rsv_establish(rsv);
140 if (ret)
141 uwb_rsv_destroy(rsv);
142 else
143 list_add_tail(&rsv->pal_node, &rc->dbg->rsvs);
144
145 return ret;
146}
147
148static int cmd_rsv_terminate(struct uwb_rc *rc,
149 struct uwb_dbg_cmd_rsv_terminate *cmd)
150{
151 struct uwb_rsv *rsv, *found = NULL;
152 int i = 0;
153
154 list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {
155 if (i == cmd->index) {
156 found = rsv;
157 break;
158 }
David Vrabelcae1c112008-10-27 15:22:46 +0000159 i++;
David Vrabel599e8d82008-09-17 16:34:10 +0100160 }
161 if (!found)
162 return -EINVAL;
163
David Vrabel599e8d82008-09-17 16:34:10 +0100164 uwb_rsv_terminate(found);
165
166 return 0;
167}
168
Stefano Panella6d5a6812008-11-04 14:24:57 +0000169static int cmd_ie_add(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_add)
170{
171 return uwb_rc_ie_add(rc,
172 (const struct uwb_ie_hdr *) ie_to_add->data,
173 ie_to_add->len);
174}
175
176static int cmd_ie_rm(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_rm)
177{
178 return uwb_rc_ie_rm(rc, ie_to_rm->data[0]);
179}
180
David Vrabel599e8d82008-09-17 16:34:10 +0100181static int command_open(struct inode *inode, struct file *file)
182{
183 file->private_data = inode->i_private;
184
185 return 0;
186}
187
188static ssize_t command_write(struct file *file, const char __user *buf,
189 size_t len, loff_t *off)
190{
191 struct uwb_rc *rc = file->private_data;
192 struct uwb_dbg_cmd cmd;
David Vrabel6fae35f2008-11-17 15:53:42 +0000193 int ret = 0;
David Vrabel599e8d82008-09-17 16:34:10 +0100194
195 if (len != sizeof(struct uwb_dbg_cmd))
196 return -EINVAL;
197
198 if (copy_from_user(&cmd, buf, len) != 0)
199 return -EFAULT;
200
201 switch (cmd.type) {
202 case UWB_DBG_CMD_RSV_ESTABLISH:
203 ret = cmd_rsv_establish(rc, &cmd.rsv_establish);
204 break;
205 case UWB_DBG_CMD_RSV_TERMINATE:
206 ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate);
207 break;
Stefano Panella6d5a6812008-11-04 14:24:57 +0000208 case UWB_DBG_CMD_IE_ADD:
209 ret = cmd_ie_add(rc, &cmd.ie_add);
210 break;
211 case UWB_DBG_CMD_IE_RM:
212 ret = cmd_ie_rm(rc, &cmd.ie_rm);
213 break;
David Vrabel6fae35f2008-11-17 15:53:42 +0000214 case UWB_DBG_CMD_RADIO_START:
215 ret = uwb_radio_start(&rc->dbg->pal);
216 break;
217 case UWB_DBG_CMD_RADIO_STOP:
218 uwb_radio_stop(&rc->dbg->pal);
219 break;
David Vrabel599e8d82008-09-17 16:34:10 +0100220 default:
221 return -EINVAL;
222 }
223
224 return ret < 0 ? ret : len;
225}
226
227static struct file_operations command_fops = {
228 .open = command_open,
229 .write = command_write,
230 .read = NULL,
231 .llseek = no_llseek,
232 .owner = THIS_MODULE,
233};
234
235static int reservations_print(struct seq_file *s, void *p)
236{
237 struct uwb_rc *rc = s->private;
238 struct uwb_rsv *rsv;
239
240 mutex_lock(&rc->rsvs_mutex);
241
242 list_for_each_entry(rsv, &rc->reservations, rc_node) {
243 struct uwb_dev_addr devaddr;
244 char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
245 bool is_owner;
246 char buf[72];
247
248 uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
249 if (rsv->target.type == UWB_RSV_TARGET_DEV) {
250 devaddr = rsv->target.dev->dev_addr;
251 is_owner = &rc->uwb_dev == rsv->owner;
252 } else {
253 devaddr = rsv->target.devaddr;
254 is_owner = true;
255 }
256 uwb_dev_addr_print(target, sizeof(target), &devaddr);
257
258 seq_printf(s, "%c %s -> %s: %s\n",
259 is_owner ? 'O' : 'T',
260 owner, target, uwb_rsv_state_str(rsv->state));
261 seq_printf(s, " stream: %d type: %s\n",
262 rsv->stream, uwb_rsv_type_str(rsv->type));
263 bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS);
264 seq_printf(s, " %s\n", buf);
265 }
266
267 mutex_unlock(&rc->rsvs_mutex);
268
269 return 0;
270}
271
272static int reservations_open(struct inode *inode, struct file *file)
273{
274 return single_open(file, reservations_print, inode->i_private);
275}
276
277static struct file_operations reservations_fops = {
278 .open = reservations_open,
279 .read = seq_read,
280 .llseek = seq_lseek,
281 .release = single_release,
282 .owner = THIS_MODULE,
283};
284
285static int drp_avail_print(struct seq_file *s, void *p)
286{
287 struct uwb_rc *rc = s->private;
288 char buf[72];
289
290 bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.global, UWB_NUM_MAS);
291 seq_printf(s, "global: %s\n", buf);
292 bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.local, UWB_NUM_MAS);
293 seq_printf(s, "local: %s\n", buf);
294 bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.pending, UWB_NUM_MAS);
295 seq_printf(s, "pending: %s\n", buf);
296
297 return 0;
298}
299
300static int drp_avail_open(struct inode *inode, struct file *file)
301{
302 return single_open(file, drp_avail_print, inode->i_private);
303}
304
305static struct file_operations drp_avail_fops = {
306 .open = drp_avail_open,
307 .read = seq_read,
308 .llseek = seq_lseek,
309 .release = single_release,
310 .owner = THIS_MODULE,
311};
312
David Vrabel6fae35f2008-11-17 15:53:42 +0000313static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel)
314{
David Vrabel6fae35f2008-11-17 15:53:42 +0000315 struct device *dev = &pal->rc->uwb_dev.dev;
316
317 if (channel > 0)
318 dev_info(dev, "debug: channel %d started\n", channel);
319 else
320 dev_info(dev, "debug: channel stopped\n");
321}
322
David Vrabele17be2b2008-11-17 15:24:14 +0000323static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv)
David Vrabel599e8d82008-09-17 16:34:10 +0100324{
David Vrabele17be2b2008-11-17 15:24:14 +0000325 struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);
David Vrabel599e8d82008-09-17 16:34:10 +0100326
David Vrabele17be2b2008-11-17 15:24:14 +0000327 if (dbg->accept) {
328 list_add_tail(&rsv->pal_node, &dbg->rsvs);
329 uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg);
David Vrabelcae1c112008-10-27 15:22:46 +0000330 }
David Vrabel599e8d82008-09-17 16:34:10 +0100331}
332
333/**
334 * uwb_dbg_add_rc - add a debug interface for a radio controller
335 * @rc: the radio controller
336 */
337void uwb_dbg_add_rc(struct uwb_rc *rc)
338{
339 rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL);
340 if (rc->dbg == NULL)
341 return;
342
343 INIT_LIST_HEAD(&rc->dbg->rsvs);
344
345 uwb_pal_init(&rc->dbg->pal);
David Vrabel6fae35f2008-11-17 15:53:42 +0000346 rc->dbg->pal.rc = rc;
347 rc->dbg->pal.channel_changed = uwb_dbg_channel_changed;
David Vrabel599e8d82008-09-17 16:34:10 +0100348 rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
David Vrabel6fae35f2008-11-17 15:53:42 +0000349 uwb_pal_register(&rc->dbg->pal);
350
David Vrabel599e8d82008-09-17 16:34:10 +0100351 if (root_dir) {
352 rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
353 root_dir);
354 rc->dbg->command_f = debugfs_create_file("command", 0200,
355 rc->dbg->root_d, rc,
356 &command_fops);
357 rc->dbg->reservations_f = debugfs_create_file("reservations", 0444,
358 rc->dbg->root_d, rc,
359 &reservations_fops);
360 rc->dbg->accept_f = debugfs_create_bool("accept", 0644,
361 rc->dbg->root_d,
362 &rc->dbg->accept);
363 rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444,
364 rc->dbg->root_d, rc,
365 &drp_avail_fops);
366 }
367}
368
369/**
Stefano Panella6d5a6812008-11-04 14:24:57 +0000370 * uwb_dbg_del_rc - remove a radio controller's debug interface
David Vrabel599e8d82008-09-17 16:34:10 +0100371 * @rc: the radio controller
372 */
373void uwb_dbg_del_rc(struct uwb_rc *rc)
374{
375 struct uwb_rsv *rsv, *t;
376
377 if (rc->dbg == NULL)
378 return;
379
380 list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) {
David Vrabelcae1c112008-10-27 15:22:46 +0000381 uwb_rsv_terminate(rsv);
David Vrabel599e8d82008-09-17 16:34:10 +0100382 }
383
David Vrabel6fae35f2008-11-17 15:53:42 +0000384 uwb_pal_unregister(&rc->dbg->pal);
David Vrabel599e8d82008-09-17 16:34:10 +0100385
386 if (root_dir) {
387 debugfs_remove(rc->dbg->drp_avail_f);
388 debugfs_remove(rc->dbg->accept_f);
389 debugfs_remove(rc->dbg->reservations_f);
390 debugfs_remove(rc->dbg->command_f);
391 debugfs_remove(rc->dbg->root_d);
392 }
393}
394
395/**
396 * uwb_dbg_exit - initialize the debug interface sub-module
397 */
398void uwb_dbg_init(void)
399{
400 root_dir = debugfs_create_dir("uwb", NULL);
401}
402
403/**
404 * uwb_dbg_exit - clean-up the debug interface sub-module
405 */
406void uwb_dbg_exit(void)
407{
408 debugfs_remove(root_dir);
409}
David Vrabeldcc74612008-11-26 13:36:59 +0000410
411/**
412 * uwb_dbg_create_pal_dir - create a debugfs directory for a PAL
413 * @pal: The PAL.
414 */
415struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal)
416{
417 struct uwb_rc *rc = pal->rc;
418
419 if (root_dir && rc->dbg && rc->dbg->root_d && pal->name)
420 return debugfs_create_dir(pal->name, rc->dbg->root_d);
421 return NULL;
422}