Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bdf5396b authored by Kalle Valo's avatar Kalle Valo
Browse files

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: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent d748753c
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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)
+153 −1
Original line number Diff line number Diff line
@@ -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
+13 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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
Loading