ath6kl: add firmware log support
Firmware sends binary logs with WMIX_DBGLOG_EVENTID event. Create a buffer which stores the latest logs and which can be copied from fwlog debugfs file with cp command. To save memory firmware log support is enabled only when CONFIG_ATH6KL_DEBUG is enabled. Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
d748753cd7
commit
bdf5396be1
@ -21,6 +21,7 @@
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "htc.h"
|
||||
#include "wmi.h"
|
||||
@ -467,6 +468,14 @@ struct ath6kl {
|
||||
u32 send_action_id;
|
||||
bool probe_req_report;
|
||||
u16 next_chan;
|
||||
|
||||
#ifdef CONFIG_ATH6KL_DEBUG
|
||||
struct {
|
||||
struct circ_buf fwlog_buf;
|
||||
spinlock_t fwlog_lock;
|
||||
void *fwlog_tmp;
|
||||
} debug;
|
||||
#endif /* CONFIG_ATH6KL_DEBUG */
|
||||
};
|
||||
|
||||
static inline void *ath6kl_priv(struct net_device *dev)
|
||||
|
@ -15,7 +15,23 @@
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "target.h"
|
||||
|
||||
struct ath6kl_fwlog_slot {
|
||||
__le32 timestamp;
|
||||
__le32 length;
|
||||
|
||||
/* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
|
||||
u8 payload[0];
|
||||
};
|
||||
|
||||
#define ATH6KL_FWLOG_SIZE 32768
|
||||
#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
|
||||
ATH6KL_FWLOG_PAYLOAD_SIZE)
|
||||
|
||||
int ath6kl_printk(const char *level, const char *fmt, ...)
|
||||
{
|
||||
@ -153,6 +169,117 @@ static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
size_t slot_len;
|
||||
|
||||
if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&ar->debug.fwlog_lock);
|
||||
|
||||
slot->timestamp = cpu_to_le32(jiffies);
|
||||
slot->length = cpu_to_le32(len);
|
||||
memcpy(slot->payload, buf, len);
|
||||
|
||||
slot_len = sizeof(*slot) + len;
|
||||
|
||||
if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
|
||||
memset(slot->payload + len, 0,
|
||||
ATH6KL_FWLOG_SLOT_SIZE - slot_len);
|
||||
|
||||
ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
|
||||
|
||||
spin_unlock_bh(&ar->debug.fwlog_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;
|
||||
}
|
||||
|
||||
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;
|
||||
ssize_t ret_cnt;
|
||||
char *buf;
|
||||
int ccnt;
|
||||
|
||||
buf = vmalloc(buf_len);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&ar->debug.fwlog_lock);
|
||||
|
||||
while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
|
||||
ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
|
||||
ATH6KL_FWLOG_SIZE);
|
||||
|
||||
if ((size_t) ccnt > buf_len - len)
|
||||
ccnt = buf_len - len;
|
||||
|
||||
memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
|
||||
len += ccnt;
|
||||
|
||||
fwlog->tail = (fwlog->tail + ccnt) &
|
||||
(ATH6KL_FWLOG_SIZE - 1);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&ar->debug.fwlog_lock);
|
||||
|
||||
if (WARN_ON(len > buf_len))
|
||||
len = buf_len;
|
||||
|
||||
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
|
||||
vfree(buf);
|
||||
|
||||
return ret_cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_fwlog = {
|
||||
.open = ath6kl_debugfs_open,
|
||||
.read = ath6kl_fwlog_read,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -358,10 +485,25 @@ static const struct file_operations fops_credit_dist_stats = {
|
||||
|
||||
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);
|
||||
|
||||
ar->debugfs_phy = debugfs_create_dir("ath6kl",
|
||||
ar->wdev->wiphy->debugfsdir);
|
||||
if (!ar->debugfs_phy)
|
||||
if (!ar->debugfs_phy) {
|
||||
vfree(ar->debug.fwlog_buf.buf);
|
||||
kfree(ar->debug.fwlog_tmp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
|
||||
&fops_tgt_stats);
|
||||
@ -369,6 +511,16 @@ int ath6kl_debug_init(struct ath6kl *ar)
|
||||
debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
|
||||
&fops_credit_dist_stats);
|
||||
|
||||
debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
|
||||
&fops_fwlog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ath6kl_debug_cleanup(struct ath6kl *ar)
|
||||
{
|
||||
vfree(ar->debug.fwlog_buf.buf);
|
||||
kfree(ar->debug.fwlog_tmp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -78,7 +78,10 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
|
||||
struct ath6kl_irq_proc_registers *irq_proc_reg,
|
||||
struct ath6kl_irq_enable_reg *irq_en_reg);
|
||||
void dump_cred_dist_stats(struct htc_target *target);
|
||||
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
|
||||
int ath6kl_debug_init(struct ath6kl *ar);
|
||||
void ath6kl_debug_cleanup(struct ath6kl *ar);
|
||||
|
||||
#else
|
||||
static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
|
||||
const char *fmt, ...)
|
||||
@ -101,9 +104,19 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
|
||||
static inline void dump_cred_dist_stats(struct htc_target *target)
|
||||
{
|
||||
}
|
||||
|
||||
void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ath6kl_debug_init(struct ath6kl *ar)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ath6kl_debug_cleanup(struct ath6kl *ar)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1389,6 +1389,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
|
||||
|
||||
ath6kl_bmi_cleanup(ar);
|
||||
|
||||
ath6kl_debug_cleanup(ar);
|
||||
|
||||
if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
|
||||
unregister_netdev(dev);
|
||||
clear_bit(NETDEV_REGISTERED, &ar->flag);
|
||||
|
@ -340,4 +340,7 @@ struct host_interest {
|
||||
#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400
|
||||
#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000
|
||||
#define AR6004_REV1_RAM_RESERVE_SIZE 11264
|
||||
|
||||
#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500
|
||||
|
||||
#endif
|
||||
|
@ -2903,6 +2903,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
|
||||
case WMIX_HB_CHALLENGE_RESP_EVENTID:
|
||||
break;
|
||||
case WMIX_DBGLOG_EVENTID:
|
||||
ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len);
|
||||
break;
|
||||
default:
|
||||
ath6kl_err("unknown cmd id 0x%x\n", id);
|
||||
|
Loading…
x
Reference in New Issue
Block a user