Skip to content

Commit

Permalink
ath6kl: add blocking debugfs file for retrieving firmware logs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kalle Valo committed Feb 8, 2012
1 parent 9b9a4f2 commit c807b30
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 1 deletion.
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath6kl/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@ struct ath6kl {
#ifdef CONFIG_ATH6KL_DEBUG
struct {
struct sk_buff_head fwlog_queue;
struct completion fwlog_completion;
bool fwlog_open;

u32 fwlog_mask;

unsigned int dbgfs_diag_reg;
Expand Down
104 changes: 103 additions & 1 deletion drivers/net/wireless/ath/ath6kl/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
spin_lock(&ar->debug.fwlog_queue.lock);

__skb_queue_tail(&ar->debug.fwlog_queue, skb);
complete(&ar->debug.fwlog_completion);

/* drop oldest entries */
while (skb_queue_len(&ar->debug.fwlog_queue) >
Expand All @@ -303,6 +304,28 @@ void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
return;
}

static int ath6kl_fwlog_open(struct inode *inode, struct file *file)
{
struct ath6kl *ar = inode->i_private;

if (ar->debug.fwlog_open)
return -EBUSY;

ar->debug.fwlog_open = true;

file->private_data = inode->i_private;
return 0;
}

static int ath6kl_fwlog_release(struct inode *inode, struct file *file)
{
struct ath6kl *ar = inode->i_private;

ar->debug.fwlog_open = false;

return 0;
}

static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
Expand Down Expand Up @@ -347,12 +370,87 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
}

static const struct file_operations fops_fwlog = {
.open = ath6kl_debugfs_open,
.open = ath6kl_fwlog_open,
.release = ath6kl_fwlog_release,
.read = ath6kl_fwlog_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};

static ssize_t ath6kl_fwlog_block_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ath6kl *ar = file->private_data;
struct sk_buff *skb;
ssize_t ret_cnt;
size_t len = 0, not_copied;
char *buf;
int ret;

buf = vmalloc(count);
if (!buf)
return -ENOMEM;

spin_lock(&ar->debug.fwlog_queue.lock);

if (skb_queue_len(&ar->debug.fwlog_queue) == 0) {
/* we must init under queue lock */
init_completion(&ar->debug.fwlog_completion);

spin_unlock(&ar->debug.fwlog_queue.lock);

ret = wait_for_completion_interruptible(
&ar->debug.fwlog_completion);
if (ret == -ERESTARTSYS)
return ret;

spin_lock(&ar->debug.fwlog_queue.lock);
}

while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
if (skb->len > count - len) {
/* not enough space, put skb back and leave */
__skb_queue_head(&ar->debug.fwlog_queue, skb);
break;
}


memcpy(buf + len, skb->data, skb->len);
len += skb->len;

kfree_skb(skb);
}

spin_unlock(&ar->debug.fwlog_queue.lock);

/* FIXME: what to do if len == 0? */

not_copied = copy_to_user(user_buf, buf, len);
if (not_copied != 0) {
ret_cnt = -EFAULT;
goto out;
}

*ppos = *ppos + len;

ret_cnt = len;

out:
vfree(buf);

return ret_cnt;
}

static const struct file_operations fops_fwlog_block = {
.open = ath6kl_fwlog_open,
.release = ath6kl_fwlog_release,
.read = ath6kl_fwlog_block_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};

static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
Expand Down Expand Up @@ -1623,6 +1721,7 @@ static const struct file_operations fops_power_params = {
int ath6kl_debug_init(struct ath6kl *ar)
{
skb_queue_head_init(&ar->debug.fwlog_queue);
init_completion(&ar->debug.fwlog_completion);

/*
* Actually we are lying here but don't know how to read the mask
Expand All @@ -1647,6 +1746,9 @@ int ath6kl_debug_init(struct ath6kl *ar)
debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
&fops_fwlog);

debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
&fops_fwlog_block);

debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
ar, &fops_fwlog_mask);

Expand Down

0 comments on commit c807b30

Please sign in to comment.