All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] ath6kl: firmware log support
@ 2011-08-31 11:09 Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 1/4] ath6kl: add " Kalle Valo
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Kalle Valo @ 2011-08-31 11:09 UTC (permalink / raw)
  To: linux-wireless

These patches make possible to get firmware logs from hardware.

v2:

o fix ath6kl_fwlog_mask_write(), use kstrtou32_from_user()
o don't use ret uninitialised in ath6kl_diag_write()

---

Kalle Valo (4):
      ath6kl: add firmware log support
      ath6kl: implement support to set firmware log parameters
      ath6kl: cleanup diagnose window read and write functions
      ath6kl: read fwlog from firmware ring buffer


 drivers/net/wireless/ath/ath6kl/core.h   |   32 ++++-
 drivers/net/wireless/ath/ath6kl/debug.c  |  208 ++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath6kl/debug.h  |   13 ++
 drivers/net/wireless/ath/ath6kl/init.c   |   31 +---
 drivers/net/wireless/ath/ath6kl/main.c   |  169 ++++++++++++++++++------
 drivers/net/wireless/ath/ath6kl/target.h |   17 ++
 drivers/net/wireless/ath/ath6kl/wmi.c    |   20 +++
 drivers/net/wireless/ath/ath6kl/wmi.h    |    6 +
 8 files changed, 427 insertions(+), 69 deletions(-)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/4] ath6kl: add firmware log support
  2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
@ 2011-08-31 11:09 ` Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 2/4] ath6kl: implement support to set firmware log parameters Kalle Valo
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2011-08-31 11:09 UTC (permalink / raw)
  To: linux-wireless

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>
---
 drivers/net/wireless/ath/ath6kl/core.h   |   10 ++
 drivers/net/wireless/ath/ath6kl/debug.c  |  154 ++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath6kl/debug.h  |   13 +++
 drivers/net/wireless/ath/ath6kl/init.c   |    2 
 drivers/net/wireless/ath/ath6kl/target.h |    3 +
 drivers/net/wireless/ath/ath6kl/wmi.c    |    1 
 6 files changed, 182 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index c5537b3..89ca343 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -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"
@@ -468,6 +469,15 @@ struct ath6kl {
 
 	struct ath6kl_node_table scan_table;
 	struct dentry *debugfs_phy;
+
+#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)
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index 2b46287..b2706da 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -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
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index e8c9ea9..f0d6471 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -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
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index ad9716c..bcb94ab 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1353,6 +1353,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);
diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h
index 53e2c78..6c66a08 100644
--- a/drivers/net/wireless/ath/ath6kl/target.h
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -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
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index d116d0e..b96a734 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -2482,6 +2482,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);


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/4] ath6kl: implement support to set firmware log parameters
  2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 1/4] ath6kl: add " Kalle Valo
@ 2011-08-31 11:09 ` Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 3/4] ath6kl: cleanup diagnose window read and write functions Kalle Valo
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2011-08-31 11:09 UTC (permalink / raw)
  To: linux-wireless

Firmware log parameters can be controlled now with help of fwlog_mask
debugfs file.

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath6kl/core.h  |    1 +
 drivers/net/wireless/ath/ath6kl/debug.c |   51 +++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath6kl/wmi.c   |   19 ++++++++++++
 drivers/net/wireless/ath/ath6kl/wmi.h   |    6 ++++
 4 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index 89ca343..d5c21bf 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -475,6 +475,7 @@ struct ath6kl {
 		struct circ_buf fwlog_buf;
 		spinlock_t fwlog_lock;
 		void *fwlog_tmp;
+		u32 fwlog_mask;
 	} debug;
 #endif /* CONFIG_ATH6KL_DEBUG */
 
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index b2706da..239c092 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -17,6 +17,7 @@
 #include "core.h"
 
 #include <linux/circ_buf.h>
+#include <linux/fs.h>
 
 #include "debug.h"
 #include "target.h"
@@ -32,6 +33,7 @@ struct ath6kl_fwlog_slot {
 #define ATH6KL_FWLOG_SIZE 32768
 #define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
 				ATH6KL_FWLOG_PAYLOAD_SIZE)
+#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
 
 int ath6kl_printk(const char *level, const char *fmt, ...)
 {
@@ -280,6 +282,46 @@ static const struct file_operations fops_fwlog = {
 	.llseek = default_llseek,
 };
 
+static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath6kl *ar = file->private_data;
+	char buf[16];
+	int len;
+
+	len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_fwlog_mask_write(struct file *file,
+				       const char __user *user_buf,
+				       size_t count, loff_t *ppos)
+{
+	struct ath6kl *ar = file->private_data;
+	int ret;
+
+	ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
+	if (ret)
+		return ret;
+
+	ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
+						 ATH6KL_FWLOG_VALID_MASK,
+						 ar->debug.fwlog_mask);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct file_operations fops_fwlog_mask = {
+	.open = ath6kl_debugfs_open,
+	.read = ath6kl_fwlog_mask_read,
+	.write = ath6kl_fwlog_mask_write,
+	.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)
 {
@@ -497,6 +539,12 @@ int ath6kl_debug_init(struct ath6kl *ar)
 
 	spin_lock_init(&ar->debug.fwlog_lock);
 
+	/*
+	 * Actually we are lying here but don't know how to read the mask
+	 * value from the firmware.
+	 */
+	ar->debug.fwlog_mask = 0;
+
 	ar->debugfs_phy = debugfs_create_dir("ath6kl",
 					     ar->wdev->wiphy->debugfsdir);
 	if (!ar->debugfs_phy) {
@@ -514,6 +562,9 @@ int ath6kl_debug_init(struct ath6kl *ar)
 	debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
 			    &fops_fwlog);
 
+	debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+			    ar, &fops_fwlog_mask);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index b96a734..fc40009 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -2203,6 +2203,25 @@ int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source)
 	return ret;
 }
 
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config)
+{
+	struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd;
+	struct sk_buff *skb;
+	int ret;
+
+	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data;
+	cmd->valid = cpu_to_le32(valid);
+	cmd->config = cpu_to_le32(config);
+
+	ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID,
+				       NO_SYNC_WMIFLAG);
+	return ret;
+}
+
 int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
 {
 	return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index 8fa5d6e..240e64c 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -1898,6 +1898,11 @@ struct wmix_hb_challenge_resp_cmd {
 	__le32 source;
 } __packed;
 
+struct ath6kl_wmix_dbglog_cfg_module_cmd {
+	__le32 valid;
+	__le32 config;
+} __packed;
+
 /* End of Extended WMI (WMIX) */
 
 enum wmi_sync_flag {
@@ -1977,6 +1982,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
 				 u8 preamble_policy);
 
 int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config);
 
 int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
 int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 3/4] ath6kl: cleanup diagnose window read and write functions
  2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 1/4] ath6kl: add " Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 2/4] ath6kl: implement support to set firmware log parameters Kalle Valo
@ 2011-08-31 11:09 ` Kalle Valo
  2011-08-31 11:09 ` [PATCH v2 4/4] ath6kl: read fwlog from firmware ring buffer Kalle Valo
  2011-09-02  7:38 ` [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
  4 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2011-08-31 11:09 UTC (permalink / raw)
  To: linux-wireless

Just to make them a bit easier to read and unify naming. 32 suffix
in the function name means that it will be a 32 bit transfer. If there's
no number a buffer is transfered instead.

Use void pointers to get rid of ugly casts.

Don't provide target address as a pointer, pass it by value. Same for
the value used in write32().

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath6kl/core.h |    6 +-
 drivers/net/wireless/ath/ath6kl/init.c |   16 ++---
 drivers/net/wireless/ath/ath6kl/main.c |   94 ++++++++++++++++++--------------
 3 files changed, 61 insertions(+), 55 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index d5c21bf..33c84c0 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -509,9 +509,9 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
 					       struct htc_packet *packet);
 void ath6kl_stop_txrx(struct ath6kl *ar);
 void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
-int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
-			   u8 *data, u32 length, bool read);
-int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data);
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
 void ath6kl_init_profile_info(struct ath6kl *ar);
 void ath6kl_tx_data_cleanup(struct ath6kl *ar);
 void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index bcb94ab..b6a1e69 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -131,14 +131,13 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar)
 	address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
 	address = TARG_VTOP(ar->target_type, address);
 
-	if (ath6kl_read_reg_diag(ar, &address, &data))
+	if (ath6kl_diag_read32(ar, address, &data))
 		return -EIO;
 
 	address = TARG_VTOP(ar->target_type, data);
 	host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
-	if (ath6kl_access_datadiag(ar, address,
-				(u8 *)&host_app_area,
-				sizeof(struct host_app_area), false))
+	if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,
+			      sizeof(struct host_app_area)))
 		return -EIO;
 
 	return 0;
@@ -375,7 +374,7 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
 	address = TARG_VTOP(ar->target_type, address);
 
 	/* read RAM location through diagnostic window */
-	status = ath6kl_read_reg_diag(ar, &address, &regdump_loc);
+	status = ath6kl_diag_read32(ar, address, &regdump_loc);
 
 	if (status || !regdump_loc) {
 		ath6kl_err("failed to get ptr to register dump area\n");
@@ -387,11 +386,8 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
 	regdump_loc = TARG_VTOP(ar->target_type, regdump_loc);
 
 	/* fetch register dump data */
-	status = ath6kl_access_datadiag(ar,
-					regdump_loc,
-					(u8 *)&regdump_val[0],
-					REG_DUMP_COUNT_AR6003 * (sizeof(u32)),
-					true);
+	status = ath6kl_diag_read(ar, regdump_loc, (u8 *)&regdump_val[0],
+				  REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
 
 	if (status) {
 		ath6kl_err("failed to get register dump\n");
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index b64b2a3..f881530 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -228,74 +228,84 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
 }
 
 /*
- * Read from the ATH6KL through its diagnostic window. No cooperation from
- * the Target is required for this.
+ * Read from the hardware through its diagnostic window. No cooperation
+ * from the firmware is required for this.
  */
-int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data)
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
 {
-	int status;
+	int ret;
 
 	/* set window register to start read cycle */
-	status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS,
-					*address);
-
-	if (status)
-		return status;
+	ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
+	if (ret)
+		return ret;
 
 	/* read the data */
-	status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
-				     sizeof(u32), HIF_RD_SYNC_BYTE_INC);
-	if (status) {
-		ath6kl_err("failed to read from window data addr\n");
-		return status;
+	ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
+				  sizeof(*value), HIF_RD_SYNC_BYTE_INC);
+	if (ret) {
+		ath6kl_warn("failed to read32 through diagnose window: %d\n",
+			    ret);
+		return ret;
 	}
 
-	return status;
+	return 0;
 }
 
-
 /*
  * Write to the ATH6KL through its diagnostic window. No cooperation from
  * the Target is required for this.
  */
-static int ath6kl_write_reg_diag(struct ath6kl *ar, u32 *address, u32 *data)
+static int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value)
 {
-	int status;
+	int ret;
 
 	/* set write data */
-	status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
-				     sizeof(u32), HIF_WR_SYNC_BYTE_INC);
-	if (status) {
-		ath6kl_err("failed to write 0x%x to window data addr\n", *data);
-		return status;
+	ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
+				  sizeof(value), HIF_WR_SYNC_BYTE_INC);
+	if (ret) {
+		ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
+			   address, value);
+		return ret;
 	}
 
 	/* set window register, which starts the write cycle */
 	return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
-				      *address);
+				      address);
 }
 
-int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
-			   u8 *data, u32 length, bool read)
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
 {
-	u32 count;
-	int status = 0;
+	u32 count, *buf = data;
+	int ret;
 
-	for (count = 0; count < length; count += 4, address += 4) {
-		if (read) {
-			status = ath6kl_read_reg_diag(ar, &address,
-						      (u32 *) &data[count]);
-			if (status)
-				break;
-		} else {
-			status = ath6kl_write_reg_diag(ar, &address,
-						       (u32 *) &data[count]);
-			if (status)
-				break;
-		}
+	if (WARN_ON(length % 4))
+		return -EINVAL;
+
+	for (count = 0; count < length / 4; count++, address += 4) {
+		ret = ath6kl_diag_read32(ar, address, &buf[count]);
+		if (ret)
+			return ret;
 	}
 
-	return status;
+	return 0;
+}
+
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
+{
+	u32 count, *buf = data;
+	int ret;
+
+	if (WARN_ON(length % 4))
+		return -EINVAL;
+
+	for (count = 0; count < length / 4; count++, address += 4) {
+		ret = ath6kl_diag_write32(ar, address, buf[count]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 /* FIXME: move to a better place, target.h? */
@@ -327,7 +337,7 @@ static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
 		break;
 	}
 
-	status = ath6kl_write_reg_diag(ar, &address, &data);
+	status = ath6kl_diag_write32(ar, address, data);
 
 	if (status)
 		ath6kl_err("failed to reset target\n");


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 4/4] ath6kl: read fwlog from firmware ring buffer
  2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
                   ` (2 preceding siblings ...)
  2011-08-31 11:09 ` [PATCH v2 3/4] ath6kl: cleanup diagnose window read and write functions Kalle Valo
@ 2011-08-31 11:09 ` Kalle Valo
  2011-09-02  7:38 ` [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
  4 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2011-08-31 11:09 UTC (permalink / raw)
  To: linux-wireless

Firmare sends the logs only when it's internal ring buffer is full. But
if firmware crashes we need to retrieve the latest logs through diagnose
window. This is now done everytime the debugfs file is read.

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath6kl/core.h   |   15 ++++++
 drivers/net/wireless/ath/ath6kl/debug.c  |    3 +
 drivers/net/wireless/ath/ath6kl/init.c   |   13 -----
 drivers/net/wireless/ath/ath6kl/main.c   |   75 ++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath6kl/target.h |   14 ++++++
 5 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index 33c84c0..61d3142 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -26,6 +26,7 @@
 #include "htc.h"
 #include "wmi.h"
 #include "bmi.h"
+#include "target.h"
 
 #define MAX_ATH6KL                        1
 #define ATH6KL_MAX_RX_BUFFERS             16
@@ -496,6 +497,19 @@ static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
 	cred_info->cur_free_credits -= credits;
 }
 
+static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
+					  u32 item_offset)
+{
+	u32 addr = 0;
+
+	if (ar->target_type == TARGET_TYPE_AR6003)
+		addr = ATH6KL_AR6003_HI_START_ADDR + item_offset;
+	else if (ar->target_type == TARGET_TYPE_AR6004)
+		addr = ATH6KL_AR6004_HI_START_ADDR + item_offset;
+
+	return addr;
+}
+
 void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
 int ath6kl_configure_target(struct ath6kl *ar);
 void ath6kl_detect_error(unsigned long ptr);
@@ -512,6 +526,7 @@ void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
 int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
 int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
 int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_read_fwlogs(struct ath6kl *ar);
 void ath6kl_init_profile_info(struct ath6kl *ar);
 void ath6kl_tx_data_cleanup(struct ath6kl *ar);
 void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index 239c092..87de44d 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -247,6 +247,9 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
 	if (!buf)
 		return -ENOMEM;
 
+	/* read undelivered logs from firmware */
+	ath6kl_read_fwlogs(ar);
+
 	spin_lock_bh(&ar->debug.fwlog_lock);
 
 	while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index b6a1e69..428b9c0 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -108,19 +108,6 @@ static u8 ath6kl_get_fw_iftype(struct ath6kl *ar)
 	}
 }
 
-static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
-					  u32 item_offset)
-{
-	u32 addr = 0;
-
-	if (ar->target_type == TARGET_TYPE_AR6003)
-		addr = ATH6KL_AR6003_HI_START_ADDR + item_offset;
-	else if (ar->target_type == TARGET_TYPE_AR6004)
-		addr = ATH6KL_AR6004_HI_START_ADDR + item_offset;
-
-	return addr;
-}
-
 static int ath6kl_set_host_app_area(struct ath6kl *ar)
 {
 	u32 address, data;
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index f881530..761c6a1 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -308,6 +308,81 @@ int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
 	return 0;
 }
 
+int ath6kl_read_fwlogs(struct ath6kl *ar)
+{
+	struct ath6kl_dbglog_hdr debug_hdr;
+	struct ath6kl_dbglog_buf debug_buf;
+	u32 address, length, dropped, firstbuf, debug_hdr_addr;
+	int ret = 0, loop;
+	u8 *buf;
+
+	buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	address = TARG_VTOP(ar->target_type,
+			    ath6kl_get_hi_item_addr(ar,
+						    HI_ITEM(hi_dbglog_hdr)));
+
+	ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
+	if (ret)
+		goto out;
+
+	/* Get the contents of the ring buffer */
+	if (debug_hdr_addr == 0) {
+		ath6kl_warn("Invalid address for debug_hdr_addr\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	address = TARG_VTOP(ar->target_type, debug_hdr_addr);
+	ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
+
+	address = TARG_VTOP(ar->target_type,
+			    le32_to_cpu(debug_hdr.dbuf_addr));
+	firstbuf = address;
+	dropped = le32_to_cpu(debug_hdr.dropped);
+	ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+
+	loop = 100;
+
+	do {
+		address = TARG_VTOP(ar->target_type,
+				    le32_to_cpu(debug_buf.buffer_addr));
+		length = le32_to_cpu(debug_buf.length);
+
+		if (length != 0 && (le32_to_cpu(debug_buf.length) <=
+				    le32_to_cpu(debug_buf.bufsize))) {
+			length = ALIGN(length, 4);
+
+			ret = ath6kl_diag_read(ar, address,
+					       buf, length);
+			if (ret)
+				goto out;
+
+			ath6kl_debug_fwlog_event(ar, buf, length);
+		}
+
+		address = TARG_VTOP(ar->target_type,
+				    le32_to_cpu(debug_buf.next));
+		ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+		if (ret)
+			goto out;
+
+		loop--;
+
+		if (WARN_ON(loop == 0)) {
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+	} while (address != firstbuf);
+
+out:
+	kfree(buf);
+
+	return ret;
+}
+
 /* FIXME: move to a better place, target.h? */
 #define AR6003_RESET_CONTROL_ADDRESS 0x00004000
 #define AR6004_RESET_CONTROL_ADDRESS 0x00004000
diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h
index 6c66a08..dd8b953 100644
--- a/drivers/net/wireless/ath/ath6kl/target.h
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -343,4 +343,18 @@ struct host_interest {
 
 #define ATH6KL_FWLOG_PAYLOAD_SIZE		1500
 
+struct ath6kl_dbglog_buf {
+	__le32 next;
+	__le32 buffer_addr;
+	__le32 bufsize;
+	__le32 length;
+	__le32 count;
+	__le32 free;
+} __packed;
+
+struct ath6kl_dbglog_hdr {
+	__le32 dbuf_addr;
+	__le32 dropped;
+} __packed;
+
 #endif


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 0/4] ath6kl: firmware log support
  2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
                   ` (3 preceding siblings ...)
  2011-08-31 11:09 ` [PATCH v2 4/4] ath6kl: read fwlog from firmware ring buffer Kalle Valo
@ 2011-09-02  7:38 ` Kalle Valo
  4 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2011-09-02  7:38 UTC (permalink / raw)
  To: Kalle Valo; +Cc: linux-wireless

On 08/31/2011 02:09 PM, Kalle Valo wrote:
> These patches make possible to get firmware logs from hardware.

All four applied to my local ath6kl.git. (I really hope
master.kernel.org comes back soon)

Kalle

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-09-02  7:38 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-31 11:09 [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo
2011-08-31 11:09 ` [PATCH v2 1/4] ath6kl: add " Kalle Valo
2011-08-31 11:09 ` [PATCH v2 2/4] ath6kl: implement support to set firmware log parameters Kalle Valo
2011-08-31 11:09 ` [PATCH v2 3/4] ath6kl: cleanup diagnose window read and write functions Kalle Valo
2011-08-31 11:09 ` [PATCH v2 4/4] ath6kl: read fwlog from firmware ring buffer Kalle Valo
2011-09-02  7:38 ` [PATCH v2 0/4] ath6kl: firmware log support Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.