1 | /* |
2 | * Copyright (c) 2004-2011 Atheros Communications Inc. |
3 | * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | */ |
17 | |
18 | #include "core.h" |
19 | |
20 | #include <linux/skbuff.h> |
21 | #include <linux/fs.h> |
22 | #include <linux/vmalloc.h> |
23 | #include <linux/export.h> |
24 | |
25 | #include "debug.h" |
26 | #include "target.h" |
27 | |
28 | struct ath6kl_fwlog_slot { |
29 | __le32 timestamp; |
30 | __le32 length; |
31 | |
32 | /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ |
33 | u8 payload[]; |
34 | }; |
35 | |
36 | #define ATH6KL_FWLOG_MAX_ENTRIES 20 |
37 | |
38 | #define ATH6KL_FWLOG_VALID_MASK 0x1ffff |
39 | |
40 | void ath6kl_printk(const char *level, const char *fmt, ...) |
41 | { |
42 | struct va_format vaf; |
43 | va_list args; |
44 | |
45 | va_start(args, fmt); |
46 | |
47 | vaf.fmt = fmt; |
48 | vaf.va = &args; |
49 | |
50 | printk("%sath6kl: %pV" , level, &vaf); |
51 | |
52 | va_end(args); |
53 | } |
54 | EXPORT_SYMBOL(ath6kl_printk); |
55 | |
56 | void ath6kl_info(const char *fmt, ...) |
57 | { |
58 | struct va_format vaf = { |
59 | .fmt = fmt, |
60 | }; |
61 | va_list args; |
62 | |
63 | va_start(args, fmt); |
64 | vaf.va = &args; |
65 | ath6kl_printk(KERN_INFO, "%pV" , &vaf); |
66 | trace_ath6kl_log_info(vaf: &vaf); |
67 | va_end(args); |
68 | } |
69 | EXPORT_SYMBOL(ath6kl_info); |
70 | |
71 | void ath6kl_err(const char *fmt, ...) |
72 | { |
73 | struct va_format vaf = { |
74 | .fmt = fmt, |
75 | }; |
76 | va_list args; |
77 | |
78 | va_start(args, fmt); |
79 | vaf.va = &args; |
80 | ath6kl_printk(KERN_ERR, "%pV" , &vaf); |
81 | trace_ath6kl_log_err(vaf: &vaf); |
82 | va_end(args); |
83 | } |
84 | EXPORT_SYMBOL(ath6kl_err); |
85 | |
86 | void ath6kl_warn(const char *fmt, ...) |
87 | { |
88 | struct va_format vaf = { |
89 | .fmt = fmt, |
90 | }; |
91 | va_list args; |
92 | |
93 | va_start(args, fmt); |
94 | vaf.va = &args; |
95 | ath6kl_printk(KERN_WARNING, "%pV" , &vaf); |
96 | trace_ath6kl_log_warn(vaf: &vaf); |
97 | va_end(args); |
98 | } |
99 | EXPORT_SYMBOL(ath6kl_warn); |
100 | |
101 | int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif) |
102 | { |
103 | long left; |
104 | |
105 | if (down_interruptible(sem: &ar->sem)) |
106 | return -EBUSY; |
107 | |
108 | set_bit(nr: STATS_UPDATE_PEND, addr: &vif->flags); |
109 | |
110 | if (ath6kl_wmi_get_stats_cmd(wmi: ar->wmi, if_idx: 0)) { |
111 | up(sem: &ar->sem); |
112 | return -EIO; |
113 | } |
114 | |
115 | left = wait_event_interruptible_timeout(ar->event_wq, |
116 | !test_bit(STATS_UPDATE_PEND, |
117 | &vif->flags), WMI_TIMEOUT); |
118 | |
119 | up(sem: &ar->sem); |
120 | |
121 | if (left <= 0) |
122 | return -ETIMEDOUT; |
123 | |
124 | return 0; |
125 | } |
126 | EXPORT_SYMBOL(ath6kl_read_tgt_stats); |
127 | |
128 | #ifdef CONFIG_ATH6KL_DEBUG |
129 | |
130 | void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) |
131 | { |
132 | struct va_format vaf; |
133 | va_list args; |
134 | |
135 | va_start(args, fmt); |
136 | |
137 | vaf.fmt = fmt; |
138 | vaf.va = &args; |
139 | |
140 | if (debug_mask & mask) |
141 | ath6kl_printk(KERN_DEBUG, "%pV" , &vaf); |
142 | |
143 | trace_ath6kl_log_dbg(level: mask, vaf: &vaf); |
144 | |
145 | va_end(args); |
146 | } |
147 | EXPORT_SYMBOL(ath6kl_dbg); |
148 | |
149 | void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, |
150 | const char *msg, const char *prefix, |
151 | const void *buf, size_t len) |
152 | { |
153 | if (debug_mask & mask) { |
154 | if (msg) |
155 | ath6kl_dbg(mask, "%s\n" , msg); |
156 | |
157 | print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); |
158 | } |
159 | |
160 | /* tracing code doesn't like null strings :/ */ |
161 | trace_ath6kl_log_dbg_dump(msg: msg ? msg : "" , prefix: prefix ? prefix : "" , |
162 | buf, buf_len: len); |
163 | } |
164 | EXPORT_SYMBOL(ath6kl_dbg_dump); |
165 | |
166 | #define REG_OUTPUT_LEN_PER_LINE 25 |
167 | #define REGTYPE_STR_LEN 100 |
168 | |
169 | struct ath6kl_diag_reg_info { |
170 | u32 reg_start; |
171 | u32 reg_end; |
172 | const char *reg_info; |
173 | }; |
174 | |
175 | static const struct ath6kl_diag_reg_info diag_reg[] = { |
176 | { 0x20000, 0x200fc, "General DMA and Rx registers" }, |
177 | { 0x28000, 0x28900, "MAC PCU register & keycache" }, |
178 | { 0x20800, 0x20a40, "QCU" }, |
179 | { 0x21000, 0x212f0, "DCU" }, |
180 | { 0x4000, 0x42e4, "RTC" }, |
181 | { 0x540000, 0x540000 + (256 * 1024), "RAM" }, |
182 | { 0x29800, 0x2B210, "Base Band" }, |
183 | { 0x1C000, 0x1C748, "Analog" }, |
184 | }; |
185 | |
186 | void ath6kl_dump_registers(struct ath6kl_device *dev, |
187 | struct ath6kl_irq_proc_registers *irq_proc_reg, |
188 | struct ath6kl_irq_enable_reg *irq_enable_reg) |
189 | { |
190 | ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n" )); |
191 | |
192 | if (irq_proc_reg != NULL) { |
193 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
194 | "Host Int status: 0x%x\n" , |
195 | irq_proc_reg->host_int_status); |
196 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
197 | "CPU Int status: 0x%x\n" , |
198 | irq_proc_reg->cpu_int_status); |
199 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
200 | "Error Int status: 0x%x\n" , |
201 | irq_proc_reg->error_int_status); |
202 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
203 | "Counter Int status: 0x%x\n" , |
204 | irq_proc_reg->counter_int_status); |
205 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
206 | "Mbox Frame: 0x%x\n" , |
207 | irq_proc_reg->mbox_frame); |
208 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
209 | "Rx Lookahead Valid: 0x%x\n" , |
210 | irq_proc_reg->rx_lkahd_valid); |
211 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
212 | "Rx Lookahead 0: 0x%x\n" , |
213 | irq_proc_reg->rx_lkahd[0]); |
214 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
215 | "Rx Lookahead 1: 0x%x\n" , |
216 | irq_proc_reg->rx_lkahd[1]); |
217 | |
218 | if (dev->ar->mbox_info.gmbox_addr != 0) { |
219 | /* |
220 | * If the target supports GMBOX hardware, dump some |
221 | * additional state. |
222 | */ |
223 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
224 | "GMBOX Host Int status 2: 0x%x\n" , |
225 | irq_proc_reg->host_int_status2); |
226 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
227 | "GMBOX RX Avail: 0x%x\n" , |
228 | irq_proc_reg->gmbox_rx_avail); |
229 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
230 | "GMBOX lookahead alias 0: 0x%x\n" , |
231 | irq_proc_reg->rx_gmbox_lkahd_alias[0]); |
232 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
233 | "GMBOX lookahead alias 1: 0x%x\n" , |
234 | irq_proc_reg->rx_gmbox_lkahd_alias[1]); |
235 | } |
236 | } |
237 | |
238 | if (irq_enable_reg != NULL) { |
239 | ath6kl_dbg(ATH6KL_DBG_IRQ, |
240 | "Int status Enable: 0x%x\n" , |
241 | irq_enable_reg->int_status_en); |
242 | ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n" , |
243 | irq_enable_reg->cntr_int_status_en); |
244 | } |
245 | ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n" ); |
246 | } |
247 | |
248 | static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) |
249 | { |
250 | ath6kl_dbg(ATH6KL_DBG_CREDIT, |
251 | "--- endpoint: %d svc_id: 0x%X ---\n" , |
252 | ep_dist->endpoint, ep_dist->svc_id); |
253 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n" , |
254 | ep_dist->dist_flags); |
255 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n" , |
256 | ep_dist->cred_norm); |
257 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n" , |
258 | ep_dist->cred_min); |
259 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n" , |
260 | ep_dist->credits); |
261 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n" , |
262 | ep_dist->cred_assngd); |
263 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n" , |
264 | ep_dist->seek_cred); |
265 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n" , |
266 | ep_dist->cred_sz); |
267 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n" , |
268 | ep_dist->cred_per_msg); |
269 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_to_dist : %d\n" , |
270 | ep_dist->cred_to_dist); |
271 | ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n" , |
272 | get_queue_depth(queue: &ep_dist->htc_ep->txq)); |
273 | ath6kl_dbg(ATH6KL_DBG_CREDIT, |
274 | "----------------------------------\n" ); |
275 | } |
276 | |
277 | /* FIXME: move to htc.c */ |
278 | void dump_cred_dist_stats(struct htc_target *target) |
279 | { |
280 | struct htc_endpoint_credit_dist *ep_list; |
281 | |
282 | list_for_each_entry(ep_list, &target->cred_dist_list, list) |
283 | dump_cred_dist(ep_dist: ep_list); |
284 | |
285 | ath6kl_dbg(ATH6KL_DBG_CREDIT, |
286 | "credit distribution total %d free %d\n" , |
287 | target->credit_info->total_avail_credits, |
288 | target->credit_info->cur_free_credits); |
289 | } |
290 | |
291 | void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) |
292 | { |
293 | switch (war) { |
294 | case ATH6KL_WAR_INVALID_RATE: |
295 | ar->debug.war_stats.invalid_rate++; |
296 | break; |
297 | } |
298 | } |
299 | |
300 | static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, |
301 | size_t count, loff_t *ppos) |
302 | { |
303 | struct ath6kl *ar = file->private_data; |
304 | char *buf; |
305 | unsigned int len = 0, buf_len = 1500; |
306 | ssize_t ret_cnt; |
307 | |
308 | buf = kzalloc(size: buf_len, GFP_KERNEL); |
309 | if (!buf) |
310 | return -ENOMEM; |
311 | |
312 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "\n" ); |
313 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
314 | "Workaround stats" ); |
315 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n\n" , |
316 | "=================" ); |
317 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10u\n" , |
318 | "Invalid rates" , ar->debug.war_stats.invalid_rate); |
319 | |
320 | if (WARN_ON(len > buf_len)) |
321 | len = buf_len; |
322 | |
323 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
324 | |
325 | kfree(objp: buf); |
326 | return ret_cnt; |
327 | } |
328 | |
329 | static const struct file_operations fops_war_stats = { |
330 | .read = read_file_war_stats, |
331 | .open = simple_open, |
332 | .owner = THIS_MODULE, |
333 | .llseek = default_llseek, |
334 | }; |
335 | |
336 | void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) |
337 | { |
338 | struct ath6kl_fwlog_slot *slot; |
339 | struct sk_buff *skb; |
340 | size_t slot_len; |
341 | |
342 | if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) |
343 | return; |
344 | |
345 | slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE; |
346 | |
347 | skb = alloc_skb(size: slot_len, GFP_KERNEL); |
348 | if (!skb) |
349 | return; |
350 | |
351 | slot = skb_put(skb, len: slot_len); |
352 | slot->timestamp = cpu_to_le32(jiffies); |
353 | slot->length = cpu_to_le32(len); |
354 | memcpy(slot->payload, buf, len); |
355 | |
356 | /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */ |
357 | memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len); |
358 | |
359 | spin_lock(lock: &ar->debug.fwlog_queue.lock); |
360 | |
361 | __skb_queue_tail(list: &ar->debug.fwlog_queue, newsk: skb); |
362 | complete(&ar->debug.fwlog_completion); |
363 | |
364 | /* drop oldest entries */ |
365 | while (skb_queue_len(list_: &ar->debug.fwlog_queue) > |
366 | ATH6KL_FWLOG_MAX_ENTRIES) { |
367 | skb = __skb_dequeue(list: &ar->debug.fwlog_queue); |
368 | kfree_skb(skb); |
369 | } |
370 | |
371 | spin_unlock(lock: &ar->debug.fwlog_queue.lock); |
372 | |
373 | return; |
374 | } |
375 | |
376 | static int ath6kl_fwlog_open(struct inode *inode, struct file *file) |
377 | { |
378 | struct ath6kl *ar = inode->i_private; |
379 | |
380 | if (ar->debug.fwlog_open) |
381 | return -EBUSY; |
382 | |
383 | ar->debug.fwlog_open = true; |
384 | |
385 | file->private_data = inode->i_private; |
386 | return 0; |
387 | } |
388 | |
389 | static int ath6kl_fwlog_release(struct inode *inode, struct file *file) |
390 | { |
391 | struct ath6kl *ar = inode->i_private; |
392 | |
393 | ar->debug.fwlog_open = false; |
394 | |
395 | return 0; |
396 | } |
397 | |
398 | static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, |
399 | size_t count, loff_t *ppos) |
400 | { |
401 | struct ath6kl *ar = file->private_data; |
402 | struct sk_buff *skb; |
403 | ssize_t ret_cnt; |
404 | size_t len = 0; |
405 | char *buf; |
406 | |
407 | buf = vmalloc(size: count); |
408 | if (!buf) |
409 | return -ENOMEM; |
410 | |
411 | /* read undelivered logs from firmware */ |
412 | ath6kl_read_fwlogs(ar); |
413 | |
414 | spin_lock(lock: &ar->debug.fwlog_queue.lock); |
415 | |
416 | while ((skb = __skb_dequeue(list: &ar->debug.fwlog_queue))) { |
417 | if (skb->len > count - len) { |
418 | /* not enough space, put skb back and leave */ |
419 | __skb_queue_head(list: &ar->debug.fwlog_queue, newsk: skb); |
420 | break; |
421 | } |
422 | |
423 | |
424 | memcpy(buf + len, skb->data, skb->len); |
425 | len += skb->len; |
426 | |
427 | kfree_skb(skb); |
428 | } |
429 | |
430 | spin_unlock(lock: &ar->debug.fwlog_queue.lock); |
431 | |
432 | /* FIXME: what to do if len == 0? */ |
433 | |
434 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
435 | |
436 | vfree(addr: buf); |
437 | |
438 | return ret_cnt; |
439 | } |
440 | |
441 | static const struct file_operations fops_fwlog = { |
442 | .open = ath6kl_fwlog_open, |
443 | .release = ath6kl_fwlog_release, |
444 | .read = ath6kl_fwlog_read, |
445 | .owner = THIS_MODULE, |
446 | .llseek = default_llseek, |
447 | }; |
448 | |
449 | static ssize_t ath6kl_fwlog_block_read(struct file *file, |
450 | char __user *user_buf, |
451 | size_t count, |
452 | loff_t *ppos) |
453 | { |
454 | struct ath6kl *ar = file->private_data; |
455 | struct sk_buff *skb; |
456 | ssize_t ret_cnt; |
457 | size_t len = 0, not_copied; |
458 | char *buf; |
459 | int ret; |
460 | |
461 | buf = vmalloc(size: count); |
462 | if (!buf) |
463 | return -ENOMEM; |
464 | |
465 | spin_lock(lock: &ar->debug.fwlog_queue.lock); |
466 | |
467 | if (skb_queue_len(list_: &ar->debug.fwlog_queue) == 0) { |
468 | /* we must init under queue lock */ |
469 | init_completion(x: &ar->debug.fwlog_completion); |
470 | |
471 | spin_unlock(lock: &ar->debug.fwlog_queue.lock); |
472 | |
473 | ret = wait_for_completion_interruptible( |
474 | x: &ar->debug.fwlog_completion); |
475 | if (ret == -ERESTARTSYS) { |
476 | vfree(addr: buf); |
477 | return ret; |
478 | } |
479 | |
480 | spin_lock(lock: &ar->debug.fwlog_queue.lock); |
481 | } |
482 | |
483 | while ((skb = __skb_dequeue(list: &ar->debug.fwlog_queue))) { |
484 | if (skb->len > count - len) { |
485 | /* not enough space, put skb back and leave */ |
486 | __skb_queue_head(list: &ar->debug.fwlog_queue, newsk: skb); |
487 | break; |
488 | } |
489 | |
490 | |
491 | memcpy(buf + len, skb->data, skb->len); |
492 | len += skb->len; |
493 | |
494 | kfree_skb(skb); |
495 | } |
496 | |
497 | spin_unlock(lock: &ar->debug.fwlog_queue.lock); |
498 | |
499 | /* FIXME: what to do if len == 0? */ |
500 | |
501 | not_copied = copy_to_user(to: user_buf, from: buf, n: len); |
502 | if (not_copied != 0) { |
503 | ret_cnt = -EFAULT; |
504 | goto out; |
505 | } |
506 | |
507 | *ppos = *ppos + len; |
508 | |
509 | ret_cnt = len; |
510 | |
511 | out: |
512 | vfree(addr: buf); |
513 | |
514 | return ret_cnt; |
515 | } |
516 | |
517 | static const struct file_operations fops_fwlog_block = { |
518 | .open = ath6kl_fwlog_open, |
519 | .release = ath6kl_fwlog_release, |
520 | .read = ath6kl_fwlog_block_read, |
521 | .owner = THIS_MODULE, |
522 | .llseek = default_llseek, |
523 | }; |
524 | |
525 | static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, |
526 | size_t count, loff_t *ppos) |
527 | { |
528 | struct ath6kl *ar = file->private_data; |
529 | char buf[16]; |
530 | int len; |
531 | |
532 | len = snprintf(buf, size: sizeof(buf), fmt: "0x%x\n" , ar->debug.fwlog_mask); |
533 | |
534 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
535 | } |
536 | |
537 | static ssize_t ath6kl_fwlog_mask_write(struct file *file, |
538 | const char __user *user_buf, |
539 | size_t count, loff_t *ppos) |
540 | { |
541 | struct ath6kl *ar = file->private_data; |
542 | int ret; |
543 | |
544 | ret = kstrtou32_from_user(s: user_buf, count, base: 0, res: &ar->debug.fwlog_mask); |
545 | if (ret) |
546 | return ret; |
547 | |
548 | ret = ath6kl_wmi_config_debug_module_cmd(wmi: ar->wmi, |
549 | ATH6KL_FWLOG_VALID_MASK, |
550 | config: ar->debug.fwlog_mask); |
551 | if (ret) |
552 | return ret; |
553 | |
554 | return count; |
555 | } |
556 | |
557 | static const struct file_operations fops_fwlog_mask = { |
558 | .open = simple_open, |
559 | .read = ath6kl_fwlog_mask_read, |
560 | .write = ath6kl_fwlog_mask_write, |
561 | .owner = THIS_MODULE, |
562 | .llseek = default_llseek, |
563 | }; |
564 | |
565 | static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, |
566 | size_t count, loff_t *ppos) |
567 | { |
568 | struct ath6kl *ar = file->private_data; |
569 | struct ath6kl_vif *vif; |
570 | struct target_stats *tgt_stats; |
571 | char *buf; |
572 | unsigned int len = 0, buf_len = 1500; |
573 | int i; |
574 | ssize_t ret_cnt; |
575 | int rv; |
576 | |
577 | vif = ath6kl_vif_first(ar); |
578 | if (!vif) |
579 | return -EIO; |
580 | |
581 | buf = kzalloc(size: buf_len, GFP_KERNEL); |
582 | if (!buf) |
583 | return -ENOMEM; |
584 | |
585 | rv = ath6kl_read_tgt_stats(ar, vif); |
586 | if (rv < 0) { |
587 | kfree(objp: buf); |
588 | return rv; |
589 | } |
590 | |
591 | tgt_stats = &vif->target_stats; |
592 | |
593 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "\n" ); |
594 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
595 | "Target Tx stats" ); |
596 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n\n" , |
597 | "=================" ); |
598 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
599 | "Ucast packets" , tgt_stats->tx_ucast_pkt); |
600 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
601 | "Bcast packets" , tgt_stats->tx_bcast_pkt); |
602 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
603 | "Ucast byte" , tgt_stats->tx_ucast_byte); |
604 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
605 | "Bcast byte" , tgt_stats->tx_bcast_byte); |
606 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
607 | "Rts success cnt" , tgt_stats->tx_rts_success_cnt); |
608 | for (i = 0; i < 4; i++) |
609 | len += scnprintf(buf: buf + len, size: buf_len - len, |
610 | fmt: "%18s %d %10llu\n" , "PER on ac" , |
611 | i, tgt_stats->tx_pkt_per_ac[i]); |
612 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
613 | "Error" , tgt_stats->tx_err); |
614 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
615 | "Fail count" , tgt_stats->tx_fail_cnt); |
616 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
617 | "Retry count" , tgt_stats->tx_retry_cnt); |
618 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
619 | "Multi retry cnt" , tgt_stats->tx_mult_retry_cnt); |
620 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
621 | "Rts fail cnt" , tgt_stats->tx_rts_fail_cnt); |
622 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s %10llu\n\n" , |
623 | "TKIP counter measure used" , |
624 | tgt_stats->tkip_cnter_measures_invoked); |
625 | |
626 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
627 | "Target Rx stats" ); |
628 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
629 | "=================" ); |
630 | |
631 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
632 | "Ucast packets" , tgt_stats->rx_ucast_pkt); |
633 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10d\n" , |
634 | "Ucast Rate" , tgt_stats->rx_ucast_rate); |
635 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
636 | "Bcast packets" , tgt_stats->rx_bcast_pkt); |
637 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
638 | "Ucast byte" , tgt_stats->rx_ucast_byte); |
639 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
640 | "Bcast byte" , tgt_stats->rx_bcast_byte); |
641 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
642 | "Fragmented pkt" , tgt_stats->rx_frgment_pkt); |
643 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
644 | "Error" , tgt_stats->rx_err); |
645 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
646 | "CRC Err" , tgt_stats->rx_crc_err); |
647 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
648 | "Key cache miss" , tgt_stats->rx_key_cache_miss); |
649 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
650 | "Decrypt Err" , tgt_stats->rx_decrypt_err); |
651 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
652 | "Duplicate frame" , tgt_stats->rx_dupl_frame); |
653 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
654 | "Tkip Mic failure" , tgt_stats->tkip_local_mic_fail); |
655 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
656 | "TKIP format err" , tgt_stats->tkip_fmt_err); |
657 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
658 | "CCMP format Err" , tgt_stats->ccmp_fmt_err); |
659 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n\n" , |
660 | "CCMP Replay Err" , tgt_stats->ccmp_replays); |
661 | |
662 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
663 | "Misc Target stats" ); |
664 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s\n" , |
665 | "=================" ); |
666 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
667 | "Beacon Miss count" , tgt_stats->cs_bmiss_cnt); |
668 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
669 | "Num Connects" , tgt_stats->cs_connect_cnt); |
670 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10llu\n" , |
671 | "Num disconnects" , tgt_stats->cs_discon_cnt); |
672 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10d\n" , |
673 | "Beacon avg rssi" , tgt_stats->cs_ave_beacon_rssi); |
674 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10d\n" , |
675 | "ARP pkt received" , tgt_stats->arp_received); |
676 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10d\n" , |
677 | "ARP pkt matched" , tgt_stats->arp_matched); |
678 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%20s %10d\n" , |
679 | "ARP pkt replied" , tgt_stats->arp_replied); |
680 | |
681 | if (len > buf_len) |
682 | len = buf_len; |
683 | |
684 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
685 | |
686 | kfree(objp: buf); |
687 | return ret_cnt; |
688 | } |
689 | |
690 | static const struct file_operations fops_tgt_stats = { |
691 | .read = read_file_tgt_stats, |
692 | .open = simple_open, |
693 | .owner = THIS_MODULE, |
694 | .llseek = default_llseek, |
695 | }; |
696 | |
697 | #define print_credit_info(fmt_str, ep_list_field) \ |
698 | (len += scnprintf(buf + len, buf_len - len, fmt_str, \ |
699 | ep_list->ep_list_field)) |
700 | #define CREDIT_INFO_DISPLAY_STRING_LEN 200 |
701 | #define CREDIT_INFO_LEN 128 |
702 | |
703 | static ssize_t read_file_credit_dist_stats(struct file *file, |
704 | char __user *user_buf, |
705 | size_t count, loff_t *ppos) |
706 | { |
707 | struct ath6kl *ar = file->private_data; |
708 | struct htc_target *target = ar->htc_target; |
709 | struct htc_endpoint_credit_dist *ep_list; |
710 | char *buf; |
711 | unsigned int buf_len, len = 0; |
712 | ssize_t ret_cnt; |
713 | |
714 | buf_len = CREDIT_INFO_DISPLAY_STRING_LEN + |
715 | get_queue_depth(queue: &target->cred_dist_list) * CREDIT_INFO_LEN; |
716 | buf = kzalloc(size: buf_len, GFP_KERNEL); |
717 | if (!buf) |
718 | return -ENOMEM; |
719 | |
720 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s%5d\n" , |
721 | "Total Avail Credits: " , |
722 | target->credit_info->total_avail_credits); |
723 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%25s%5d\n" , |
724 | "Free credits :" , |
725 | target->credit_info->cur_free_credits); |
726 | |
727 | len += scnprintf(buf: buf + len, size: buf_len - len, |
728 | fmt: " Epid Flags Cred_norm Cred_min Credits Cred_assngd" |
729 | " Seek_cred Cred_sz Cred_per_msg Cred_to_dist" |
730 | " qdepth\n" ); |
731 | |
732 | list_for_each_entry(ep_list, &target->cred_dist_list, list) { |
733 | print_credit_info(" %2d" , endpoint); |
734 | print_credit_info("%10x" , dist_flags); |
735 | print_credit_info("%8d" , cred_norm); |
736 | print_credit_info("%9d" , cred_min); |
737 | print_credit_info("%9d" , credits); |
738 | print_credit_info("%10d" , cred_assngd); |
739 | print_credit_info("%13d" , seek_cred); |
740 | print_credit_info("%12d" , cred_sz); |
741 | print_credit_info("%9d" , cred_per_msg); |
742 | print_credit_info("%14d" , cred_to_dist); |
743 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%12d\n" , |
744 | get_queue_depth(queue: &ep_list->htc_ep->txq)); |
745 | } |
746 | |
747 | if (len > buf_len) |
748 | len = buf_len; |
749 | |
750 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
751 | kfree(objp: buf); |
752 | return ret_cnt; |
753 | } |
754 | |
755 | static const struct file_operations fops_credit_dist_stats = { |
756 | .read = read_file_credit_dist_stats, |
757 | .open = simple_open, |
758 | .owner = THIS_MODULE, |
759 | .llseek = default_llseek, |
760 | }; |
761 | |
762 | static unsigned int print_endpoint_stat(struct htc_target *target, char *buf, |
763 | unsigned int buf_len, unsigned int len, |
764 | int offset, const char *name) |
765 | { |
766 | int i; |
767 | struct htc_endpoint_stats *ep_st; |
768 | u32 *counter; |
769 | |
770 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%s:" , name); |
771 | for (i = 0; i < ENDPOINT_MAX; i++) { |
772 | ep_st = &target->endpoint[i].ep_st; |
773 | counter = ((u32 *) ep_st) + (offset / 4); |
774 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: " %u" , *counter); |
775 | } |
776 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "\n" ); |
777 | |
778 | return len; |
779 | } |
780 | |
781 | static ssize_t ath6kl_endpoint_stats_read(struct file *file, |
782 | char __user *user_buf, |
783 | size_t count, loff_t *ppos) |
784 | { |
785 | struct ath6kl *ar = file->private_data; |
786 | struct htc_target *target = ar->htc_target; |
787 | char *buf; |
788 | unsigned int buf_len, len = 0; |
789 | ssize_t ret_cnt; |
790 | |
791 | buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) * |
792 | (25 + ENDPOINT_MAX * 11); |
793 | buf = kmalloc(size: buf_len, GFP_KERNEL); |
794 | if (!buf) |
795 | return -ENOMEM; |
796 | |
797 | #define EPSTAT(name) \ |
798 | do { \ |
799 | len = print_endpoint_stat(target, buf, buf_len, len, \ |
800 | offsetof(struct htc_endpoint_stats, \ |
801 | name), \ |
802 | #name); \ |
803 | } while (0) |
804 | |
805 | EPSTAT(cred_low_indicate); |
806 | EPSTAT(tx_issued); |
807 | EPSTAT(tx_pkt_bundled); |
808 | EPSTAT(tx_bundles); |
809 | EPSTAT(tx_dropped); |
810 | EPSTAT(tx_cred_rpt); |
811 | EPSTAT(cred_rpt_from_rx); |
812 | EPSTAT(cred_rpt_from_other); |
813 | EPSTAT(cred_rpt_ep0); |
814 | EPSTAT(cred_from_rx); |
815 | EPSTAT(cred_from_other); |
816 | EPSTAT(cred_from_ep0); |
817 | EPSTAT(cred_cosumd); |
818 | EPSTAT(cred_retnd); |
819 | EPSTAT(rx_pkts); |
820 | EPSTAT(rx_lkahds); |
821 | EPSTAT(rx_bundl); |
822 | EPSTAT(rx_bundle_lkahd); |
823 | EPSTAT(rx_bundle_from_hdr); |
824 | EPSTAT(rx_alloc_thresh_hit); |
825 | EPSTAT(rxalloc_thresh_byte); |
826 | #undef EPSTAT |
827 | |
828 | if (len > buf_len) |
829 | len = buf_len; |
830 | |
831 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
832 | kfree(objp: buf); |
833 | return ret_cnt; |
834 | } |
835 | |
836 | static ssize_t ath6kl_endpoint_stats_write(struct file *file, |
837 | const char __user *user_buf, |
838 | size_t count, loff_t *ppos) |
839 | { |
840 | struct ath6kl *ar = file->private_data; |
841 | struct htc_target *target = ar->htc_target; |
842 | int ret, i; |
843 | u32 val; |
844 | struct htc_endpoint_stats *ep_st; |
845 | |
846 | ret = kstrtou32_from_user(s: user_buf, count, base: 0, res: &val); |
847 | if (ret) |
848 | return ret; |
849 | if (val == 0) { |
850 | for (i = 0; i < ENDPOINT_MAX; i++) { |
851 | ep_st = &target->endpoint[i].ep_st; |
852 | memset(ep_st, 0, sizeof(*ep_st)); |
853 | } |
854 | } |
855 | |
856 | return count; |
857 | } |
858 | |
859 | static const struct file_operations fops_endpoint_stats = { |
860 | .open = simple_open, |
861 | .read = ath6kl_endpoint_stats_read, |
862 | .write = ath6kl_endpoint_stats_write, |
863 | .owner = THIS_MODULE, |
864 | .llseek = default_llseek, |
865 | }; |
866 | |
867 | static unsigned long ath6kl_get_num_reg(void) |
868 | { |
869 | int i; |
870 | unsigned long n_reg = 0; |
871 | |
872 | for (i = 0; i < ARRAY_SIZE(diag_reg); i++) |
873 | n_reg = n_reg + |
874 | (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1; |
875 | |
876 | return n_reg; |
877 | } |
878 | |
879 | static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr) |
880 | { |
881 | int i; |
882 | |
883 | for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { |
884 | if (reg_addr >= diag_reg[i].reg_start && |
885 | reg_addr <= diag_reg[i].reg_end) |
886 | return true; |
887 | } |
888 | |
889 | return false; |
890 | } |
891 | |
892 | static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf, |
893 | size_t count, loff_t *ppos) |
894 | { |
895 | struct ath6kl *ar = file->private_data; |
896 | u8 buf[50]; |
897 | unsigned int len = 0; |
898 | |
899 | if (ar->debug.dbgfs_diag_reg) |
900 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "0x%x\n" , |
901 | ar->debug.dbgfs_diag_reg); |
902 | else |
903 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
904 | fmt: "All diag registers\n" ); |
905 | |
906 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
907 | } |
908 | |
909 | static ssize_t ath6kl_regread_write(struct file *file, |
910 | const char __user *user_buf, |
911 | size_t count, loff_t *ppos) |
912 | { |
913 | struct ath6kl *ar = file->private_data; |
914 | unsigned long reg_addr; |
915 | |
916 | if (kstrtoul_from_user(s: user_buf, count, base: 0, res: ®_addr)) |
917 | return -EINVAL; |
918 | |
919 | if ((reg_addr % 4) != 0) |
920 | return -EINVAL; |
921 | |
922 | if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr)) |
923 | return -EINVAL; |
924 | |
925 | ar->debug.dbgfs_diag_reg = reg_addr; |
926 | |
927 | return count; |
928 | } |
929 | |
930 | static const struct file_operations fops_diag_reg_read = { |
931 | .read = ath6kl_regread_read, |
932 | .write = ath6kl_regread_write, |
933 | .open = simple_open, |
934 | .owner = THIS_MODULE, |
935 | .llseek = default_llseek, |
936 | }; |
937 | |
938 | static int ath6kl_regdump_open(struct inode *inode, struct file *file) |
939 | { |
940 | struct ath6kl *ar = inode->i_private; |
941 | u8 *buf; |
942 | unsigned long int reg_len; |
943 | unsigned int len = 0, n_reg; |
944 | u32 addr; |
945 | __le32 reg_val; |
946 | int i, status; |
947 | |
948 | /* Dump all the registers if no register is specified */ |
949 | if (!ar->debug.dbgfs_diag_reg) |
950 | n_reg = ath6kl_get_num_reg(); |
951 | else |
952 | n_reg = 1; |
953 | |
954 | reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE; |
955 | if (n_reg > 1) |
956 | reg_len += REGTYPE_STR_LEN; |
957 | |
958 | buf = vmalloc(size: reg_len); |
959 | if (!buf) |
960 | return -ENOMEM; |
961 | |
962 | if (n_reg == 1) { |
963 | addr = ar->debug.dbgfs_diag_reg; |
964 | |
965 | status = ath6kl_diag_read32(ar, |
966 | TARG_VTOP(ar->target_type, addr), |
967 | value: (u32 *)®_val); |
968 | if (status) |
969 | goto fail_reg_read; |
970 | |
971 | len += scnprintf(buf: buf + len, size: reg_len - len, |
972 | fmt: "0x%06x 0x%08x\n" , addr, le32_to_cpu(reg_val)); |
973 | goto done; |
974 | } |
975 | |
976 | for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { |
977 | len += scnprintf(buf: buf + len, size: reg_len - len, |
978 | fmt: "%s\n" , diag_reg[i].reg_info); |
979 | for (addr = diag_reg[i].reg_start; |
980 | addr <= diag_reg[i].reg_end; addr += 4) { |
981 | status = ath6kl_diag_read32(ar, |
982 | TARG_VTOP(ar->target_type, addr), |
983 | value: (u32 *)®_val); |
984 | if (status) |
985 | goto fail_reg_read; |
986 | |
987 | len += scnprintf(buf: buf + len, size: reg_len - len, |
988 | fmt: "0x%06x 0x%08x\n" , |
989 | addr, le32_to_cpu(reg_val)); |
990 | } |
991 | } |
992 | |
993 | done: |
994 | file->private_data = buf; |
995 | return 0; |
996 | |
997 | fail_reg_read: |
998 | ath6kl_warn("Unable to read memory:%u\n" , addr); |
999 | vfree(addr: buf); |
1000 | return -EIO; |
1001 | } |
1002 | |
1003 | static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf, |
1004 | size_t count, loff_t *ppos) |
1005 | { |
1006 | u8 *buf = file->private_data; |
1007 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, strlen(buf)); |
1008 | } |
1009 | |
1010 | static int ath6kl_regdump_release(struct inode *inode, struct file *file) |
1011 | { |
1012 | vfree(addr: file->private_data); |
1013 | return 0; |
1014 | } |
1015 | |
1016 | static const struct file_operations fops_reg_dump = { |
1017 | .open = ath6kl_regdump_open, |
1018 | .read = ath6kl_regdump_read, |
1019 | .release = ath6kl_regdump_release, |
1020 | .owner = THIS_MODULE, |
1021 | .llseek = default_llseek, |
1022 | }; |
1023 | |
1024 | static ssize_t (struct file *file, |
1025 | const char __user *user_buf, |
1026 | size_t count, loff_t *ppos) |
1027 | { |
1028 | struct ath6kl *ar = file->private_data; |
1029 | unsigned long ; |
1030 | int ret; |
1031 | |
1032 | if (kstrtoul_from_user(s: user_buf, count, base: 0, res: &lrssi_roam_threshold)) |
1033 | return -EINVAL; |
1034 | |
1035 | ar->lrssi_roam_threshold = lrssi_roam_threshold; |
1036 | |
1037 | ret = ath6kl_wmi_set_roam_lrssi_cmd(wmi: ar->wmi, lrssi: ar->lrssi_roam_threshold); |
1038 | |
1039 | if (ret) |
1040 | return ret; |
1041 | return count; |
1042 | } |
1043 | |
1044 | static ssize_t (struct file *file, |
1045 | char __user *user_buf, |
1046 | size_t count, loff_t *ppos) |
1047 | { |
1048 | struct ath6kl *ar = file->private_data; |
1049 | char buf[32]; |
1050 | unsigned int len; |
1051 | |
1052 | len = snprintf(buf, size: sizeof(buf), fmt: "%u\n" , ar->lrssi_roam_threshold); |
1053 | |
1054 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1055 | } |
1056 | |
1057 | static const struct file_operations = { |
1058 | .read = ath6kl_lrssi_roam_read, |
1059 | .write = ath6kl_lrssi_roam_write, |
1060 | .open = simple_open, |
1061 | .owner = THIS_MODULE, |
1062 | .llseek = default_llseek, |
1063 | }; |
1064 | |
1065 | static ssize_t ath6kl_regwrite_read(struct file *file, |
1066 | char __user *user_buf, |
1067 | size_t count, loff_t *ppos) |
1068 | { |
1069 | struct ath6kl *ar = file->private_data; |
1070 | u8 buf[32]; |
1071 | unsigned int len = 0; |
1072 | |
1073 | len = scnprintf(buf, size: sizeof(buf), fmt: "Addr: 0x%x Val: 0x%x\n" , |
1074 | ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr); |
1075 | |
1076 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1077 | } |
1078 | |
1079 | static ssize_t ath6kl_regwrite_write(struct file *file, |
1080 | const char __user *user_buf, |
1081 | size_t count, loff_t *ppos) |
1082 | { |
1083 | struct ath6kl *ar = file->private_data; |
1084 | char buf[32]; |
1085 | char *sptr, *token; |
1086 | unsigned int len = 0; |
1087 | u32 reg_addr, reg_val; |
1088 | |
1089 | len = min(count, sizeof(buf) - 1); |
1090 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1091 | return -EFAULT; |
1092 | |
1093 | buf[len] = '\0'; |
1094 | sptr = buf; |
1095 | |
1096 | token = strsep(&sptr, "=" ); |
1097 | if (!token) |
1098 | return -EINVAL; |
1099 | |
1100 | if (kstrtou32(s: token, base: 0, res: ®_addr)) |
1101 | return -EINVAL; |
1102 | |
1103 | if (!ath6kl_dbg_is_diag_reg_valid(reg_addr)) |
1104 | return -EINVAL; |
1105 | |
1106 | if (kstrtou32(s: sptr, base: 0, res: ®_val)) |
1107 | return -EINVAL; |
1108 | |
1109 | ar->debug.diag_reg_addr_wr = reg_addr; |
1110 | ar->debug.diag_reg_val_wr = reg_val; |
1111 | |
1112 | if (ath6kl_diag_write32(ar, address: ar->debug.diag_reg_addr_wr, |
1113 | cpu_to_le32(ar->debug.diag_reg_val_wr))) |
1114 | return -EIO; |
1115 | |
1116 | return count; |
1117 | } |
1118 | |
1119 | static const struct file_operations fops_diag_reg_write = { |
1120 | .read = ath6kl_regwrite_read, |
1121 | .write = ath6kl_regwrite_write, |
1122 | .open = simple_open, |
1123 | .owner = THIS_MODULE, |
1124 | .llseek = default_llseek, |
1125 | }; |
1126 | |
1127 | int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, |
1128 | size_t len) |
1129 | { |
1130 | const struct wmi_target_roam_tbl *tbl; |
1131 | u16 num_entries; |
1132 | |
1133 | if (len < sizeof(*tbl)) |
1134 | return -EINVAL; |
1135 | |
1136 | tbl = (const struct wmi_target_roam_tbl *) buf; |
1137 | num_entries = le16_to_cpu(tbl->num_entries); |
1138 | if (struct_size(tbl, info, num_entries) > len) |
1139 | return -EINVAL; |
1140 | |
1141 | if (ar->debug.roam_tbl == NULL || |
1142 | ar->debug.roam_tbl_len < (unsigned int) len) { |
1143 | kfree(objp: ar->debug.roam_tbl); |
1144 | ar->debug.roam_tbl = kmalloc(size: len, GFP_ATOMIC); |
1145 | if (ar->debug.roam_tbl == NULL) |
1146 | return -ENOMEM; |
1147 | } |
1148 | |
1149 | memcpy(ar->debug.roam_tbl, buf, len); |
1150 | ar->debug.roam_tbl_len = len; |
1151 | |
1152 | if (test_bit(ROAM_TBL_PEND, &ar->flag)) { |
1153 | clear_bit(nr: ROAM_TBL_PEND, addr: &ar->flag); |
1154 | wake_up(&ar->event_wq); |
1155 | } |
1156 | |
1157 | return 0; |
1158 | } |
1159 | |
1160 | static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, |
1161 | size_t count, loff_t *ppos) |
1162 | { |
1163 | struct ath6kl *ar = file->private_data; |
1164 | int ret; |
1165 | long left; |
1166 | struct wmi_target_roam_tbl *tbl; |
1167 | u16 num_entries, i; |
1168 | char *buf; |
1169 | unsigned int len, buf_len; |
1170 | ssize_t ret_cnt; |
1171 | |
1172 | if (down_interruptible(sem: &ar->sem)) |
1173 | return -EBUSY; |
1174 | |
1175 | set_bit(nr: ROAM_TBL_PEND, addr: &ar->flag); |
1176 | |
1177 | ret = ath6kl_wmi_get_roam_tbl_cmd(wmi: ar->wmi); |
1178 | if (ret) { |
1179 | up(sem: &ar->sem); |
1180 | return ret; |
1181 | } |
1182 | |
1183 | left = wait_event_interruptible_timeout( |
1184 | ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT); |
1185 | up(sem: &ar->sem); |
1186 | |
1187 | if (left <= 0) |
1188 | return -ETIMEDOUT; |
1189 | |
1190 | if (ar->debug.roam_tbl == NULL) |
1191 | return -ENOMEM; |
1192 | |
1193 | tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl; |
1194 | num_entries = le16_to_cpu(tbl->num_entries); |
1195 | |
1196 | buf_len = 100 + num_entries * 100; |
1197 | buf = kzalloc(size: buf_len, GFP_KERNEL); |
1198 | if (buf == NULL) |
1199 | return -ENOMEM; |
1200 | len = 0; |
1201 | len += scnprintf(buf: buf + len, size: buf_len - len, |
1202 | fmt: "roam_mode=%u\n\n" |
1203 | "# roam_util bssid rssi rssidt last_rssi util bias\n" , |
1204 | le16_to_cpu(tbl->roam_mode)); |
1205 | |
1206 | for (i = 0; i < num_entries; i++) { |
1207 | struct wmi_bss_roam_info *info = &tbl->info[i]; |
1208 | len += scnprintf(buf: buf + len, size: buf_len - len, |
1209 | fmt: "%d %pM %d %d %d %d %d\n" , |
1210 | a_sle32_to_cpu(val: info->roam_util), info->bssid, |
1211 | info->rssi, info->rssidt, info->last_rssi, |
1212 | info->util, info->bias); |
1213 | } |
1214 | |
1215 | if (len > buf_len) |
1216 | len = buf_len; |
1217 | |
1218 | ret_cnt = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1219 | |
1220 | kfree(objp: buf); |
1221 | return ret_cnt; |
1222 | } |
1223 | |
1224 | static const struct file_operations fops_roam_table = { |
1225 | .read = ath6kl_roam_table_read, |
1226 | .open = simple_open, |
1227 | .owner = THIS_MODULE, |
1228 | .llseek = default_llseek, |
1229 | }; |
1230 | |
1231 | static ssize_t ath6kl_force_roam_write(struct file *file, |
1232 | const char __user *user_buf, |
1233 | size_t count, loff_t *ppos) |
1234 | { |
1235 | struct ath6kl *ar = file->private_data; |
1236 | int ret; |
1237 | char buf[20]; |
1238 | size_t len; |
1239 | u8 bssid[ETH_ALEN]; |
1240 | |
1241 | len = min(count, sizeof(buf) - 1); |
1242 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1243 | return -EFAULT; |
1244 | buf[len] = '\0'; |
1245 | |
1246 | if (!mac_pton(s: buf, mac: bssid)) |
1247 | return -EINVAL; |
1248 | |
1249 | ret = ath6kl_wmi_force_roam_cmd(wmi: ar->wmi, bssid); |
1250 | if (ret) |
1251 | return ret; |
1252 | |
1253 | return count; |
1254 | } |
1255 | |
1256 | static const struct file_operations fops_force_roam = { |
1257 | .write = ath6kl_force_roam_write, |
1258 | .open = simple_open, |
1259 | .owner = THIS_MODULE, |
1260 | .llseek = default_llseek, |
1261 | }; |
1262 | |
1263 | static ssize_t ath6kl_roam_mode_write(struct file *file, |
1264 | const char __user *user_buf, |
1265 | size_t count, loff_t *ppos) |
1266 | { |
1267 | struct ath6kl *ar = file->private_data; |
1268 | int ret; |
1269 | char buf[20]; |
1270 | size_t len; |
1271 | enum wmi_roam_mode mode; |
1272 | |
1273 | len = min(count, sizeof(buf) - 1); |
1274 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1275 | return -EFAULT; |
1276 | buf[len] = '\0'; |
1277 | if (len > 0 && buf[len - 1] == '\n') |
1278 | buf[len - 1] = '\0'; |
1279 | |
1280 | if (strcasecmp(s1: buf, s2: "default" ) == 0) |
1281 | mode = WMI_DEFAULT_ROAM_MODE; |
1282 | else if (strcasecmp(s1: buf, s2: "bssbias" ) == 0) |
1283 | mode = WMI_HOST_BIAS_ROAM_MODE; |
1284 | else if (strcasecmp(s1: buf, s2: "lock" ) == 0) |
1285 | mode = WMI_LOCK_BSS_MODE; |
1286 | else |
1287 | return -EINVAL; |
1288 | |
1289 | ret = ath6kl_wmi_set_roam_mode_cmd(wmi: ar->wmi, mode); |
1290 | if (ret) |
1291 | return ret; |
1292 | |
1293 | return count; |
1294 | } |
1295 | |
1296 | static const struct file_operations fops_roam_mode = { |
1297 | .write = ath6kl_roam_mode_write, |
1298 | .open = simple_open, |
1299 | .owner = THIS_MODULE, |
1300 | .llseek = default_llseek, |
1301 | }; |
1302 | |
1303 | void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) |
1304 | { |
1305 | ar->debug.keepalive = keepalive; |
1306 | } |
1307 | |
1308 | static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf, |
1309 | size_t count, loff_t *ppos) |
1310 | { |
1311 | struct ath6kl *ar = file->private_data; |
1312 | char buf[16]; |
1313 | int len; |
1314 | |
1315 | len = snprintf(buf, size: sizeof(buf), fmt: "%u\n" , ar->debug.keepalive); |
1316 | |
1317 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1318 | } |
1319 | |
1320 | static ssize_t ath6kl_keepalive_write(struct file *file, |
1321 | const char __user *user_buf, |
1322 | size_t count, loff_t *ppos) |
1323 | { |
1324 | struct ath6kl *ar = file->private_data; |
1325 | int ret; |
1326 | u8 val; |
1327 | |
1328 | ret = kstrtou8_from_user(s: user_buf, count, base: 0, res: &val); |
1329 | if (ret) |
1330 | return ret; |
1331 | |
1332 | ret = ath6kl_wmi_set_keepalive_cmd(wmi: ar->wmi, if_idx: 0, keep_alive_intvl: val); |
1333 | if (ret) |
1334 | return ret; |
1335 | |
1336 | return count; |
1337 | } |
1338 | |
1339 | static const struct file_operations fops_keepalive = { |
1340 | .open = simple_open, |
1341 | .read = ath6kl_keepalive_read, |
1342 | .write = ath6kl_keepalive_write, |
1343 | .owner = THIS_MODULE, |
1344 | .llseek = default_llseek, |
1345 | }; |
1346 | |
1347 | void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout) |
1348 | { |
1349 | ar->debug.disc_timeout = timeout; |
1350 | } |
1351 | |
1352 | static ssize_t ath6kl_disconnect_timeout_read(struct file *file, |
1353 | char __user *user_buf, |
1354 | size_t count, loff_t *ppos) |
1355 | { |
1356 | struct ath6kl *ar = file->private_data; |
1357 | char buf[16]; |
1358 | int len; |
1359 | |
1360 | len = snprintf(buf, size: sizeof(buf), fmt: "%u\n" , ar->debug.disc_timeout); |
1361 | |
1362 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1363 | } |
1364 | |
1365 | static ssize_t ath6kl_disconnect_timeout_write(struct file *file, |
1366 | const char __user *user_buf, |
1367 | size_t count, loff_t *ppos) |
1368 | { |
1369 | struct ath6kl *ar = file->private_data; |
1370 | int ret; |
1371 | u8 val; |
1372 | |
1373 | ret = kstrtou8_from_user(s: user_buf, count, base: 0, res: &val); |
1374 | if (ret) |
1375 | return ret; |
1376 | |
1377 | ret = ath6kl_wmi_disctimeout_cmd(wmi: ar->wmi, if_idx: 0, timeout: val); |
1378 | if (ret) |
1379 | return ret; |
1380 | |
1381 | return count; |
1382 | } |
1383 | |
1384 | static const struct file_operations fops_disconnect_timeout = { |
1385 | .open = simple_open, |
1386 | .read = ath6kl_disconnect_timeout_read, |
1387 | .write = ath6kl_disconnect_timeout_write, |
1388 | .owner = THIS_MODULE, |
1389 | .llseek = default_llseek, |
1390 | }; |
1391 | |
1392 | static ssize_t ath6kl_create_qos_write(struct file *file, |
1393 | const char __user *user_buf, |
1394 | size_t count, loff_t *ppos) |
1395 | { |
1396 | struct ath6kl *ar = file->private_data; |
1397 | struct ath6kl_vif *vif; |
1398 | char buf[200]; |
1399 | ssize_t len; |
1400 | char *sptr, *token; |
1401 | struct wmi_create_pstream_cmd pstream; |
1402 | u32 val32; |
1403 | u16 val16; |
1404 | |
1405 | vif = ath6kl_vif_first(ar); |
1406 | if (!vif) |
1407 | return -EIO; |
1408 | |
1409 | len = min(count, sizeof(buf) - 1); |
1410 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1411 | return -EFAULT; |
1412 | buf[len] = '\0'; |
1413 | sptr = buf; |
1414 | |
1415 | token = strsep(&sptr, " " ); |
1416 | if (!token) |
1417 | return -EINVAL; |
1418 | if (kstrtou8(s: token, base: 0, res: &pstream.user_pri)) |
1419 | return -EINVAL; |
1420 | |
1421 | token = strsep(&sptr, " " ); |
1422 | if (!token) |
1423 | return -EINVAL; |
1424 | if (kstrtou8(s: token, base: 0, res: &pstream.traffic_direc)) |
1425 | return -EINVAL; |
1426 | |
1427 | token = strsep(&sptr, " " ); |
1428 | if (!token) |
1429 | return -EINVAL; |
1430 | if (kstrtou8(s: token, base: 0, res: &pstream.traffic_class)) |
1431 | return -EINVAL; |
1432 | |
1433 | token = strsep(&sptr, " " ); |
1434 | if (!token) |
1435 | return -EINVAL; |
1436 | if (kstrtou8(s: token, base: 0, res: &pstream.traffic_type)) |
1437 | return -EINVAL; |
1438 | |
1439 | token = strsep(&sptr, " " ); |
1440 | if (!token) |
1441 | return -EINVAL; |
1442 | if (kstrtou8(s: token, base: 0, res: &pstream.voice_psc_cap)) |
1443 | return -EINVAL; |
1444 | |
1445 | token = strsep(&sptr, " " ); |
1446 | if (!token) |
1447 | return -EINVAL; |
1448 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1449 | return -EINVAL; |
1450 | pstream.min_service_int = cpu_to_le32(val32); |
1451 | |
1452 | token = strsep(&sptr, " " ); |
1453 | if (!token) |
1454 | return -EINVAL; |
1455 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1456 | return -EINVAL; |
1457 | pstream.max_service_int = cpu_to_le32(val32); |
1458 | |
1459 | token = strsep(&sptr, " " ); |
1460 | if (!token) |
1461 | return -EINVAL; |
1462 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1463 | return -EINVAL; |
1464 | pstream.inactivity_int = cpu_to_le32(val32); |
1465 | |
1466 | token = strsep(&sptr, " " ); |
1467 | if (!token) |
1468 | return -EINVAL; |
1469 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1470 | return -EINVAL; |
1471 | pstream.suspension_int = cpu_to_le32(val32); |
1472 | |
1473 | token = strsep(&sptr, " " ); |
1474 | if (!token) |
1475 | return -EINVAL; |
1476 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1477 | return -EINVAL; |
1478 | pstream.service_start_time = cpu_to_le32(val32); |
1479 | |
1480 | token = strsep(&sptr, " " ); |
1481 | if (!token) |
1482 | return -EINVAL; |
1483 | if (kstrtou8(s: token, base: 0, res: &pstream.tsid)) |
1484 | return -EINVAL; |
1485 | |
1486 | token = strsep(&sptr, " " ); |
1487 | if (!token) |
1488 | return -EINVAL; |
1489 | if (kstrtou16(s: token, base: 0, res: &val16)) |
1490 | return -EINVAL; |
1491 | pstream.nominal_msdu = cpu_to_le16(val16); |
1492 | |
1493 | token = strsep(&sptr, " " ); |
1494 | if (!token) |
1495 | return -EINVAL; |
1496 | if (kstrtou16(s: token, base: 0, res: &val16)) |
1497 | return -EINVAL; |
1498 | pstream.max_msdu = cpu_to_le16(val16); |
1499 | |
1500 | token = strsep(&sptr, " " ); |
1501 | if (!token) |
1502 | return -EINVAL; |
1503 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1504 | return -EINVAL; |
1505 | pstream.min_data_rate = cpu_to_le32(val32); |
1506 | |
1507 | token = strsep(&sptr, " " ); |
1508 | if (!token) |
1509 | return -EINVAL; |
1510 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1511 | return -EINVAL; |
1512 | pstream.mean_data_rate = cpu_to_le32(val32); |
1513 | |
1514 | token = strsep(&sptr, " " ); |
1515 | if (!token) |
1516 | return -EINVAL; |
1517 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1518 | return -EINVAL; |
1519 | pstream.peak_data_rate = cpu_to_le32(val32); |
1520 | |
1521 | token = strsep(&sptr, " " ); |
1522 | if (!token) |
1523 | return -EINVAL; |
1524 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1525 | return -EINVAL; |
1526 | pstream.max_burst_size = cpu_to_le32(val32); |
1527 | |
1528 | token = strsep(&sptr, " " ); |
1529 | if (!token) |
1530 | return -EINVAL; |
1531 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1532 | return -EINVAL; |
1533 | pstream.delay_bound = cpu_to_le32(val32); |
1534 | |
1535 | token = strsep(&sptr, " " ); |
1536 | if (!token) |
1537 | return -EINVAL; |
1538 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1539 | return -EINVAL; |
1540 | pstream.min_phy_rate = cpu_to_le32(val32); |
1541 | |
1542 | token = strsep(&sptr, " " ); |
1543 | if (!token) |
1544 | return -EINVAL; |
1545 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1546 | return -EINVAL; |
1547 | pstream.sba = cpu_to_le32(val32); |
1548 | |
1549 | token = strsep(&sptr, " " ); |
1550 | if (!token) |
1551 | return -EINVAL; |
1552 | if (kstrtou32(s: token, base: 0, res: &val32)) |
1553 | return -EINVAL; |
1554 | pstream.medium_time = cpu_to_le32(val32); |
1555 | |
1556 | pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000; |
1557 | |
1558 | ath6kl_wmi_create_pstream_cmd(wmi: ar->wmi, if_idx: vif->fw_vif_idx, pstream: &pstream); |
1559 | |
1560 | return count; |
1561 | } |
1562 | |
1563 | static const struct file_operations fops_create_qos = { |
1564 | .write = ath6kl_create_qos_write, |
1565 | .open = simple_open, |
1566 | .owner = THIS_MODULE, |
1567 | .llseek = default_llseek, |
1568 | }; |
1569 | |
1570 | static ssize_t ath6kl_delete_qos_write(struct file *file, |
1571 | const char __user *user_buf, |
1572 | size_t count, loff_t *ppos) |
1573 | { |
1574 | struct ath6kl *ar = file->private_data; |
1575 | struct ath6kl_vif *vif; |
1576 | char buf[100]; |
1577 | ssize_t len; |
1578 | char *sptr, *token; |
1579 | u8 traffic_class; |
1580 | u8 tsid; |
1581 | |
1582 | vif = ath6kl_vif_first(ar); |
1583 | if (!vif) |
1584 | return -EIO; |
1585 | |
1586 | len = min(count, sizeof(buf) - 1); |
1587 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1588 | return -EFAULT; |
1589 | buf[len] = '\0'; |
1590 | sptr = buf; |
1591 | |
1592 | token = strsep(&sptr, " " ); |
1593 | if (!token) |
1594 | return -EINVAL; |
1595 | if (kstrtou8(s: token, base: 0, res: &traffic_class)) |
1596 | return -EINVAL; |
1597 | |
1598 | token = strsep(&sptr, " " ); |
1599 | if (!token) |
1600 | return -EINVAL; |
1601 | if (kstrtou8(s: token, base: 0, res: &tsid)) |
1602 | return -EINVAL; |
1603 | |
1604 | ath6kl_wmi_delete_pstream_cmd(wmi: ar->wmi, if_idx: vif->fw_vif_idx, |
1605 | traffic_class, tsid); |
1606 | |
1607 | return count; |
1608 | } |
1609 | |
1610 | static const struct file_operations fops_delete_qos = { |
1611 | .write = ath6kl_delete_qos_write, |
1612 | .open = simple_open, |
1613 | .owner = THIS_MODULE, |
1614 | .llseek = default_llseek, |
1615 | }; |
1616 | |
1617 | static ssize_t ath6kl_bgscan_int_write(struct file *file, |
1618 | const char __user *user_buf, |
1619 | size_t count, loff_t *ppos) |
1620 | { |
1621 | struct ath6kl *ar = file->private_data; |
1622 | struct ath6kl_vif *vif; |
1623 | u16 bgscan_int; |
1624 | char buf[32]; |
1625 | ssize_t len; |
1626 | |
1627 | vif = ath6kl_vif_first(ar); |
1628 | if (!vif) |
1629 | return -EIO; |
1630 | |
1631 | len = min(count, sizeof(buf) - 1); |
1632 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1633 | return -EFAULT; |
1634 | |
1635 | buf[len] = '\0'; |
1636 | if (kstrtou16(s: buf, base: 0, res: &bgscan_int)) |
1637 | return -EINVAL; |
1638 | |
1639 | if (bgscan_int == 0) |
1640 | bgscan_int = 0xffff; |
1641 | |
1642 | vif->bg_scan_period = bgscan_int; |
1643 | |
1644 | ath6kl_wmi_scanparams_cmd(wmi: ar->wmi, if_idx: 0, fg_start_sec: 0, fg_end_sec: 0, bg_sec: bgscan_int, minact_chdw_msec: 0, maxact_chdw_msec: 0, pas_chdw_msec: 0, short_scan_ratio: 3, |
1645 | scan_ctrl_flag: 0, max_dfsch_act_time: 0, maxact_scan_per_ssid: 0); |
1646 | |
1647 | return count; |
1648 | } |
1649 | |
1650 | static const struct file_operations fops_bgscan_int = { |
1651 | .write = ath6kl_bgscan_int_write, |
1652 | .open = simple_open, |
1653 | .owner = THIS_MODULE, |
1654 | .llseek = default_llseek, |
1655 | }; |
1656 | |
1657 | static ssize_t ath6kl_listen_int_write(struct file *file, |
1658 | const char __user *user_buf, |
1659 | size_t count, loff_t *ppos) |
1660 | { |
1661 | struct ath6kl *ar = file->private_data; |
1662 | struct ath6kl_vif *vif; |
1663 | u16 listen_interval; |
1664 | char buf[32]; |
1665 | ssize_t len; |
1666 | |
1667 | vif = ath6kl_vif_first(ar); |
1668 | if (!vif) |
1669 | return -EIO; |
1670 | |
1671 | len = min(count, sizeof(buf) - 1); |
1672 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1673 | return -EFAULT; |
1674 | |
1675 | buf[len] = '\0'; |
1676 | if (kstrtou16(s: buf, base: 0, res: &listen_interval)) |
1677 | return -EINVAL; |
1678 | |
1679 | if ((listen_interval < 15) || (listen_interval > 3000)) |
1680 | return -EINVAL; |
1681 | |
1682 | vif->listen_intvl_t = listen_interval; |
1683 | ath6kl_wmi_listeninterval_cmd(wmi: ar->wmi, if_idx: vif->fw_vif_idx, |
1684 | listen_interval: vif->listen_intvl_t, listen_beacons: 0); |
1685 | |
1686 | return count; |
1687 | } |
1688 | |
1689 | static ssize_t ath6kl_listen_int_read(struct file *file, |
1690 | char __user *user_buf, |
1691 | size_t count, loff_t *ppos) |
1692 | { |
1693 | struct ath6kl *ar = file->private_data; |
1694 | struct ath6kl_vif *vif; |
1695 | char buf[32]; |
1696 | int len; |
1697 | |
1698 | vif = ath6kl_vif_first(ar); |
1699 | if (!vif) |
1700 | return -EIO; |
1701 | |
1702 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , vif->listen_intvl_t); |
1703 | |
1704 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
1705 | } |
1706 | |
1707 | static const struct file_operations fops_listen_int = { |
1708 | .read = ath6kl_listen_int_read, |
1709 | .write = ath6kl_listen_int_write, |
1710 | .open = simple_open, |
1711 | .owner = THIS_MODULE, |
1712 | .llseek = default_llseek, |
1713 | }; |
1714 | |
1715 | static ssize_t ath6kl_power_params_write(struct file *file, |
1716 | const char __user *user_buf, |
1717 | size_t count, loff_t *ppos) |
1718 | { |
1719 | struct ath6kl *ar = file->private_data; |
1720 | u8 buf[100]; |
1721 | unsigned int len = 0; |
1722 | char *sptr, *token; |
1723 | u16 idle_period, ps_poll_num, dtim, |
1724 | tx_wakeup, num_tx; |
1725 | |
1726 | len = min(count, sizeof(buf) - 1); |
1727 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
1728 | return -EFAULT; |
1729 | buf[len] = '\0'; |
1730 | sptr = buf; |
1731 | |
1732 | token = strsep(&sptr, " " ); |
1733 | if (!token) |
1734 | return -EINVAL; |
1735 | if (kstrtou16(s: token, base: 0, res: &idle_period)) |
1736 | return -EINVAL; |
1737 | |
1738 | token = strsep(&sptr, " " ); |
1739 | if (!token) |
1740 | return -EINVAL; |
1741 | if (kstrtou16(s: token, base: 0, res: &ps_poll_num)) |
1742 | return -EINVAL; |
1743 | |
1744 | token = strsep(&sptr, " " ); |
1745 | if (!token) |
1746 | return -EINVAL; |
1747 | if (kstrtou16(s: token, base: 0, res: &dtim)) |
1748 | return -EINVAL; |
1749 | |
1750 | token = strsep(&sptr, " " ); |
1751 | if (!token) |
1752 | return -EINVAL; |
1753 | if (kstrtou16(s: token, base: 0, res: &tx_wakeup)) |
1754 | return -EINVAL; |
1755 | |
1756 | token = strsep(&sptr, " " ); |
1757 | if (!token) |
1758 | return -EINVAL; |
1759 | if (kstrtou16(s: token, base: 0, res: &num_tx)) |
1760 | return -EINVAL; |
1761 | |
1762 | ath6kl_wmi_pmparams_cmd(wmi: ar->wmi, if_idx: 0, idle_period, ps_poll_num, |
1763 | dtim_policy: dtim, tx_wakup_policy: tx_wakeup, num_tx_to_wakeup: num_tx, ps_fail_event_policy: 0); |
1764 | |
1765 | return count; |
1766 | } |
1767 | |
1768 | static const struct file_operations fops_power_params = { |
1769 | .write = ath6kl_power_params_write, |
1770 | .open = simple_open, |
1771 | .owner = THIS_MODULE, |
1772 | .llseek = default_llseek, |
1773 | }; |
1774 | |
1775 | void ath6kl_debug_init(struct ath6kl *ar) |
1776 | { |
1777 | skb_queue_head_init(list: &ar->debug.fwlog_queue); |
1778 | init_completion(x: &ar->debug.fwlog_completion); |
1779 | |
1780 | /* |
1781 | * Actually we are lying here but don't know how to read the mask |
1782 | * value from the firmware. |
1783 | */ |
1784 | ar->debug.fwlog_mask = 0; |
1785 | } |
1786 | |
1787 | /* |
1788 | * Initialisation needs to happen in two stages as fwlog events can come |
1789 | * before cfg80211 is initialised, and debugfs depends on cfg80211 |
1790 | * initialisation. |
1791 | */ |
1792 | int ath6kl_debug_init_fs(struct ath6kl *ar) |
1793 | { |
1794 | ar->debugfs_phy = debugfs_create_dir(name: "ath6kl" , |
1795 | parent: ar->wiphy->debugfsdir); |
1796 | |
1797 | debugfs_create_file(name: "tgt_stats" , mode: 0400, parent: ar->debugfs_phy, data: ar, |
1798 | fops: &fops_tgt_stats); |
1799 | |
1800 | if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO) |
1801 | debugfs_create_file(name: "credit_dist_stats" , mode: 0400, |
1802 | parent: ar->debugfs_phy, data: ar, |
1803 | fops: &fops_credit_dist_stats); |
1804 | |
1805 | debugfs_create_file(name: "endpoint_stats" , mode: 0600, |
1806 | parent: ar->debugfs_phy, data: ar, fops: &fops_endpoint_stats); |
1807 | |
1808 | debugfs_create_file(name: "fwlog" , mode: 0400, parent: ar->debugfs_phy, data: ar, fops: &fops_fwlog); |
1809 | |
1810 | debugfs_create_file(name: "fwlog_block" , mode: 0400, parent: ar->debugfs_phy, data: ar, |
1811 | fops: &fops_fwlog_block); |
1812 | |
1813 | debugfs_create_file(name: "fwlog_mask" , mode: 0600, parent: ar->debugfs_phy, |
1814 | data: ar, fops: &fops_fwlog_mask); |
1815 | |
1816 | debugfs_create_file(name: "reg_addr" , mode: 0600, parent: ar->debugfs_phy, data: ar, |
1817 | fops: &fops_diag_reg_read); |
1818 | |
1819 | debugfs_create_file(name: "reg_dump" , mode: 0400, parent: ar->debugfs_phy, data: ar, |
1820 | fops: &fops_reg_dump); |
1821 | |
1822 | debugfs_create_file(name: "lrssi_roam_threshold" , mode: 0600, |
1823 | parent: ar->debugfs_phy, data: ar, fops: &fops_lrssi_roam_threshold); |
1824 | |
1825 | debugfs_create_file(name: "reg_write" , mode: 0600, |
1826 | parent: ar->debugfs_phy, data: ar, fops: &fops_diag_reg_write); |
1827 | |
1828 | debugfs_create_file(name: "war_stats" , mode: 0400, parent: ar->debugfs_phy, data: ar, |
1829 | fops: &fops_war_stats); |
1830 | |
1831 | debugfs_create_file(name: "roam_table" , mode: 0400, parent: ar->debugfs_phy, data: ar, |
1832 | fops: &fops_roam_table); |
1833 | |
1834 | debugfs_create_file(name: "force_roam" , mode: 0200, parent: ar->debugfs_phy, data: ar, |
1835 | fops: &fops_force_roam); |
1836 | |
1837 | debugfs_create_file(name: "roam_mode" , mode: 0200, parent: ar->debugfs_phy, data: ar, |
1838 | fops: &fops_roam_mode); |
1839 | |
1840 | debugfs_create_file(name: "keepalive" , mode: 0600, parent: ar->debugfs_phy, data: ar, |
1841 | fops: &fops_keepalive); |
1842 | |
1843 | debugfs_create_file(name: "disconnect_timeout" , mode: 0600, |
1844 | parent: ar->debugfs_phy, data: ar, fops: &fops_disconnect_timeout); |
1845 | |
1846 | debugfs_create_file(name: "create_qos" , mode: 0200, parent: ar->debugfs_phy, data: ar, |
1847 | fops: &fops_create_qos); |
1848 | |
1849 | debugfs_create_file(name: "delete_qos" , mode: 0200, parent: ar->debugfs_phy, data: ar, |
1850 | fops: &fops_delete_qos); |
1851 | |
1852 | debugfs_create_file(name: "bgscan_interval" , mode: 0200, |
1853 | parent: ar->debugfs_phy, data: ar, fops: &fops_bgscan_int); |
1854 | |
1855 | debugfs_create_file(name: "listen_interval" , mode: 0600, |
1856 | parent: ar->debugfs_phy, data: ar, fops: &fops_listen_int); |
1857 | |
1858 | debugfs_create_file(name: "power_params" , mode: 0200, parent: ar->debugfs_phy, data: ar, |
1859 | fops: &fops_power_params); |
1860 | |
1861 | return 0; |
1862 | } |
1863 | |
1864 | void ath6kl_debug_cleanup(struct ath6kl *ar) |
1865 | { |
1866 | skb_queue_purge(list: &ar->debug.fwlog_queue); |
1867 | complete(&ar->debug.fwlog_completion); |
1868 | kfree(objp: ar->debug.roam_tbl); |
1869 | } |
1870 | |
1871 | #endif |
1872 | |