89b2e6a7214c661b82062bdaeee19483ca99a05c
[linux-2.6.git] / drivers / uwb / uwb-debug.c
1 /*
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>
36
37 #include "uwb-internal.h"
38
39 void 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 }
57 EXPORT_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
74 struct 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         spinlock_t list_lock;
86 };
87
88 static struct dentry *root_dir;
89
90 static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)
91 {
92         struct uwb_dbg *dbg = rsv->pal_priv;
93
94         uwb_rsv_dump("debug", rsv);
95
96         if (rsv->state == UWB_RSV_STATE_NONE) {
97                 spin_lock(&dbg->list_lock);
98                 list_del(&rsv->pal_node);
99                 spin_unlock(&dbg->list_lock);
100                 uwb_rsv_destroy(rsv);
101         }
102 }
103
104 static int cmd_rsv_establish(struct uwb_rc *rc,
105                              struct uwb_dbg_cmd_rsv_establish *cmd)
106 {
107         struct uwb_mac_addr macaddr;
108         struct uwb_rsv *rsv;
109         struct uwb_dev *target;
110         int ret;
111
112         memcpy(&macaddr, cmd->target, sizeof(macaddr));
113         target = uwb_dev_get_by_macaddr(rc, &macaddr);
114         if (target == NULL)
115                 return -ENODEV;
116
117         rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, rc->dbg);
118         if (rsv == NULL) {
119                 uwb_dev_put(target);
120                 return -ENOMEM;
121         }
122
123         rsv->target.type  = UWB_RSV_TARGET_DEV;
124         rsv->target.dev   = target;
125         rsv->type         = cmd->type;
126         rsv->max_mas      = cmd->max_mas;
127         rsv->min_mas      = cmd->min_mas;
128         rsv->max_interval = cmd->max_interval;
129
130         ret = uwb_rsv_establish(rsv);
131         if (ret)
132                 uwb_rsv_destroy(rsv);
133         else {
134                 spin_lock(&(rc->dbg)->list_lock);
135                 list_add_tail(&rsv->pal_node, &rc->dbg->rsvs);
136                 spin_unlock(&(rc->dbg)->list_lock);
137         }
138         return ret;
139 }
140
141 static int cmd_rsv_terminate(struct uwb_rc *rc,
142                              struct uwb_dbg_cmd_rsv_terminate *cmd)
143 {
144         struct uwb_rsv *rsv, *found = NULL;
145         int i = 0;
146
147         spin_lock(&(rc->dbg)->list_lock);
148
149         list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {
150                 if (i == cmd->index) {
151                         found = rsv;
152                         uwb_rsv_get(found);
153                         break;
154                 }
155                 i++;
156         }
157
158         spin_unlock(&(rc->dbg)->list_lock);
159
160         if (!found)
161                 return -EINVAL;
162
163         uwb_rsv_terminate(found);
164         uwb_rsv_put(found);
165
166         return 0;
167 }
168
169 static 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
176 static 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
181 static int command_open(struct inode *inode, struct file *file)
182 {
183         file->private_data = inode->i_private;
184
185         return 0;
186 }
187
188 static 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;
193         int ret = 0;
194         
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;
208         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;
214         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;
220         default:
221                 return -EINVAL;
222         }
223
224         return ret < 0 ? ret : len;
225 }
226
227 static 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
235 static 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
272 static int reservations_open(struct inode *inode, struct file *file)
273 {
274         return single_open(file, reservations_print, inode->i_private);
275 }
276
277 static 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
285 static 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
300 static int drp_avail_open(struct inode *inode, struct file *file)
301 {
302         return single_open(file, drp_avail_print, inode->i_private);
303 }
304
305 static 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
313 static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel)
314 {
315         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
323 static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv)
324 {
325         struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);
326
327         if (dbg->accept) {
328                 spin_lock(&dbg->list_lock);
329                 list_add_tail(&rsv->pal_node, &dbg->rsvs);
330                 spin_unlock(&dbg->list_lock);
331                 uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg);
332         }
333 }
334
335 /**
336  * uwb_dbg_add_rc - add a debug interface for a radio controller
337  * @rc: the radio controller
338  */
339 void uwb_dbg_add_rc(struct uwb_rc *rc)
340 {
341         rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL);
342         if (rc->dbg == NULL)
343                 return;
344
345         INIT_LIST_HEAD(&rc->dbg->rsvs);
346         spin_lock_init(&(rc->dbg)->list_lock);
347
348         uwb_pal_init(&rc->dbg->pal);
349         rc->dbg->pal.rc = rc;
350         rc->dbg->pal.channel_changed = uwb_dbg_channel_changed;
351         rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
352         uwb_pal_register(&rc->dbg->pal);
353
354         if (root_dir) {
355                 rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
356                                                      root_dir);
357                 rc->dbg->command_f = debugfs_create_file("command", 0200,
358                                                          rc->dbg->root_d, rc,
359                                                          &command_fops);
360                 rc->dbg->reservations_f = debugfs_create_file("reservations", 0444,
361                                                               rc->dbg->root_d, rc,
362                                                               &reservations_fops);
363                 rc->dbg->accept_f = debugfs_create_bool("accept", 0644,
364                                                         rc->dbg->root_d,
365                                                         &rc->dbg->accept);
366                 rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444,
367                                                            rc->dbg->root_d, rc,
368                                                            &drp_avail_fops);
369         }
370 }
371
372 /**
373  * uwb_dbg_del_rc - remove a radio controller's debug interface
374  * @rc: the radio controller
375  */
376 void uwb_dbg_del_rc(struct uwb_rc *rc)
377 {
378         struct uwb_rsv *rsv, *t;
379
380         if (rc->dbg == NULL)
381                 return;
382
383         list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) {
384                 uwb_rsv_terminate(rsv);
385         }
386
387         uwb_pal_unregister(&rc->dbg->pal);
388
389         if (root_dir) {
390                 debugfs_remove(rc->dbg->drp_avail_f);
391                 debugfs_remove(rc->dbg->accept_f);
392                 debugfs_remove(rc->dbg->reservations_f);
393                 debugfs_remove(rc->dbg->command_f);
394                 debugfs_remove(rc->dbg->root_d);
395         }
396 }
397
398 /**
399  * uwb_dbg_exit - initialize the debug interface sub-module
400  */
401 void uwb_dbg_init(void)
402 {
403         root_dir = debugfs_create_dir("uwb", NULL);
404 }
405
406 /**
407  * uwb_dbg_exit - clean-up the debug interface sub-module
408  */
409 void uwb_dbg_exit(void)
410 {
411         debugfs_remove(root_dir);
412 }
413
414 /**
415  * uwb_dbg_create_pal_dir - create a debugfs directory for a PAL
416  * @pal: The PAL.
417  */
418 struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal)
419 {
420         struct uwb_rc *rc = pal->rc;
421
422         if (root_dir && rc->dbg && rc->dbg->root_d && pal->name)
423                 return debugfs_create_dir(pal->name, rc->dbg->root_d);
424         return NULL;
425 }