diff options
author | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-02-06 01:23:40 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-02-08 04:25:07 -0500 |
commit | c807b30d2588dd3c74db1f690a0e9e724dd332da (patch) | |
tree | 285723e62f2e40814d164597c6a44340e0d8566f /drivers/net/wireless/ath | |
parent | 9b9a4f2acac2a04416ba15242b8666d4f8273e31 (diff) |
ath6kl: add blocking debugfs file for retrieving firmware logs
When debugging firmware issues it's not always enough to get
the latest firmware logs, sometimes we need to get logs from a longer
period. To make this possible, add a debugfs file named fwlog_block. When
reading from this file ath6kl will send firmware logs whenever available
and otherwise it will block and wait for new logs.
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/core.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/debug.c | 104 |
2 files changed, 106 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 9a58214135b9..4ff06a326785 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h | |||
@@ -653,6 +653,9 @@ struct ath6kl { | |||
653 | #ifdef CONFIG_ATH6KL_DEBUG | 653 | #ifdef CONFIG_ATH6KL_DEBUG |
654 | struct { | 654 | struct { |
655 | struct sk_buff_head fwlog_queue; | 655 | struct sk_buff_head fwlog_queue; |
656 | struct completion fwlog_completion; | ||
657 | bool fwlog_open; | ||
658 | |||
656 | u32 fwlog_mask; | 659 | u32 fwlog_mask; |
657 | 660 | ||
658 | unsigned int dbgfs_diag_reg; | 661 | unsigned int dbgfs_diag_reg; |
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 98b5f15f622e..ec32ff692163 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c | |||
@@ -290,6 +290,7 @@ void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) | |||
290 | spin_lock(&ar->debug.fwlog_queue.lock); | 290 | spin_lock(&ar->debug.fwlog_queue.lock); |
291 | 291 | ||
292 | __skb_queue_tail(&ar->debug.fwlog_queue, skb); | 292 | __skb_queue_tail(&ar->debug.fwlog_queue, skb); |
293 | complete(&ar->debug.fwlog_completion); | ||
293 | 294 | ||
294 | /* drop oldest entries */ | 295 | /* drop oldest entries */ |
295 | while (skb_queue_len(&ar->debug.fwlog_queue) > | 296 | while (skb_queue_len(&ar->debug.fwlog_queue) > |
@@ -303,6 +304,28 @@ void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) | |||
303 | return; | 304 | return; |
304 | } | 305 | } |
305 | 306 | ||
307 | static int ath6kl_fwlog_open(struct inode *inode, struct file *file) | ||
308 | { | ||
309 | struct ath6kl *ar = inode->i_private; | ||
310 | |||
311 | if (ar->debug.fwlog_open) | ||
312 | return -EBUSY; | ||
313 | |||
314 | ar->debug.fwlog_open = true; | ||
315 | |||
316 | file->private_data = inode->i_private; | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int ath6kl_fwlog_release(struct inode *inode, struct file *file) | ||
321 | { | ||
322 | struct ath6kl *ar = inode->i_private; | ||
323 | |||
324 | ar->debug.fwlog_open = false; | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
306 | static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, | 329 | static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, |
307 | size_t count, loff_t *ppos) | 330 | size_t count, loff_t *ppos) |
308 | { | 331 | { |
@@ -347,12 +370,87 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, | |||
347 | } | 370 | } |
348 | 371 | ||
349 | static const struct file_operations fops_fwlog = { | 372 | static const struct file_operations fops_fwlog = { |
350 | .open = ath6kl_debugfs_open, | 373 | .open = ath6kl_fwlog_open, |
374 | .release = ath6kl_fwlog_release, | ||
351 | .read = ath6kl_fwlog_read, | 375 | .read = ath6kl_fwlog_read, |
352 | .owner = THIS_MODULE, | 376 | .owner = THIS_MODULE, |
353 | .llseek = default_llseek, | 377 | .llseek = default_llseek, |
354 | }; | 378 | }; |
355 | 379 | ||
380 | static ssize_t ath6kl_fwlog_block_read(struct file *file, | ||
381 | char __user *user_buf, | ||
382 | size_t count, | ||
383 | loff_t *ppos) | ||
384 | { | ||
385 | struct ath6kl *ar = file->private_data; | ||
386 | struct sk_buff *skb; | ||
387 | ssize_t ret_cnt; | ||
388 | size_t len = 0, not_copied; | ||
389 | char *buf; | ||
390 | int ret; | ||
391 | |||
392 | buf = vmalloc(count); | ||
393 | if (!buf) | ||
394 | return -ENOMEM; | ||
395 | |||
396 | spin_lock(&ar->debug.fwlog_queue.lock); | ||
397 | |||
398 | if (skb_queue_len(&ar->debug.fwlog_queue) == 0) { | ||
399 | /* we must init under queue lock */ | ||
400 | init_completion(&ar->debug.fwlog_completion); | ||
401 | |||
402 | spin_unlock(&ar->debug.fwlog_queue.lock); | ||
403 | |||
404 | ret = wait_for_completion_interruptible( | ||
405 | &ar->debug.fwlog_completion); | ||
406 | if (ret == -ERESTARTSYS) | ||
407 | return ret; | ||
408 | |||
409 | spin_lock(&ar->debug.fwlog_queue.lock); | ||
410 | } | ||
411 | |||
412 | while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { | ||
413 | if (skb->len > count - len) { | ||
414 | /* not enough space, put skb back and leave */ | ||
415 | __skb_queue_head(&ar->debug.fwlog_queue, skb); | ||
416 | break; | ||
417 | } | ||
418 | |||
419 | |||
420 | memcpy(buf + len, skb->data, skb->len); | ||
421 | len += skb->len; | ||
422 | |||
423 | kfree_skb(skb); | ||
424 | } | ||
425 | |||
426 | spin_unlock(&ar->debug.fwlog_queue.lock); | ||
427 | |||
428 | /* FIXME: what to do if len == 0? */ | ||
429 | |||
430 | not_copied = copy_to_user(user_buf, buf, len); | ||
431 | if (not_copied != 0) { | ||
432 | ret_cnt = -EFAULT; | ||
433 | goto out; | ||
434 | } | ||
435 | |||
436 | *ppos = *ppos + len; | ||
437 | |||
438 | ret_cnt = len; | ||
439 | |||
440 | out: | ||
441 | vfree(buf); | ||
442 | |||
443 | return ret_cnt; | ||
444 | } | ||
445 | |||
446 | static const struct file_operations fops_fwlog_block = { | ||
447 | .open = ath6kl_fwlog_open, | ||
448 | .release = ath6kl_fwlog_release, | ||
449 | .read = ath6kl_fwlog_block_read, | ||
450 | .owner = THIS_MODULE, | ||
451 | .llseek = default_llseek, | ||
452 | }; | ||
453 | |||
356 | static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, | 454 | static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, |
357 | size_t count, loff_t *ppos) | 455 | size_t count, loff_t *ppos) |
358 | { | 456 | { |
@@ -1623,6 +1721,7 @@ static const struct file_operations fops_power_params = { | |||
1623 | int ath6kl_debug_init(struct ath6kl *ar) | 1721 | int ath6kl_debug_init(struct ath6kl *ar) |
1624 | { | 1722 | { |
1625 | skb_queue_head_init(&ar->debug.fwlog_queue); | 1723 | skb_queue_head_init(&ar->debug.fwlog_queue); |
1724 | init_completion(&ar->debug.fwlog_completion); | ||
1626 | 1725 | ||
1627 | /* | 1726 | /* |
1628 | * Actually we are lying here but don't know how to read the mask | 1727 | * Actually we are lying here but don't know how to read the mask |
@@ -1647,6 +1746,9 @@ int ath6kl_debug_init(struct ath6kl *ar) | |||
1647 | debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, | 1746 | debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, |
1648 | &fops_fwlog); | 1747 | &fops_fwlog); |
1649 | 1748 | ||
1749 | debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar, | ||
1750 | &fops_fwlog_block); | ||
1751 | |||
1650 | debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, | 1752 | debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, |
1651 | ar, &fops_fwlog_mask); | 1753 | ar, &fops_fwlog_mask); |
1652 | 1754 | ||