aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00/rt2x00debug.c
diff options
context:
space:
mode:
authorIvo van Doorn <ivdoorn@gmail.com>2007-11-27 15:49:29 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 18:05:08 -0500
commit4d8dd66c1659ba0d1b110ed0488f4f6ffbc90e71 (patch)
tree03ae5e8fc993ec504f099ddb5d1f40f3a371289d /drivers/net/wireless/rt2x00/rt2x00debug.c
parent08992f7fb139c7dbaf593402312ee5a055352f05 (diff)
rt2x00: Add TX/RX frame dumping facility
This adds TX/RX frame dumping capabilities through debugfs. The intention is that with this approach debugging of rt2x00 is simplified since _all_ frames going in and out of the device are send to debugfs as well along with additional information like the hardware descriptor. Based on the patch by Mattias Nissler. Mattias also has some tools that will make the dumped frames available to wireshark: http://www-user.rhrk.uni-kl.de/~nissler/rt2x00/ Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00debug.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c182
1 files changed, 181 insertions, 1 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 3aa7e0ab513b..e72c98133c05 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -26,10 +26,12 @@
26#include <linux/debugfs.h> 26#include <linux/debugfs.h>
27#include <linux/kernel.h> 27#include <linux/kernel.h>
28#include <linux/module.h> 28#include <linux/module.h>
29#include <linux/poll.h>
29#include <linux/uaccess.h> 30#include <linux/uaccess.h>
30 31
31#include "rt2x00.h" 32#include "rt2x00.h"
32#include "rt2x00lib.h" 33#include "rt2x00lib.h"
34#include "rt2x00dump.h"
33 35
34#define PRINT_LINE_LEN_MAX 32 36#define PRINT_LINE_LEN_MAX 32
35 37
@@ -58,6 +60,8 @@ struct rt2x00debug_intf {
58 * - eeprom offset/value files 60 * - eeprom offset/value files
59 * - bbp offset/value files 61 * - bbp offset/value files
60 * - rf offset/value files 62 * - rf offset/value files
63 * - frame dump folder
64 * - frame dump file
61 */ 65 */
62 struct dentry *driver_folder; 66 struct dentry *driver_folder;
63 struct dentry *driver_entry; 67 struct dentry *driver_entry;
@@ -72,6 +76,24 @@ struct rt2x00debug_intf {
72 struct dentry *bbp_val_entry; 76 struct dentry *bbp_val_entry;
73 struct dentry *rf_off_entry; 77 struct dentry *rf_off_entry;
74 struct dentry *rf_val_entry; 78 struct dentry *rf_val_entry;
79 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;
75 97
76 /* 98 /*
77 * Driver and chipset files will use a data buffer 99 * Driver and chipset files will use a data buffer
@@ -90,6 +112,63 @@ struct rt2x00debug_intf {
90 unsigned int offset_rf; 112 unsigned int offset_rf;
91}; 113};
92 114
115void 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
93static int rt2x00debug_file_open(struct inode *inode, struct file *file) 172static int rt2x00debug_file_open(struct inode *inode, struct file *file)
94{ 173{
95 struct rt2x00debug_intf *intf = inode->i_private; 174 struct rt2x00debug_intf *intf = inode->i_private;
@@ -111,6 +190,89 @@ static int rt2x00debug_file_release(struct inode *inode, struct file *file)
111 return 0; 190 return 0;
112} 191}
113 192
193static 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
114#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ 276#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \
115static ssize_t rt2x00debug_read_##__name(struct file *file, \ 277static ssize_t rt2x00debug_read_##__name(struct file *file, \
116 char __user *buf, \ 278 char __user *buf, \
@@ -339,6 +501,20 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
339 501
340#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY 502#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
341 503
504 intf->frame_folder =
505 debugfs_create_dir("frame", intf->driver_folder);
506 if (IS_ERR(intf->frame_folder))
507 goto exit;
508
509 intf->frame_dump_entry =
510 debugfs_create_file("dump", S_IRUGO, intf->frame_folder,
511 intf, &rt2x00debug_fop_ring_dump);
512 if (IS_ERR(intf->frame_dump_entry))
513 goto exit;
514
515 skb_queue_head_init(&intf->frame_dump_skbqueue);
516 init_waitqueue_head(&intf->frame_dump_waitqueue);
517
342 return; 518 return;
343 519
344exit: 520exit:
@@ -350,11 +526,15 @@ exit:
350 526
351void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) 527void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
352{ 528{
353 const struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; 529 struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
354 530
355 if (unlikely(!intf)) 531 if (unlikely(!intf))
356 return; 532 return;
357 533
534 skb_queue_purge(&intf->frame_dump_skbqueue);
535
536 debugfs_remove(intf->frame_dump_entry);
537 debugfs_remove(intf->frame_folder);
358 debugfs_remove(intf->rf_val_entry); 538 debugfs_remove(intf->rf_val_entry);
359 debugfs_remove(intf->rf_off_entry); 539 debugfs_remove(intf->rf_off_entry);
360 debugfs_remove(intf->bbp_val_entry); 540 debugfs_remove(intf->bbp_val_entry);