aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath6kl/debug.c
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 /drivers/net/wireless/ath/ath6kl/debug.c
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>
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/debug.c')
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c154
1 files changed, 153 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index 2b462876cec1..b2706da58149 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