aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKalle Valo <kvalo@qca.qualcomm.com>2011-09-02 03:32:04 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2011-09-02 03:32:04 -0400
commitbdf5396be177b689c00ae6ebed00d13fafaed36e (patch)
treeb185c185ab024aca98871ce06d4a3fbc37c4b5ab
parentd748753cd71f4504129fc6bd2262e0c5e4abe62f (diff)
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>
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h9
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c154
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.h13
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/target.h3
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c1
6 files changed, 181 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index cfbbad9feb9..319e768d9ad 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -21,6 +21,7 @@
21#include <linux/rtnetlink.h> 21#include <linux/rtnetlink.h>
22#include <linux/firmware.h> 22#include <linux/firmware.h>
23#include <linux/sched.h> 23#include <linux/sched.h>
24#include <linux/circ_buf.h>
24#include <net/cfg80211.h> 25#include <net/cfg80211.h>
25#include "htc.h" 26#include "htc.h"
26#include "wmi.h" 27#include "wmi.h"
@@ -467,6 +468,14 @@ struct ath6kl {
467 u32 send_action_id; 468 u32 send_action_id;
468 bool probe_req_report; 469 bool probe_req_report;
469 u16 next_chan; 470 u16 next_chan;
471
472#ifdef CONFIG_ATH6KL_DEBUG
473 struct {
474 struct circ_buf fwlog_buf;
475 spinlock_t fwlog_lock;
476 void *fwlog_tmp;
477 } debug;
478#endif /* CONFIG_ATH6KL_DEBUG */
470}; 479};
471 480
472static inline void *ath6kl_priv(struct net_device *dev) 481static 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 2b462876cec..b2706da5814 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -15,7 +15,23 @@
15 */ 15 */
16 16
17#include "core.h" 17#include "core.h"
18
19#include <linux/circ_buf.h>
20
18#include "debug.h" 21#include "debug.h"
22#include "target.h"
23
24struct ath6kl_fwlog_slot {
25 __le32 timestamp;
26 __le32 length;
27
28 /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
29 u8 payload[0];
30};
31
32#define ATH6KL_FWLOG_SIZE 32768
33#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
34 ATH6KL_FWLOG_PAYLOAD_SIZE)
19 35
20int ath6kl_printk(const char *level, const char *fmt, ...) 36int ath6kl_printk(const char *level, const char *fmt, ...)
21{ 37{
@@ -153,6 +169,117 @@ static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
153 return 0; 169 return 0;
154} 170}
155 171
172static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
173 size_t buf_len)
174{
175 struct circ_buf *fwlog = &ar->debug.fwlog_buf;
176 size_t space;
177 int i;
178
179 /* entries must all be equal size */
180 if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
181 return;
182
183 space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
184 if (space < buf_len)
185 /* discard oldest slot */
186 fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
187 (ATH6KL_FWLOG_SIZE - 1);
188
189 for (i = 0; i < buf_len; i += space) {
190 space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
191 ATH6KL_FWLOG_SIZE);
192
193 if ((size_t) space > buf_len - i)
194 space = buf_len - i;
195
196 memcpy(&fwlog->buf[fwlog->head], buf, space);
197 fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
198 }
199
200}
201
202void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
203{
204 struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
205 size_t slot_len;
206
207 if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
208 return;
209
210 spin_lock_bh(&ar->debug.fwlog_lock);
211
212 slot->timestamp = cpu_to_le32(jiffies);
213 slot->length = cpu_to_le32(len);
214 memcpy(slot->payload, buf, len);
215
216 slot_len = sizeof(*slot) + len;
217
218 if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
219 memset(slot->payload + len, 0,
220 ATH6KL_FWLOG_SLOT_SIZE - slot_len);
221
222 ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
223
224 spin_unlock_bh(&ar->debug.fwlog_lock);
225}
226
227static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
228{
229 return CIRC_CNT(ar->debug.fwlog_buf.head,
230 ar->debug.fwlog_buf.tail,
231 ATH6KL_FWLOG_SLOT_SIZE) == 0;
232}
233
234static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
235 size_t count, loff_t *ppos)
236{
237 struct ath6kl *ar = file->private_data;
238 struct circ_buf *fwlog = &ar->debug.fwlog_buf;
239 size_t len = 0, buf_len = count;
240 ssize_t ret_cnt;
241 char *buf;
242 int ccnt;
243
244 buf = vmalloc(buf_len);
245 if (!buf)
246 return -ENOMEM;
247
248 spin_lock_bh(&ar->debug.fwlog_lock);
249
250 while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
251 ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
252 ATH6KL_FWLOG_SIZE);
253
254 if ((size_t) ccnt > buf_len - len)
255 ccnt = buf_len - len;
256
257 memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
258 len += ccnt;
259
260 fwlog->tail = (fwlog->tail + ccnt) &
261 (ATH6KL_FWLOG_SIZE - 1);
262 }
263
264 spin_unlock_bh(&ar->debug.fwlog_lock);
265
266 if (WARN_ON(len > buf_len))
267 len = buf_len;
268
269 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
270
271 vfree(buf);
272
273 return ret_cnt;
274}
275
276static const struct file_operations fops_fwlog = {
277 .open = ath6kl_debugfs_open,
278 .read = ath6kl_fwlog_read,
279 .owner = THIS_MODULE,
280 .llseek = default_llseek,
281};
282
156static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, 283static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
157 size_t count, loff_t *ppos) 284 size_t count, loff_t *ppos)
158{ 285{
@@ -358,10 +485,25 @@ static const struct file_operations fops_credit_dist_stats = {
358 485
359int ath6kl_debug_init(struct ath6kl *ar) 486int ath6kl_debug_init(struct ath6kl *ar)
360{ 487{
488 ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
489 if (ar->debug.fwlog_buf.buf == NULL)
490 return -ENOMEM;
491
492 ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
493 if (ar->debug.fwlog_tmp == NULL) {
494 vfree(ar->debug.fwlog_buf.buf);
495 return -ENOMEM;
496 }
497
498 spin_lock_init(&ar->debug.fwlog_lock);
499
361 ar->debugfs_phy = debugfs_create_dir("ath6kl", 500 ar->debugfs_phy = debugfs_create_dir("ath6kl",
362 ar->wdev->wiphy->debugfsdir); 501 ar->wdev->wiphy->debugfsdir);
363 if (!ar->debugfs_phy) 502 if (!ar->debugfs_phy) {
503 vfree(ar->debug.fwlog_buf.buf);
504 kfree(ar->debug.fwlog_tmp);
364 return -ENOMEM; 505 return -ENOMEM;
506 }
365 507
366 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, 508 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
367 &fops_tgt_stats); 509 &fops_tgt_stats);
@@ -369,6 +511,16 @@ int ath6kl_debug_init(struct ath6kl *ar)
369 debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, 511 debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
370 &fops_credit_dist_stats); 512 &fops_credit_dist_stats);
371 513
514 debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
515 &fops_fwlog);
516
372 return 0; 517 return 0;
373} 518}
519
520void ath6kl_debug_cleanup(struct ath6kl *ar)
521{
522 vfree(ar->debug.fwlog_buf.buf);
523 kfree(ar->debug.fwlog_tmp);
524}
525
374#endif 526#endif
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index e8c9ea9ce02..f0d64711b41 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,
78 struct ath6kl_irq_proc_registers *irq_proc_reg, 78 struct ath6kl_irq_proc_registers *irq_proc_reg,
79 struct ath6kl_irq_enable_reg *irq_en_reg); 79 struct ath6kl_irq_enable_reg *irq_en_reg);
80void dump_cred_dist_stats(struct htc_target *target); 80void dump_cred_dist_stats(struct htc_target *target);
81void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
81int ath6kl_debug_init(struct ath6kl *ar); 82int ath6kl_debug_init(struct ath6kl *ar);
83void ath6kl_debug_cleanup(struct ath6kl *ar);
84
82#else 85#else
83static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, 86static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
84 const char *fmt, ...) 87 const char *fmt, ...)
@@ -101,9 +104,19 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
101static inline void dump_cred_dist_stats(struct htc_target *target) 104static inline void dump_cred_dist_stats(struct htc_target *target)
102{ 105{
103} 106}
107
108void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
109{
110}
111
104static inline int ath6kl_debug_init(struct ath6kl *ar) 112static inline int ath6kl_debug_init(struct ath6kl *ar)
105{ 113{
106 return 0; 114 return 0;
107} 115}
116
117void ath6kl_debug_cleanup(struct ath6kl *ar)
118{
119}
120
108#endif 121#endif
109#endif 122#endif
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 96953be5cd7..a638c3c9b79 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1389,6 +1389,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
1389 1389
1390 ath6kl_bmi_cleanup(ar); 1390 ath6kl_bmi_cleanup(ar);
1391 1391
1392 ath6kl_debug_cleanup(ar);
1393
1392 if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) { 1394 if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
1393 unregister_netdev(dev); 1395 unregister_netdev(dev);
1394 clear_bit(NETDEV_REGISTERED, &ar->flag); 1396 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 53e2c786f8e..6c66a08e179 100644
--- a/drivers/net/wireless/ath/ath6kl/target.h
+++ b/drivers/net/wireless/ath/ath6kl/target.h
@@ -340,4 +340,7 @@ struct host_interest {
340#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400 340#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400
341#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000 341#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000
342#define AR6004_REV1_RAM_RESERVE_SIZE 11264 342#define AR6004_REV1_RAM_RESERVE_SIZE 11264
343
344#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500
345
343#endif 346#endif
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index c34e36806da..954d5e18e88 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -2903,6 +2903,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
2903 case WMIX_HB_CHALLENGE_RESP_EVENTID: 2903 case WMIX_HB_CHALLENGE_RESP_EVENTID:
2904 break; 2904 break;
2905 case WMIX_DBGLOG_EVENTID: 2905 case WMIX_DBGLOG_EVENTID:
2906 ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len);
2906 break; 2907 break;
2907 default: 2908 default:
2908 ath6kl_err("unknown cmd id 0x%x\n", id); 2909 ath6kl_err("unknown cmd id 0x%x\n", id);