|
@@ -16,7 +16,7 @@
|
|
|
|
|
|
#include "core.h"
|
|
|
|
|
|
-#include <linux/circ_buf.h>
|
|
|
+#include <linux/skbuff.h>
|
|
|
#include <linux/fs.h>
|
|
|
#include <linux/vmalloc.h>
|
|
|
#include <linux/export.h>
|
|
@@ -32,9 +32,8 @@ struct ath6kl_fwlog_slot {
|
|
|
u8 payload[0];
|
|
|
};
|
|
|
|
|
|
-#define ATH6KL_FWLOG_SIZE 32768
|
|
|
-#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
|
|
|
- ATH6KL_FWLOG_PAYLOAD_SIZE)
|
|
|
+#define ATH6KL_FWLOG_MAX_ENTRIES 20
|
|
|
+
|
|
|
#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
|
|
|
|
|
|
int ath6kl_printk(const char *level, const char *fmt, ...)
|
|
@@ -268,105 +267,77 @@ static const struct file_operations fops_war_stats = {
|
|
|
.llseek = default_llseek,
|
|
|
};
|
|
|
|
|
|
-static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
|
|
|
- size_t buf_len)
|
|
|
-{
|
|
|
- struct circ_buf *fwlog = &ar->debug.fwlog_buf;
|
|
|
- size_t space;
|
|
|
- int i;
|
|
|
-
|
|
|
- /* entries must all be equal size */
|
|
|
- if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
|
|
|
- return;
|
|
|
-
|
|
|
- space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
|
|
|
- if (space < buf_len)
|
|
|
- /* discard oldest slot */
|
|
|
- fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
|
|
|
- (ATH6KL_FWLOG_SIZE - 1);
|
|
|
-
|
|
|
- for (i = 0; i < buf_len; i += space) {
|
|
|
- space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
|
|
|
- ATH6KL_FWLOG_SIZE);
|
|
|
-
|
|
|
- if ((size_t) space > buf_len - i)
|
|
|
- space = buf_len - i;
|
|
|
-
|
|
|
- memcpy(&fwlog->buf[fwlog->head], buf, space);
|
|
|
- fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
|
|
|
{
|
|
|
- struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
|
|
|
+ struct ath6kl_fwlog_slot *slot;
|
|
|
+ struct sk_buff *skb;
|
|
|
size_t slot_len;
|
|
|
|
|
|
if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
|
|
|
return;
|
|
|
|
|
|
- spin_lock_bh(&ar->debug.fwlog_lock);
|
|
|
+ slot_len = sizeof(*slot) + len;
|
|
|
|
|
|
+ skb = alloc_skb(slot_len, GFP_KERNEL);
|
|
|
+ if (!skb)
|
|
|
+ return;
|
|
|
+
|
|
|
+ slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len);
|
|
|
slot->timestamp = cpu_to_le32(jiffies);
|
|
|
slot->length = cpu_to_le32(len);
|
|
|
memcpy(slot->payload, buf, len);
|
|
|
|
|
|
- slot_len = sizeof(*slot) + len;
|
|
|
+ spin_lock(&ar->debug.fwlog_queue.lock);
|
|
|
|
|
|
- if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
|
|
|
- memset(slot->payload + len, 0,
|
|
|
- ATH6KL_FWLOG_SLOT_SIZE - slot_len);
|
|
|
+ __skb_queue_tail(&ar->debug.fwlog_queue, skb);
|
|
|
|
|
|
- ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
|
|
|
+ /* drop oldest entries */
|
|
|
+ while (skb_queue_len(&ar->debug.fwlog_queue) >
|
|
|
+ ATH6KL_FWLOG_MAX_ENTRIES) {
|
|
|
+ skb = __skb_dequeue(&ar->debug.fwlog_queue);
|
|
|
+ kfree_skb(skb);
|
|
|
+ }
|
|
|
|
|
|
- spin_unlock_bh(&ar->debug.fwlog_lock);
|
|
|
-}
|
|
|
+ spin_unlock(&ar->debug.fwlog_queue.lock);
|
|
|
|
|
|
-static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
|
|
|
-{
|
|
|
- return CIRC_CNT(ar->debug.fwlog_buf.head,
|
|
|
- ar->debug.fwlog_buf.tail,
|
|
|
- ATH6KL_FWLOG_SLOT_SIZE) == 0;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
|
|
|
size_t count, loff_t *ppos)
|
|
|
{
|
|
|
struct ath6kl *ar = file->private_data;
|
|
|
- struct circ_buf *fwlog = &ar->debug.fwlog_buf;
|
|
|
- size_t len = 0, buf_len = count;
|
|
|
+ struct sk_buff *skb;
|
|
|
ssize_t ret_cnt;
|
|
|
+ size_t len = 0;
|
|
|
char *buf;
|
|
|
- int ccnt;
|
|
|
|
|
|
- buf = vmalloc(buf_len);
|
|
|
+ buf = vmalloc(count);
|
|
|
if (!buf)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
/* read undelivered logs from firmware */
|
|
|
ath6kl_read_fwlogs(ar);
|
|
|
|
|
|
- spin_lock_bh(&ar->debug.fwlog_lock);
|
|
|
+ spin_lock(&ar->debug.fwlog_queue.lock);
|
|
|
|
|
|
- while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
|
|
|
- ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
|
|
|
- ATH6KL_FWLOG_SIZE);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- if ((size_t) ccnt > buf_len - len)
|
|
|
- ccnt = buf_len - len;
|
|
|
|
|
|
- memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
|
|
|
- len += ccnt;
|
|
|
+ memcpy(buf + len, skb->data, skb->len);
|
|
|
+ len += skb->len;
|
|
|
|
|
|
- fwlog->tail = (fwlog->tail + ccnt) &
|
|
|
- (ATH6KL_FWLOG_SIZE - 1);
|
|
|
+ kfree_skb(skb);
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&ar->debug.fwlog_lock);
|
|
|
+ spin_unlock(&ar->debug.fwlog_queue.lock);
|
|
|
|
|
|
- if (WARN_ON(len > buf_len))
|
|
|
- len = buf_len;
|
|
|
+ /* FIXME: what to do if len == 0? */
|
|
|
|
|
|
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
|
|
|
@@ -1651,17 +1622,7 @@ static const struct file_operations fops_power_params = {
|
|
|
|
|
|
int ath6kl_debug_init(struct ath6kl *ar)
|
|
|
{
|
|
|
- ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
|
|
|
- if (ar->debug.fwlog_buf.buf == NULL)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
|
|
|
- if (ar->debug.fwlog_tmp == NULL) {
|
|
|
- vfree(ar->debug.fwlog_buf.buf);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- spin_lock_init(&ar->debug.fwlog_lock);
|
|
|
+ skb_queue_head_init(&ar->debug.fwlog_queue);
|
|
|
|
|
|
/*
|
|
|
* Actually we are lying here but don't know how to read the mask
|
|
@@ -1671,11 +1632,8 @@ int ath6kl_debug_init(struct ath6kl *ar)
|
|
|
|
|
|
ar->debugfs_phy = debugfs_create_dir("ath6kl",
|
|
|
ar->wiphy->debugfsdir);
|
|
|
- if (!ar->debugfs_phy) {
|
|
|
- vfree(ar->debug.fwlog_buf.buf);
|
|
|
- kfree(ar->debug.fwlog_tmp);
|
|
|
+ if (!ar->debugfs_phy)
|
|
|
return -ENOMEM;
|
|
|
- }
|
|
|
|
|
|
debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
|
|
|
&fops_tgt_stats);
|
|
@@ -1742,8 +1700,7 @@ int ath6kl_debug_init(struct ath6kl *ar)
|
|
|
|
|
|
void ath6kl_debug_cleanup(struct ath6kl *ar)
|
|
|
{
|
|
|
- vfree(ar->debug.fwlog_buf.buf);
|
|
|
- kfree(ar->debug.fwlog_tmp);
|
|
|
+ skb_queue_purge(&ar->debug.fwlog_queue);
|
|
|
kfree(ar->debug.roam_tbl);
|
|
|
}
|
|
|
|