blob: c55bf0eea274f9201eecdd9770d025da6d3e6a83 [file] [log] [blame]
Ivo van Doorn95ea3622007-09-25 17:57:13 -07001/*
2 Copyright (C) 2004 - 2007 rt2x00 SourceForge Project
3 <http://rt2x00.serialmonkey.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21/*
22 Module: rt2x00lib
23 Abstract: rt2x00 debugfs specific routines.
24 */
25
Ivo van Doorn95ea3622007-09-25 17:57:13 -070026#include <linux/debugfs.h>
27#include <linux/kernel.h>
28#include <linux/module.h>
Ivo van Doorn4d8dd662007-11-27 21:49:29 +010029#include <linux/poll.h>
Ivo van Doorn95ea3622007-09-25 17:57:13 -070030#include <linux/uaccess.h>
31
32#include "rt2x00.h"
33#include "rt2x00lib.h"
Ivo van Doorn4d8dd662007-11-27 21:49:29 +010034#include "rt2x00dump.h"
Ivo van Doorn95ea3622007-09-25 17:57:13 -070035
36#define PRINT_LINE_LEN_MAX 32
37
38struct rt2x00debug_intf {
39 /*
40 * Pointer to driver structure where
41 * this debugfs entry belongs to.
42 */
43 struct rt2x00_dev *rt2x00dev;
44
45 /*
46 * Reference to the rt2x00debug structure
47 * which can be used to communicate with
48 * the registers.
49 */
50 const struct rt2x00debug *debug;
51
52 /*
53 * Debugfs entries for:
54 * - driver folder
Ivo van Doorn91921a42007-11-27 21:48:16 +010055 * - driver file
56 * - chipset file
57 * - device flags file
58 * - register folder
59 * - csr offset/value files
60 * - eeprom offset/value files
61 * - bbp offset/value files
62 * - rf offset/value files
Ivo van Doorn4d8dd662007-11-27 21:49:29 +010063 * - frame dump folder
64 * - frame dump file
Ivo van Doorn95ea3622007-09-25 17:57:13 -070065 */
66 struct dentry *driver_folder;
67 struct dentry *driver_entry;
68 struct dentry *chipset_entry;
Ivo van Doorn643b2522007-09-25 20:13:51 +020069 struct dentry *dev_flags;
Ivo van Doorn91921a42007-11-27 21:48:16 +010070 struct dentry *register_folder;
Ivo van Doorn95ea3622007-09-25 17:57:13 -070071 struct dentry *csr_off_entry;
72 struct dentry *csr_val_entry;
73 struct dentry *eeprom_off_entry;
74 struct dentry *eeprom_val_entry;
75 struct dentry *bbp_off_entry;
76 struct dentry *bbp_val_entry;
77 struct dentry *rf_off_entry;
78 struct dentry *rf_val_entry;
Ivo van Doorn4d8dd662007-11-27 21:49:29 +010079 struct dentry *frame_folder;
80 struct dentry *frame_dump_entry;
81
82 /*
83 * The frame dump file only allows a single reader,
84 * so we need to store the current state here.
85 */
86 unsigned long frame_dump_flags;
87#define FRAME_DUMP_FILE_OPEN 1
88
89 /*
90 * We queue each frame before dumping it to the user,
91 * per read command we will pass a single skb structure
92 * so we should be prepared to queue multiple sk buffers
93 * before sending it to userspace.
94 */
95 struct sk_buff_head frame_dump_skbqueue;
96 wait_queue_head_t frame_dump_waitqueue;
Ivo van Doorn95ea3622007-09-25 17:57:13 -070097
98 /*
99 * Driver and chipset files will use a data buffer
100 * that has been created in advance. This will simplify
101 * the code since we can use the debugfs functions.
102 */
103 struct debugfs_blob_wrapper driver_blob;
104 struct debugfs_blob_wrapper chipset_blob;
105
106 /*
107 * Requested offset for each register type.
108 */
109 unsigned int offset_csr;
110 unsigned int offset_eeprom;
111 unsigned int offset_bbp;
112 unsigned int offset_rf;
113};
114
Ivo van Doorn4d8dd662007-11-27 21:49:29 +0100115void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
116 struct sk_buff *skb)
117{
118 struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
119 struct skb_desc *desc = get_skb_desc(skb);
120 struct sk_buff *skbcopy;
121 struct rt2x00dump_hdr *dump_hdr;
122 struct timeval timestamp;
123 unsigned int ring_index;
124 unsigned int entry_index;
125
126 do_gettimeofday(&timestamp);
127 ring_index = ARRAY_INDEX(desc->ring, rt2x00dev->rx);
128 entry_index = ARRAY_INDEX(desc->entry, desc->ring->entry);
129
130 if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))
131 return;
132
133 if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) {
134 DEBUG(rt2x00dev, "txrx dump queue length exceeded.\n");
135 return;
136 }
137
138 skbcopy = alloc_skb(sizeof(*dump_hdr) + desc->desc_len + desc->data_len,
139 GFP_ATOMIC);
140 if (!skbcopy) {
141 DEBUG(rt2x00dev, "Failed to copy skb for dump.\n");
142 return;
143 }
144
145 dump_hdr = (struct rt2x00dump_hdr *)skb_put(skbcopy, sizeof(*dump_hdr));
146 dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION);
147 dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr));
148 dump_hdr->desc_length = cpu_to_le32(desc->desc_len);
149 dump_hdr->data_length = cpu_to_le32(desc->data_len);
150 dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt);
151 dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf);
152 dump_hdr->chip_rev = cpu_to_le32(rt2x00dev->chip.rev);
153 dump_hdr->type = cpu_to_le16(desc->frame_type);
154 dump_hdr->ring_index = ring_index;
155 dump_hdr->entry_index = entry_index;
156 dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec);
157 dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_usec);
158
159 memcpy(skb_put(skbcopy, desc->desc_len), desc->desc, desc->desc_len);
160 memcpy(skb_put(skbcopy, desc->data_len), desc->data, desc->data_len);
161
162 skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy);
163 wake_up_interruptible(&intf->frame_dump_waitqueue);
164
165 /*
166 * Verify that the file has not been closed while we were working.
167 */
168 if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))
169 skb_queue_purge(&intf->frame_dump_skbqueue);
170}
171
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700172static int rt2x00debug_file_open(struct inode *inode, struct file *file)
173{
174 struct rt2x00debug_intf *intf = inode->i_private;
175
176 file->private_data = inode->i_private;
177
178 if (!try_module_get(intf->debug->owner))
179 return -EBUSY;
180
181 return 0;
182}
183
184static int rt2x00debug_file_release(struct inode *inode, struct file *file)
185{
186 struct rt2x00debug_intf *intf = file->private_data;
187
188 module_put(intf->debug->owner);
189
190 return 0;
191}
192
Ivo van Doorn4d8dd662007-11-27 21:49:29 +0100193static int rt2x00debug_open_ring_dump(struct inode *inode, struct file *file)
194{
195 struct rt2x00debug_intf *intf = inode->i_private;
196 int retval;
197
198 retval = rt2x00debug_file_open(inode, file);
199 if (retval)
200 return retval;
201
202 if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) {
203 rt2x00debug_file_release(inode, file);
204 return -EBUSY;
205 }
206
207 return 0;
208}
209
210static int rt2x00debug_release_ring_dump(struct inode *inode, struct file *file)
211{
212 struct rt2x00debug_intf *intf = inode->i_private;
213
214 skb_queue_purge(&intf->frame_dump_skbqueue);
215
216 clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags);
217
218 return rt2x00debug_file_release(inode, file);
219}
220
221static ssize_t rt2x00debug_read_ring_dump(struct file *file,
222 char __user *buf,
223 size_t length,
224 loff_t *offset)
225{
226 struct rt2x00debug_intf *intf = file->private_data;
227 struct sk_buff *skb;
228 size_t status;
229 int retval;
230
231 if (file->f_flags & O_NONBLOCK)
232 return -EAGAIN;
233
234 retval =
235 wait_event_interruptible(intf->frame_dump_waitqueue,
236 (skb =
237 skb_dequeue(&intf->frame_dump_skbqueue)));
238 if (retval)
239 return retval;
240
241 status = min((size_t)skb->len, length);
242 if (copy_to_user(buf, skb->data, status)) {
243 status = -EFAULT;
244 goto exit;
245 }
246
247 *offset += status;
248
249exit:
250 kfree_skb(skb);
251
252 return status;
253}
254
255static unsigned int rt2x00debug_poll_ring_dump(struct file *file,
256 poll_table *wait)
257{
258 struct rt2x00debug_intf *intf = file->private_data;
259
260 poll_wait(file, &intf->frame_dump_waitqueue, wait);
261
262 if (!skb_queue_empty(&intf->frame_dump_skbqueue))
263 return POLLOUT | POLLWRNORM;
264
265 return 0;
266}
267
268static const struct file_operations rt2x00debug_fop_ring_dump = {
269 .owner = THIS_MODULE,
270 .read = rt2x00debug_read_ring_dump,
271 .poll = rt2x00debug_poll_ring_dump,
272 .open = rt2x00debug_open_ring_dump,
273 .release = rt2x00debug_release_ring_dump,
274};
275
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700276#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \
277static ssize_t rt2x00debug_read_##__name(struct file *file, \
278 char __user *buf, \
279 size_t length, \
280 loff_t *offset) \
281{ \
Ivo van Doorn91921a42007-11-27 21:48:16 +0100282 struct rt2x00debug_intf *intf = file->private_data; \
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700283 const struct rt2x00debug *debug = intf->debug; \
284 char line[16]; \
285 size_t size; \
286 __type value; \
287 \
288 if (*offset) \
289 return 0; \
290 \
291 if (intf->offset_##__name >= debug->__name.word_count) \
292 return -EINVAL; \
293 \
294 debug->__name.read(intf->rt2x00dev, \
295 intf->offset_##__name, &value); \
296 \
297 size = sprintf(line, __format, value); \
298 \
299 if (copy_to_user(buf, line, size)) \
300 return -EFAULT; \
301 \
302 *offset += size; \
303 return size; \
304}
305
306#define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \
307static ssize_t rt2x00debug_write_##__name(struct file *file, \
308 const char __user *buf,\
309 size_t length, \
310 loff_t *offset) \
311{ \
Ivo van Doorn91921a42007-11-27 21:48:16 +0100312 struct rt2x00debug_intf *intf = file->private_data; \
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700313 const struct rt2x00debug *debug = intf->debug; \
314 char line[16]; \
315 size_t size; \
316 __type value; \
317 \
318 if (*offset) \
319 return 0; \
320 \
321 if (!capable(CAP_NET_ADMIN)) \
322 return -EPERM; \
323 \
324 if (intf->offset_##__name >= debug->__name.word_count) \
325 return -EINVAL; \
326 \
327 if (copy_from_user(line, buf, length)) \
328 return -EFAULT; \
329 \
330 size = strlen(line); \
331 value = simple_strtoul(line, NULL, 0); \
332 \
333 debug->__name.write(intf->rt2x00dev, \
334 intf->offset_##__name, value); \
335 \
336 *offset += size; \
337 return size; \
338}
339
340#define RT2X00DEBUGFS_OPS(__name, __format, __type) \
341RT2X00DEBUGFS_OPS_READ(__name, __format, __type); \
342RT2X00DEBUGFS_OPS_WRITE(__name, __type); \
343 \
344static const struct file_operations rt2x00debug_fop_##__name = {\
345 .owner = THIS_MODULE, \
346 .read = rt2x00debug_read_##__name, \
347 .write = rt2x00debug_write_##__name, \
348 .open = rt2x00debug_file_open, \
349 .release = rt2x00debug_file_release, \
350};
351
352RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32);
353RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16);
354RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8);
355RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32);
356
Ivo van Doorn643b2522007-09-25 20:13:51 +0200357static ssize_t rt2x00debug_read_dev_flags(struct file *file,
358 char __user *buf,
359 size_t length,
360 loff_t *offset)
361{
362 struct rt2x00debug_intf *intf = file->private_data;
363 char line[16];
364 size_t size;
365
366 if (*offset)
367 return 0;
368
369 size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags);
370
371 if (copy_to_user(buf, line, size))
372 return -EFAULT;
373
374 *offset += size;
375 return size;
376}
377
378static const struct file_operations rt2x00debug_fop_dev_flags = {
379 .owner = THIS_MODULE,
380 .read = rt2x00debug_read_dev_flags,
381 .open = rt2x00debug_file_open,
382 .release = rt2x00debug_file_release,
383};
384
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700385static struct dentry *rt2x00debug_create_file_driver(const char *name,
386 struct rt2x00debug_intf
387 *intf,
388 struct debugfs_blob_wrapper
389 *blob)
390{
391 char *data;
392
393 data = kzalloc(3 * PRINT_LINE_LEN_MAX, GFP_KERNEL);
394 if (!data)
395 return NULL;
396
397 blob->data = data;
398 data += sprintf(data, "driver: %s\n", intf->rt2x00dev->ops->name);
399 data += sprintf(data, "version: %s\n", DRV_VERSION);
400 data += sprintf(data, "compiled: %s %s\n", __DATE__, __TIME__);
401 blob->size = strlen(blob->data);
402
403 return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob);
404}
405
406static struct dentry *rt2x00debug_create_file_chipset(const char *name,
407 struct rt2x00debug_intf
408 *intf,
409 struct
410 debugfs_blob_wrapper
411 *blob)
412{
413 const struct rt2x00debug *debug = intf->debug;
414 char *data;
415
Ivo van Doorn22c96c22007-11-27 21:48:37 +0100416 data = kzalloc(8 * PRINT_LINE_LEN_MAX, GFP_KERNEL);
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700417 if (!data)
418 return NULL;
419
Ivo van Doorn3e34c6d2008-01-06 23:38:10 +0100420 blob->data = data;
Ivo van Doorn22c96c22007-11-27 21:48:37 +0100421 data += sprintf(data, "rt chip: %04x\n", intf->rt2x00dev->chip.rt);
422 data += sprintf(data, "rf chip: %04x\n", intf->rt2x00dev->chip.rf);
423 data += sprintf(data, "revision:%08x\n", intf->rt2x00dev->chip.rev);
424 data += sprintf(data, "\n");
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700425 data += sprintf(data, "csr length: %d\n", debug->csr.word_count);
426 data += sprintf(data, "eeprom length: %d\n", debug->eeprom.word_count);
427 data += sprintf(data, "bbp length: %d\n", debug->bbp.word_count);
428 data += sprintf(data, "rf length: %d\n", debug->rf.word_count);
Ivo van Doorn3e34c6d2008-01-06 23:38:10 +0100429 blob->size = strlen(blob->data);
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700430
431 return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob);
432}
433
434void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
435{
436 const struct rt2x00debug *debug = rt2x00dev->ops->debugfs;
437 struct rt2x00debug_intf *intf;
438
439 intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL);
440 if (!intf) {
441 ERROR(rt2x00dev, "Failed to allocate debug handler.\n");
442 return;
443 }
444
445 intf->debug = debug;
446 intf->rt2x00dev = rt2x00dev;
447 rt2x00dev->debugfs_intf = intf;
448
449 intf->driver_folder =
450 debugfs_create_dir(intf->rt2x00dev->ops->name,
451 rt2x00dev->hw->wiphy->debugfsdir);
452 if (IS_ERR(intf->driver_folder))
453 goto exit;
454
455 intf->driver_entry =
456 rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
457 if (IS_ERR(intf->driver_entry))
458 goto exit;
459
460 intf->chipset_entry =
461 rt2x00debug_create_file_chipset("chipset",
462 intf, &intf->chipset_blob);
463 if (IS_ERR(intf->chipset_entry))
464 goto exit;
465
Ivo van Doorn643b2522007-09-25 20:13:51 +0200466 intf->dev_flags = debugfs_create_file("dev_flags", S_IRUGO,
467 intf->driver_folder, intf,
468 &rt2x00debug_fop_dev_flags);
469 if (IS_ERR(intf->dev_flags))
470 goto exit;
471
Ivo van Doorn91921a42007-11-27 21:48:16 +0100472 intf->register_folder =
473 debugfs_create_dir("register", intf->driver_folder);
474 if (IS_ERR(intf->register_folder))
475 goto exit;
476
477#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700478({ \
479 (__intf)->__name##_off_entry = \
480 debugfs_create_u32(__stringify(__name) "_offset", \
481 S_IRUGO | S_IWUSR, \
Ivo van Doorn91921a42007-11-27 21:48:16 +0100482 (__intf)->register_folder, \
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700483 &(__intf)->offset_##__name); \
484 if (IS_ERR((__intf)->__name##_off_entry)) \
485 goto exit; \
486 \
487 (__intf)->__name##_val_entry = \
488 debugfs_create_file(__stringify(__name) "_value", \
489 S_IRUGO | S_IWUSR, \
Ivo van Doorn91921a42007-11-27 21:48:16 +0100490 (__intf)->register_folder, \
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700491 (__intf), &rt2x00debug_fop_##__name);\
492 if (IS_ERR((__intf)->__name##_val_entry)) \
493 goto exit; \
494})
495
Ivo van Doorn91921a42007-11-27 21:48:16 +0100496 RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr);
497 RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom);
498 RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp);
499 RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf);
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700500
Ivo van Doorn91921a42007-11-27 21:48:16 +0100501#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700502
Ivo van Doorn4d8dd662007-11-27 21:49:29 +0100503 intf->frame_folder =
504 debugfs_create_dir("frame", intf->driver_folder);
505 if (IS_ERR(intf->frame_folder))
506 goto exit;
507
508 intf->frame_dump_entry =
509 debugfs_create_file("dump", S_IRUGO, intf->frame_folder,
510 intf, &rt2x00debug_fop_ring_dump);
511 if (IS_ERR(intf->frame_dump_entry))
512 goto exit;
513
514 skb_queue_head_init(&intf->frame_dump_skbqueue);
515 init_waitqueue_head(&intf->frame_dump_waitqueue);
516
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700517 return;
518
519exit:
520 rt2x00debug_deregister(rt2x00dev);
521 ERROR(rt2x00dev, "Failed to register debug handler.\n");
522
523 return;
524}
525
526void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
527{
Ivo van Doorn4d8dd662007-11-27 21:49:29 +0100528 struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700529
530 if (unlikely(!intf))
531 return;
532
Ivo van Doorn4d8dd662007-11-27 21:49:29 +0100533 skb_queue_purge(&intf->frame_dump_skbqueue);
534
535 debugfs_remove(intf->frame_dump_entry);
536 debugfs_remove(intf->frame_folder);
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700537 debugfs_remove(intf->rf_val_entry);
538 debugfs_remove(intf->rf_off_entry);
539 debugfs_remove(intf->bbp_val_entry);
540 debugfs_remove(intf->bbp_off_entry);
541 debugfs_remove(intf->eeprom_val_entry);
542 debugfs_remove(intf->eeprom_off_entry);
543 debugfs_remove(intf->csr_val_entry);
544 debugfs_remove(intf->csr_off_entry);
Ivo van Doorn91921a42007-11-27 21:48:16 +0100545 debugfs_remove(intf->register_folder);
Ivo van Doorn643b2522007-09-25 20:13:51 +0200546 debugfs_remove(intf->dev_flags);
Ivo van Doorn95ea3622007-09-25 17:57:13 -0700547 debugfs_remove(intf->chipset_entry);
548 debugfs_remove(intf->driver_entry);
549 debugfs_remove(intf->driver_folder);
550 kfree(intf->chipset_blob.data);
551 kfree(intf->driver_blob.data);
552 kfree(intf);
553
554 rt2x00dev->debugfs_intf = NULL;
555}