diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00debug.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00debug.c | 182 |
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 | ||
115 | void 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(×tamp); | ||
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 | |||
93 | static int rt2x00debug_file_open(struct inode *inode, struct file *file) | 172 | static 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 | ||
193 | static 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 | |||
210 | static 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 | |||
221 | static 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 | |||
249 | exit: | ||
250 | kfree_skb(skb); | ||
251 | |||
252 | return status; | ||
253 | } | ||
254 | |||
255 | static 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 | |||
268 | static 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) \ |
115 | static ssize_t rt2x00debug_read_##__name(struct file *file, \ | 277 | static 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 | ||
344 | exit: | 520 | exit: |
@@ -350,11 +526,15 @@ exit: | |||
350 | 526 | ||
351 | void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) | 527 | void 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); |