1 /*
2 * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
3 * Basically selected code segments from usb-cdc.c and usb-rndis.c
4 *
5 * Copyright (C) 2020, Broadcom.
6 *
7 * Unless you and Broadcom execute a separate written software license
8 * agreement governing use of this software, this software is licensed to you
9 * under the terms of the GNU General Public License version 2 (the "GPL"),
10 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11 * following added to such license:
12 *
13 * As a special exception, the copyright holders of this software give you
14 * permission to link this software with independent modules, and to copy and
15 * distribute the resulting executable under terms of your choice, provided that
16 * you also meet, for each linked independent module, the terms and conditions of
17 * the license of that module. An independent module is a module which is not
18 * derived from this software. The special exception does not apply to any
19 * modifications of the software.
20 *
21 *
22 * <<Broadcom-WL-IPTag/Open:>>
23 *
24 * $Id$
25 */
26 #include <linux/kobject.h>
27 #include <linux/proc_fs.h>
28 #include <linux/sysfs.h>
29 #include <osl.h>
30 #include <dhd.h>
31 #include <dhd_dbg.h>
32 #include <dhd_linux_priv.h>
33 #if defined(DHD_ADPS_BAM_EXPORT) && defined(WL_BAM)
34 #include <wl_bam.h>
35 #endif /* DHD_ADPS_BAM_EXPORT && WL_BAM */
36 #ifdef PWRSTATS_SYSFS
37 #include <wldev_common.h>
38 #endif /* PWRSTATS_SYSFS */
39 #ifdef WL_CFG80211
40 #include <wl_cfg80211.h>
41 #endif /* WL_CFG80211 */
42 #ifdef CSI_SUPPORT
43 #include <dhd_csi.h>
44 #endif /* CSI_SUPPORT */
45
46 #ifdef SHOW_LOGTRACE
47 extern dhd_pub_t* g_dhd_pub;
48 static int dhd_ring_proc_open(struct inode *inode, struct file *file);
49 ssize_t dhd_ring_proc_read(struct file *file, char *buffer, size_t tt, loff_t *loff);
50
51 static const struct file_operations dhd_ring_proc_fops = {
52 .open = dhd_ring_proc_open,
53 .read = dhd_ring_proc_read,
54 .release = single_release,
55 };
56
57 static int
dhd_ring_proc_open(struct inode * inode,struct file * file)58 dhd_ring_proc_open(struct inode *inode, struct file *file)
59 {
60 int ret = BCME_ERROR;
61 if (inode) {
62 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
63 ret = single_open(file, 0, PDE_DATA(inode));
64 #else
65 /* This feature is not supported for lower kernel versions */
66 ret = single_open(file, 0, NULL);
67 #endif
68 } else {
69 DHD_ERROR(("%s: inode is NULL\n", __FUNCTION__));
70 }
71 return ret;
72 }
73
74 ssize_t
dhd_ring_proc_read(struct file * file,char __user * buffer,size_t tt,loff_t * loff)75 dhd_ring_proc_read(struct file *file, char __user *buffer, size_t tt, loff_t *loff)
76 {
77 trace_buf_info_t *trace_buf_info;
78 int ret = BCME_ERROR;
79 dhd_dbg_ring_t *ring = (dhd_dbg_ring_t *)((struct seq_file *)(file->private_data))->private;
80
81 if (ring == NULL) {
82 DHD_ERROR(("%s: ring is NULL\n", __FUNCTION__));
83 return ret;
84 }
85
86 ASSERT(g_dhd_pub);
87
88 trace_buf_info = (trace_buf_info_t *)MALLOCZ(g_dhd_pub->osh, sizeof(trace_buf_info_t));
89 if (trace_buf_info) {
90 dhd_dbg_read_ring_into_trace_buf(ring, trace_buf_info);
91 if (copy_to_user(buffer, (void*)trace_buf_info->buf, MIN(trace_buf_info->size, tt)))
92 {
93 ret = -EFAULT;
94 goto exit;
95 }
96 if (trace_buf_info->availability == BUF_NOT_AVAILABLE)
97 ret = BUF_NOT_AVAILABLE;
98 else
99 ret = trace_buf_info->size;
100 } else
101 DHD_ERROR(("Memory allocation Failed\n"));
102
103 exit:
104 if (trace_buf_info) {
105 MFREE(g_dhd_pub->osh, trace_buf_info, sizeof(trace_buf_info_t));
106 }
107 return ret;
108 }
109
110 void
dhd_dbg_ring_proc_create(dhd_pub_t * dhdp)111 dhd_dbg_ring_proc_create(dhd_pub_t *dhdp)
112 {
113 #ifdef DEBUGABILITY
114 dhd_dbg_ring_t *dbg_verbose_ring = NULL;
115
116 dbg_verbose_ring = dhd_dbg_get_ring_from_ring_id(dhdp, FW_VERBOSE_RING_ID);
117 if (dbg_verbose_ring) {
118 if (!proc_create_data("dhd_trace", S_IRUSR, NULL, &dhd_ring_proc_fops,
119 dbg_verbose_ring)) {
120 DHD_ERROR(("Failed to create /proc/dhd_trace procfs interface\n"));
121 } else {
122 DHD_ERROR(("Created /proc/dhd_trace procfs interface\n"));
123 }
124 } else {
125 DHD_ERROR(("dbg_verbose_ring is NULL, /proc/dhd_trace not created\n"));
126 }
127 #endif /* DEBUGABILITY */
128
129 #ifdef EWP_ECNTRS_LOGGING
130 if (!proc_create_data("dhd_ecounters", S_IRUSR, NULL, &dhd_ring_proc_fops,
131 dhdp->ecntr_dbg_ring)) {
132 DHD_ERROR(("Failed to create /proc/dhd_ecounters procfs interface\n"));
133 } else {
134 DHD_ERROR(("Created /proc/dhd_ecounters procfs interface\n"));
135 }
136 #endif /* EWP_ECNTRS_LOGGING */
137
138 #ifdef EWP_RTT_LOGGING
139 if (!proc_create_data("dhd_rtt", S_IRUSR, NULL, &dhd_ring_proc_fops,
140 dhdp->rtt_dbg_ring)) {
141 DHD_ERROR(("Failed to create /proc/dhd_rtt procfs interface\n"));
142 } else {
143 DHD_ERROR(("Created /proc/dhd_rtt procfs interface\n"));
144 }
145 #endif /* EWP_RTT_LOGGING */
146 }
147
148 void
dhd_dbg_ring_proc_destroy(dhd_pub_t * dhdp)149 dhd_dbg_ring_proc_destroy(dhd_pub_t *dhdp)
150 {
151 #ifdef DEBUGABILITY
152 remove_proc_entry("dhd_trace", NULL);
153 #endif /* DEBUGABILITY */
154
155 #ifdef EWP_ECNTRS_LOGGING
156 remove_proc_entry("dhd_ecounters", NULL);
157 #endif /* EWP_ECNTRS_LOGGING */
158
159 #ifdef EWP_RTT_LOGGING
160 remove_proc_entry("dhd_rtt", NULL);
161 #endif /* EWP_RTT_LOGGING */
162
163 }
164 #endif /* SHOW_LOGTRACE */
165
166 /* ----------------------------------------------------------------------------
167 * Infrastructure code for sysfs interface support for DHD
168 *
169 * What is sysfs interface?
170 * https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt
171 *
172 * Why sysfs interface?
173 * This is the Linux standard way of changing/configuring Run Time parameters
174 * for a driver. We can use this interface to control "linux" specific driver
175 * parameters.
176 *
177 * -----------------------------------------------------------------------------
178 */
179
180 #if defined(DHD_TRACE_WAKE_LOCK)
181 extern atomic_t trace_wklock_onoff;
182
183 /* Function to show the history buffer */
184 static ssize_t
show_wklock_trace(struct dhd_info * dev,char * buf)185 show_wklock_trace(struct dhd_info *dev, char *buf)
186 {
187 ssize_t ret = 0;
188 dhd_info_t *dhd = (dhd_info_t *)dev;
189
190 buf[ret] = '\n';
191 buf[ret+1] = 0;
192
193 dhd_wk_lock_stats_dump(&dhd->pub);
194 return ret+1;
195 }
196
197 /* Function to enable/disable wakelock trace */
198 static ssize_t
wklock_trace_onoff(struct dhd_info * dev,const char * buf,size_t count)199 wklock_trace_onoff(struct dhd_info *dev, const char *buf, size_t count)
200 {
201 unsigned long onoff;
202 dhd_info_t *dhd = (dhd_info_t *)dev;
203 BCM_REFERENCE(dhd);
204
205 onoff = bcm_strtoul(buf, NULL, 10);
206 if (onoff != 0 && onoff != 1) {
207 return -EINVAL;
208 }
209
210 atomic_set(&trace_wklock_onoff, onoff);
211 if (atomic_read(&trace_wklock_onoff)) {
212 DHD_ERROR(("ENABLE WAKLOCK TRACE\n"));
213 } else {
214 DHD_ERROR(("DISABLE WAKELOCK TRACE\n"));
215 }
216
217 return (ssize_t)(onoff+1);
218 }
219 #endif /* DHD_TRACE_WAKE_LOCK */
220
221 #ifdef DHD_LOG_DUMP
222 extern int logdump_periodic_flush;
223 extern int logdump_ecntr_enable;
224 static ssize_t
show_logdump_periodic_flush(struct dhd_info * dev,char * buf)225 show_logdump_periodic_flush(struct dhd_info *dev, char *buf)
226 {
227 ssize_t ret = 0;
228 unsigned long val;
229
230 val = logdump_periodic_flush;
231 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n", val);
232 return ret;
233 }
234
235 static ssize_t
logdump_periodic_flush_onoff(struct dhd_info * dev,const char * buf,size_t count)236 logdump_periodic_flush_onoff(struct dhd_info *dev, const char *buf, size_t count)
237 {
238 unsigned long val;
239
240 val = bcm_strtoul(buf, NULL, 10);
241
242 sscanf(buf, "%lu", &val);
243 if (val != 0 && val != 1) {
244 return -EINVAL;
245 }
246 logdump_periodic_flush = val;
247 return count;
248 }
249
250 static ssize_t
show_logdump_ecntr(struct dhd_info * dev,char * buf)251 show_logdump_ecntr(struct dhd_info *dev, char *buf)
252 {
253 ssize_t ret = 0;
254 unsigned long val;
255
256 val = logdump_ecntr_enable;
257 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n", val);
258 return ret;
259 }
260
261 static ssize_t
logdump_ecntr_onoff(struct dhd_info * dev,const char * buf,size_t count)262 logdump_ecntr_onoff(struct dhd_info *dev, const char *buf, size_t count)
263 {
264 unsigned long val;
265
266 val = bcm_strtoul(buf, NULL, 10);
267
268 sscanf(buf, "%lu", &val);
269 if (val != 0 && val != 1) {
270 return -EINVAL;
271 }
272 logdump_ecntr_enable = val;
273 return count;
274 }
275
276 #endif /* DHD_LOG_DUMP */
277
278 extern uint enable_ecounter;
279 static ssize_t
show_enable_ecounter(struct dhd_info * dev,char * buf)280 show_enable_ecounter(struct dhd_info *dev, char *buf)
281 {
282 ssize_t ret = 0;
283 unsigned long onoff;
284
285 onoff = enable_ecounter;
286 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
287 onoff);
288 return ret;
289 }
290
291 static ssize_t
ecounter_onoff(struct dhd_info * dev,const char * buf,size_t count)292 ecounter_onoff(struct dhd_info *dev, const char *buf, size_t count)
293 {
294 unsigned long onoff;
295 dhd_info_t *dhd = (dhd_info_t *)dev;
296 dhd_pub_t *dhdp;
297
298 if (!dhd) {
299 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
300 return count;
301 }
302 dhdp = &dhd->pub;
303 if (!FW_SUPPORTED(dhdp, ecounters)) {
304 DHD_ERROR(("%s: ecounters not supported by FW\n", __FUNCTION__));
305 return count;
306 }
307
308 onoff = bcm_strtoul(buf, NULL, 10);
309
310 sscanf(buf, "%lu", &onoff);
311 if (onoff != 0 && onoff != 1) {
312 return -EINVAL;
313 }
314
315 if (enable_ecounter == onoff) {
316 DHD_ERROR(("%s: ecounters already %d\n", __FUNCTION__, enable_ecounter));
317 return count;
318 }
319
320 enable_ecounter = onoff;
321 dhd_ecounter_configure(dhdp, enable_ecounter);
322
323 return count;
324 }
325
326 #if defined(DHD_QOS_ON_SOCK_FLOW)
327 #include <dhd_linux_sock_qos.h>
328
329 static ssize_t
show_sock_qos_onoff(struct dhd_info * dev,char * buf)330 show_sock_qos_onoff(struct dhd_info *dev, char *buf)
331 {
332 ssize_t ret = 0;
333 unsigned long onoff;
334 dhd_info_t *dhd = (dhd_info_t *)dev;
335
336 onoff = dhd_sock_qos_get_status(dhd);
337 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
338 onoff);
339 return ret;
340 }
341
342 static ssize_t
update_sock_qos_onoff(struct dhd_info * dev,const char * buf,size_t count)343 update_sock_qos_onoff(struct dhd_info *dev, const char *buf, size_t count)
344 {
345 unsigned long onoff;
346 dhd_info_t *dhd = (dhd_info_t *)dev;
347
348 onoff = bcm_strtoul(buf, NULL, 10);
349
350 sscanf(buf, "%lu", &onoff);
351 if (onoff != 0 && onoff != 1) {
352 return -EINVAL;
353 }
354
355 dhd_sock_qos_set_status(dhd, onoff);
356
357 return count;
358 }
359
360 static ssize_t
show_sock_qos_upgrade(struct dhd_info * dev,char * buf)361 show_sock_qos_upgrade(struct dhd_info *dev, char *buf)
362 {
363 ssize_t ret = 0;
364 unsigned long onoff;
365 dhd_info_t *dhd = (dhd_info_t *)dev;
366
367 onoff = dhd_sock_qos_get_force_upgrade(dhd);
368 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
369 onoff);
370 return ret;
371 }
372
373 static ssize_t
update_sock_qos_upgrade(struct dhd_info * dev,const char * buf,size_t count)374 update_sock_qos_upgrade(struct dhd_info *dev, const char *buf, size_t count)
375 {
376 unsigned long onoff;
377 dhd_info_t *dhd = (dhd_info_t *)dev;
378
379 onoff = bcm_strtoul(buf, NULL, 10);
380
381 sscanf(buf, "%lu", &onoff);
382 if (onoff != 0 && onoff != 1) {
383 return -EINVAL;
384 }
385
386 dhd_sock_qos_set_force_upgrade(dhd, onoff);
387
388 return count;
389 }
390
391 static ssize_t
show_sock_qos_numfl_upgrd_thresh(struct dhd_info * dev,char * buf)392 show_sock_qos_numfl_upgrd_thresh(struct dhd_info *dev, char *buf)
393 {
394 ssize_t ret = 0;
395 int upgrade_thresh;
396 dhd_info_t *dhd = (dhd_info_t *)dev;
397
398 upgrade_thresh = dhd_sock_qos_get_numfl_upgrd_thresh(dhd);
399 ret = scnprintf(buf, PAGE_SIZE - 1, "%d \n",
400 upgrade_thresh);
401 return ret;
402 }
403
404 static ssize_t
update_sock_qos_numfl_upgrd_thresh(struct dhd_info * dev,const char * buf,size_t count)405 update_sock_qos_numfl_upgrd_thresh(struct dhd_info *dev, const char *buf, size_t count)
406 {
407 int upgrade_thresh;
408 dhd_info_t *dhd = (dhd_info_t *)dev;
409
410 sscanf(buf, "%d", &upgrade_thresh);
411 if (upgrade_thresh < 0) {
412 return -EINVAL;
413 }
414
415 dhd_sock_qos_set_numfl_upgrd_thresh(dhd, upgrade_thresh);
416
417 return count;
418 }
419
420 static ssize_t
show_sock_qos_avgpktsize_thresh(struct dhd_info * dev,char * buf)421 show_sock_qos_avgpktsize_thresh(struct dhd_info *dev, char *buf)
422 {
423 ssize_t ret = 0;
424 unsigned long avgpktsize_low, avgpktsize_high;
425 dhd_info_t *dhd = (dhd_info_t *)dev;
426
427 dhd_sock_qos_get_avgpktsize_thresh(dhd, &avgpktsize_low, &avgpktsize_high);
428 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu %lu\n",
429 avgpktsize_low, avgpktsize_high);
430
431 return ret;
432 }
433
434 static ssize_t
update_sock_qos_avgpktsize_thresh(struct dhd_info * dev,const char * buf,size_t count)435 update_sock_qos_avgpktsize_thresh(struct dhd_info *dev, const char *buf, size_t count)
436 {
437 unsigned long avgpktsize_low, avgpktsize_high;
438 dhd_info_t *dhd = (dhd_info_t *)dev;
439
440 sscanf(buf, "%lu %lu", &avgpktsize_low, &avgpktsize_high);
441
442 dhd_sock_qos_set_avgpktsize_thresh(dhd, avgpktsize_low, avgpktsize_high);
443
444 return count;
445 }
446
447 static ssize_t
show_sock_qos_numpkts_thresh(struct dhd_info * dev,char * buf)448 show_sock_qos_numpkts_thresh(struct dhd_info *dev, char *buf)
449 {
450 ssize_t ret = 0;
451 unsigned long numpkts_low, numpkts_high;
452 dhd_info_t *dhd = (dhd_info_t *)dev;
453
454 dhd_sock_qos_get_numpkts_thresh(dhd, &numpkts_low, &numpkts_high);
455 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu %lu\n",
456 numpkts_low, numpkts_high);
457
458 return ret;
459 }
460
461 static ssize_t
update_sock_qos_numpkts_thresh(struct dhd_info * dev,const char * buf,size_t count)462 update_sock_qos_numpkts_thresh(struct dhd_info *dev, const char *buf, size_t count)
463 {
464 unsigned long numpkts_low, numpkts_high;
465 dhd_info_t *dhd = (dhd_info_t *)dev;
466
467 sscanf(buf, "%lu %lu", &numpkts_low, &numpkts_high);
468
469 dhd_sock_qos_set_numpkts_thresh(dhd, numpkts_low, numpkts_high);
470
471 return count;
472 }
473
474 static ssize_t
show_sock_qos_detectcnt_thresh(struct dhd_info * dev,char * buf)475 show_sock_qos_detectcnt_thresh(struct dhd_info *dev, char *buf)
476 {
477 ssize_t ret = 0;
478 unsigned char detectcnt_inc, detectcnt_dec;
479 dhd_info_t *dhd = (dhd_info_t *)dev;
480
481 dhd_sock_qos_get_detectcnt_thresh(dhd, &detectcnt_inc, &detectcnt_dec);
482 ret = scnprintf(buf, PAGE_SIZE - 1, "%d %d\n",
483 detectcnt_inc, detectcnt_dec);
484
485 return ret;
486 }
487
488 static ssize_t
update_sock_qos_detectcnt_thresh(struct dhd_info * dev,const char * buf,size_t count)489 update_sock_qos_detectcnt_thresh(struct dhd_info *dev, const char *buf, size_t count)
490 {
491 unsigned int detectcnt_inc, detectcnt_dec;
492 dhd_info_t *dhd = (dhd_info_t *)dev;
493
494 sscanf(buf, "%u %u", &detectcnt_inc, &detectcnt_dec);
495
496 dhd_sock_qos_set_detectcnt_thresh(dhd, detectcnt_inc, detectcnt_dec);
497
498 return count;
499 }
500
501 static ssize_t
show_sock_qos_detectcnt_upgrd_thresh(struct dhd_info * dev,char * buf)502 show_sock_qos_detectcnt_upgrd_thresh(struct dhd_info *dev, char *buf)
503 {
504 ssize_t ret = 0;
505 unsigned int detectcnt_upgrd_thresh;
506 dhd_info_t *dhd = (dhd_info_t *)dev;
507
508 detectcnt_upgrd_thresh = dhd_sock_qos_get_detectcnt_upgrd_thresh(dhd);
509 ret = scnprintf(buf, PAGE_SIZE - 1, "%d \n", detectcnt_upgrd_thresh);
510
511 return ret;
512 }
513
514 static ssize_t
update_sock_qos_detectcnt_upgrd_thresh(struct dhd_info * dev,const char * buf,size_t count)515 update_sock_qos_detectcnt_upgrd_thresh(struct dhd_info *dev, const char *buf, size_t count)
516 {
517 unsigned int detectcnt_upgrd_thresh;
518 dhd_info_t *dhd = (dhd_info_t *)dev;
519
520 sscanf(buf, "%u", &detectcnt_upgrd_thresh);
521
522 dhd_sock_qos_set_detectcnt_upgrd_thresh(dhd, detectcnt_upgrd_thresh);
523
524 return count;
525 }
526
527 static ssize_t
show_sock_qos_maxfl(struct dhd_info * dev,char * buf)528 show_sock_qos_maxfl(struct dhd_info *dev, char *buf)
529 {
530 ssize_t ret = 0;
531 unsigned int maxfl;
532 dhd_info_t *dhd = (dhd_info_t *)dev;
533
534 maxfl = dhd_sock_qos_get_maxfl(dhd);
535 ret = scnprintf(buf, PAGE_SIZE - 1, "%u \n", maxfl);
536
537 return ret;
538 }
539
540 static ssize_t
update_sock_qos_maxfl(struct dhd_info * dev,const char * buf,size_t count)541 update_sock_qos_maxfl(struct dhd_info *dev, const char *buf, size_t count)
542 {
543 unsigned int maxfl;
544 dhd_info_t *dhd = (dhd_info_t *)dev;
545
546 sscanf(buf, "%u", &maxfl);
547
548 dhd_sock_qos_set_maxfl(dhd, maxfl);
549
550 return count;
551 }
552
553 static ssize_t
show_sock_qos_stats(struct dhd_info * dev,char * buf)554 show_sock_qos_stats(struct dhd_info *dev, char *buf)
555 {
556 dhd_info_t *dhd = (dhd_info_t *)dev;
557
558 dhd_sock_qos_show_stats(dhd, buf, PAGE_SIZE);
559
560 return PAGE_SIZE - 1;
561 }
562
563 static ssize_t
clear_sock_qos_stats(struct dhd_info * dev,const char * buf,size_t count)564 clear_sock_qos_stats(struct dhd_info *dev, const char *buf, size_t count)
565 {
566 unsigned long clear;
567 dhd_info_t *dhd = (dhd_info_t *)dev;
568
569 clear = bcm_strtoul(buf, NULL, 10);
570
571 sscanf(buf, "%lu", &clear);
572 if (clear != 0) {
573 return -EINVAL;
574 }
575
576 dhd_sock_qos_clear_stats(dhd);
577
578 return count;
579 }
580
581 #ifdef DHD_QOS_ON_SOCK_FLOW_UT
582
583 /*
584 * test_id sub_id Description
585 * ------ ------ -----------
586 * 1 0 psk_qos->sk_fl
587 * The number of free sk_fl entries in the Table is exhausted
588 * and more sockets are still getting created
589 *
590 * 1 1 psk_qos->sk_fl
591 * is Full for more than x seconds, there are lot of periodic
592 * flows, but none of them are detected for upgrade for more
593 * than 'x' seconds
594 *
595 * 2 Force upgrade the socket flows to reach skfl_upgrade_thresh
596 * check the behaviour
597 *
598 * Downgrade one of the sk_fls and check if the 'next' pending
599 * sk_fl is getting upgraded. The sk_fl getting upgraded
600 * should follow FIFO scheme.
601 *
602 * 3 Upgrade a socket flow ... after some time downgrade the
603 * same and check if the sk_fl is actually getting downgraded
604 * Keep switching the behavior every 'x' seconds and observe
605 * the switches
606 */
607 static ssize_t
do_sock_qos_unit_test(struct dhd_info * dev,const char * buf,size_t count)608 do_sock_qos_unit_test(struct dhd_info *dev, const char *buf, size_t count)
609 {
610 unsigned int test_id = 0;
611 unsigned int sub_id = 0;
612 dhd_info_t *dhd = (dhd_info_t *)dev;
613 int ret;
614
615 BCM_REFERENCE(dhd);
616
617 ret = sscanf(buf, "%d %d", &test_id, &sub_id);
618 if (ret < 1) {
619 return -EINVAL;
620 }
621
622 return count;
623 }
624
625 #endif /* DHD_QOS_ON_SOCK_FLOW_UT */
626 #endif /* DHD_QOS_ON_SOCK_FLOW */
627
628 #ifdef DHD_SSSR_DUMP
629 static ssize_t
show_sssr_enab(struct dhd_info * dev,char * buf)630 show_sssr_enab(struct dhd_info *dev, char *buf)
631 {
632 ssize_t ret = 0;
633 unsigned long onoff;
634
635 onoff = sssr_enab;
636 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
637 onoff);
638 return ret;
639 }
640
641 static ssize_t
set_sssr_enab(struct dhd_info * dev,const char * buf,size_t count)642 set_sssr_enab(struct dhd_info *dev, const char *buf, size_t count)
643 {
644 unsigned long onoff;
645
646 onoff = bcm_strtoul(buf, NULL, 10);
647
648 sscanf(buf, "%lu", &onoff);
649 if (onoff != 0 && onoff != 1) {
650 return -EINVAL;
651 }
652
653 sssr_enab = (uint)onoff;
654
655 return count;
656 }
657
658 static ssize_t
show_fis_enab(struct dhd_info * dev,char * buf)659 show_fis_enab(struct dhd_info *dev, char *buf)
660 {
661 ssize_t ret = 0;
662 unsigned long onoff;
663
664 onoff = fis_enab;
665 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
666 onoff);
667 return ret;
668 }
669
670 static ssize_t
set_fis_enab(struct dhd_info * dev,const char * buf,size_t count)671 set_fis_enab(struct dhd_info *dev, const char *buf, size_t count)
672 {
673 unsigned long onoff;
674
675 onoff = bcm_strtoul(buf, NULL, 10);
676
677 sscanf(buf, "%lu", &onoff);
678 if (onoff != 0 && onoff != 1) {
679 return -EINVAL;
680 }
681
682 fis_enab = (uint)onoff;
683
684 return count;
685 }
686 #endif /* DHD_SSSR_DUMP */
687
688 #define FMT_BUFSZ 32
689 extern char firmware_path[];
690
691 static ssize_t
show_firmware_path(struct dhd_info * dev,char * buf)692 show_firmware_path(struct dhd_info *dev, char *buf)
693 {
694 ssize_t ret = 0;
695 ret = scnprintf(buf, PAGE_SIZE - 1, "%s\n", firmware_path);
696
697 return ret;
698 }
699
700 static ssize_t
store_firmware_path(struct dhd_info * dev,const char * buf,size_t count)701 store_firmware_path(struct dhd_info *dev, const char *buf, size_t count)
702 {
703 char fmt_spec[FMT_BUFSZ] = "";
704
705 if ((int)strlen(buf) >= MOD_PARAM_PATHLEN) {
706 return -EINVAL;
707 }
708
709 snprintf(fmt_spec, FMT_BUFSZ, "%%%ds", MOD_PARAM_PATHLEN - 1);
710 sscanf(buf, fmt_spec, firmware_path);
711
712 return count;
713 }
714
715 extern char nvram_path[];
716
717 static ssize_t
show_nvram_path(struct dhd_info * dev,char * buf)718 show_nvram_path(struct dhd_info *dev, char *buf)
719 {
720 ssize_t ret = 0;
721 ret = scnprintf(buf, PAGE_SIZE - 1, "%s\n", nvram_path);
722
723 return ret;
724 }
725
726 static ssize_t
store_nvram_path(struct dhd_info * dev,const char * buf,size_t count)727 store_nvram_path(struct dhd_info *dev, const char *buf, size_t count)
728 {
729 char fmt_spec[FMT_BUFSZ] = "";
730
731 if ((int)strlen(buf) >= MOD_PARAM_PATHLEN) {
732 return -EINVAL;
733 }
734
735 snprintf(fmt_spec, FMT_BUFSZ, "%%%ds", MOD_PARAM_PATHLEN - 1);
736 sscanf(buf, fmt_spec, nvram_path);
737
738 return count;
739 }
740
741 #ifdef PWRSTATS_SYSFS
742 typedef struct wl_pwrstats_sysfs {
743 uint64 current_ts;
744 uint64 pm_cnt;
745 uint64 pm_dur;
746 uint64 pm_last_entry_us;
747 uint64 awake_cnt;
748 uint64 awake_dur;
749 uint64 awake_last_entry_us;
750 uint64 l0_cnt;
751 uint64 l0_dur_us;
752 uint64 l1_cnt;
753 uint64 l1_dur_us;
754 uint64 l1_1_cnt;
755 uint64 l1_1_dur_us;
756 uint64 l1_2_cnt;
757 uint64 l1_2_dur_us;
758 uint64 l2_cnt;
759 uint64 l2_dur_us;
760 } wl_pwrstats_sysfs_t;
761
762 uint64 last_delta = 0;
763 wl_pwrstats_sysfs_t accumstats = {0, };
764 wl_pwrstats_sysfs_t laststats = {0, };
765 static const char pwrstr_cnt[] = "count:";
766 static const char pwrstr_dur[] = "duration_usec:";
767 static const char pwrstr_ts[] = "last_entry_timestamp_usec:";
768
update_pwrstats_cum(uint64 * accum,uint64 * last,uint64 * now,bool force)769 void update_pwrstats_cum(uint64 *accum, uint64 *last, uint64 *now, bool force)
770 {
771 if (accum) { /* accumulation case, ex; counts, duration */
772 if (*now < *last) {
773 if (force || ((*last - *now) > USEC_PER_MSEC)) {
774 /* not to update accum for pm_dur/awake_dur case */
775 *accum += *now;
776 *last = *now;
777 }
778 } else {
779 *accum += (*now - *last);
780 *last = *now;
781 }
782 } else if (*now != 0) { /* last entry timestamp case */
783 *last = *now + last_delta;
784 }
785 }
786
787 static const uint16 pwrstats_req_type[] = {
788 WL_PWRSTATS_TYPE_PCIE,
789 WL_PWRSTATS_TYPE_PM_ACCUMUL
790 };
791 #define PWRSTATS_REQ_TYPE_NUM sizeof(pwrstats_req_type) / sizeof(uint16)
792 #define PWRSTATS_IOV_BUF_LEN OFFSETOF(wl_pwrstats_t, data) \
793 + sizeof(uint32) * PWRSTATS_REQ_TYPE_NUM \
794 + sizeof(wl_pwr_pcie_stats_t) \
795 + sizeof(wl_pwr_pm_accum_stats_v1_t) \
796 + (uint)strlen("pwrstats") + 1
797
798 static ssize_t
show_pwrstats_path(struct dhd_info * dev,char * buf)799 show_pwrstats_path(struct dhd_info *dev, char *buf)
800 {
801 int err = 0;
802 void *p_data = NULL;
803 ssize_t ret = 0;
804 dhd_info_t *dhd = (dhd_info_t *)dev;
805 struct net_device *ndev = dhd_linux_get_primary_netdev(&dhd->pub);
806 char *iovar_buf = NULL;
807 wl_pwrstats_query_t *p_query = NULL;
808 wl_pwrstats_sysfs_t pwrstats_sysfs = {0, };
809 wl_pwrstats_t *pwrstats;
810 uint len, taglen, i;
811 uint16 type;
812 uint64 ts_sec, ts_usec, time_delta;
813
814 ASSERT(g_dhd_pub);
815
816 len = PWRSTATS_IOV_BUF_LEN;
817 iovar_buf = (char *)MALLOCZ(g_dhd_pub->osh, len);
818 if (iovar_buf == NULL) {
819 DHD_ERROR(("%s Fail to malloc buffer\n", __FUNCTION__));
820 goto done;
821 }
822
823 /* Alloc req buffer */
824 len = OFFSETOF(wl_pwrstats_query_t, type) +
825 PWRSTATS_REQ_TYPE_NUM * sizeof(uint16);
826 p_query = (wl_pwrstats_query_t *)MALLOCZ(g_dhd_pub->osh, len);
827 if (p_query == NULL) {
828 DHD_ERROR(("%s Fail to malloc buffer\n", __FUNCTION__));
829 goto done;
830 }
831
832 /* Build a list of types */
833 p_query->length = PWRSTATS_REQ_TYPE_NUM;
834 for (i = 0; i < PWRSTATS_REQ_TYPE_NUM; i++) {
835 p_query->type[i] = pwrstats_req_type[i];
836 }
837
838 /* Query with desired type list */
839 err = wldev_iovar_getbuf(ndev, "pwrstats", p_query, len,
840 iovar_buf, PWRSTATS_IOV_BUF_LEN, NULL);
841 if (err != BCME_OK) {
842 DHD_ERROR(("error (%d) - size = %zu\n", err, sizeof(wl_pwrstats_t)));
843 goto done;
844 }
845
846 /* Check version */
847 pwrstats = (wl_pwrstats_t *) iovar_buf;
848 if (dtoh16(pwrstats->version) != WL_PWRSTATS_VERSION) {
849 DHD_ERROR(("PWRSTATS Version mismatch\n"));
850 goto done;
851 }
852
853 /* Parse TLVs */
854 len = dtoh16(pwrstats->length) - WL_PWR_STATS_HDRLEN;
855 p_data = pwrstats->data;
856 do {
857 type = dtoh16(((uint16*)p_data)[0]);
858 taglen = dtoh16(((uint16*)p_data)[1]);
859
860 if ((taglen < BCM_XTLV_HDR_SIZE) || (taglen > len)) {
861 DHD_ERROR(("Bad len %d for tag %d, remaining len %d\n",
862 taglen, type, len));
863 goto done;
864 }
865
866 if (taglen & 0xF000) {
867 DHD_ERROR(("Resrved bits in len %d for tag %d, remaining len %d\n",
868 taglen, type, len));
869 goto done;
870 }
871
872 switch (type) {
873 case WL_PWRSTATS_TYPE_PCIE:
874 {
875 wl_pwr_pcie_stats_t *stats =
876 (wl_pwr_pcie_stats_t *)p_data;
877
878 if (taglen < sizeof(wl_pwr_pcie_stats_t)) {
879 DHD_ERROR(("Short len for %d: %d < %d\n",
880 type, taglen, (int)sizeof(wl_pwr_pcie_stats_t)));
881 goto done;
882 }
883
884 if (dtoh32(stats->pcie.l0_cnt) == 0) {
885 DHD_ERROR(("link stats are not supported for this pcie core\n"));
886 }
887
888 pwrstats_sysfs.l0_cnt = dtoh32(stats->pcie.l0_cnt);
889 pwrstats_sysfs.l0_dur_us = dtoh32(stats->pcie.l0_usecs);
890 pwrstats_sysfs.l1_cnt = dtoh32(stats->pcie.l1_cnt);
891 pwrstats_sysfs.l1_dur_us = dtoh32(stats->pcie.l1_usecs);
892 pwrstats_sysfs.l1_1_cnt = dtoh32(stats->pcie.l1_1_cnt);
893 pwrstats_sysfs.l1_1_dur_us = dtoh32(stats->pcie.l1_1_usecs);
894 pwrstats_sysfs.l1_2_cnt = dtoh32(stats->pcie.l1_2_cnt);
895 pwrstats_sysfs.l1_2_dur_us = dtoh32(stats->pcie.l1_2_usecs);
896 pwrstats_sysfs.l2_cnt = dtoh32(stats->pcie.l2_cnt);
897 pwrstats_sysfs.l2_dur_us = dtoh32(stats->pcie.l2_usecs);
898 }
899 break;
900
901 case WL_PWRSTATS_TYPE_PM_ACCUMUL:
902 {
903 wl_pwr_pm_accum_stats_v1_t *stats =
904 (wl_pwr_pm_accum_stats_v1_t *)p_data;
905
906 if (taglen < sizeof(wl_pwr_pm_accum_stats_v1_t)) {
907 DHD_ERROR(("Short len for %d: %d < %d\n", type,
908 taglen, (int)sizeof(wl_pwr_pm_accum_stats_v1_t)));
909 goto done;
910 }
911
912 pwrstats_sysfs.current_ts =
913 dtoh64(stats->accum_data.current_ts);
914 pwrstats_sysfs.pm_cnt =
915 dtoh64(stats->accum_data.pm_cnt);
916 pwrstats_sysfs.pm_dur =
917 dtoh64(stats->accum_data.pm_dur);
918 pwrstats_sysfs.pm_last_entry_us =
919 dtoh64(stats->accum_data.pm_last_entry_us);
920 pwrstats_sysfs.awake_cnt =
921 dtoh64(stats->accum_data.awake_cnt);
922 pwrstats_sysfs.awake_dur =
923 dtoh64(stats->accum_data.awake_dur);
924 pwrstats_sysfs.awake_last_entry_us =
925 dtoh64(stats->accum_data.awake_last_entry_us);
926 }
927 break;
928
929 default:
930 DHD_ERROR(("Skipping uknown %d-byte tag %d\n", taglen, type));
931 break;
932 }
933
934 /* Adjust length to account for padding, but don't exceed total len */
935 taglen = (ROUNDUP(taglen, 4) > len) ? len : ROUNDUP(taglen, 4);
936 len -= taglen;
937 *(uint8**)&p_data += taglen;
938 } while (len >= BCM_XTLV_HDR_SIZE);
939
940 OSL_GET_LOCALTIME(&ts_sec, &ts_usec);
941 time_delta = ts_sec * USEC_PER_SEC + ts_usec - pwrstats_sysfs.current_ts;
942 if ((time_delta > last_delta) &&
943 ((time_delta - last_delta) > USEC_PER_SEC)) {
944 last_delta = time_delta;
945 }
946
947 update_pwrstats_cum(&accumstats.awake_cnt, &laststats.awake_cnt,
948 &pwrstats_sysfs.awake_cnt, TRUE);
949 update_pwrstats_cum(&accumstats.awake_dur, &laststats.awake_dur,
950 &pwrstats_sysfs.awake_dur, FALSE);
951 update_pwrstats_cum(&accumstats.pm_cnt, &laststats.pm_cnt, &pwrstats_sysfs.pm_cnt,
952 TRUE);
953 update_pwrstats_cum(&accumstats.pm_dur, &laststats.pm_dur, &pwrstats_sysfs.pm_dur,
954 FALSE);
955 update_pwrstats_cum(NULL, &laststats.awake_last_entry_us,
956 &pwrstats_sysfs.awake_last_entry_us, TRUE);
957 update_pwrstats_cum(NULL, &laststats.pm_last_entry_us,
958 &pwrstats_sysfs.pm_last_entry_us, TRUE);
959
960 ret += scnprintf(buf, PAGE_SIZE - 1, "AWAKE:\n");
961 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
962 accumstats.awake_cnt);
963 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
964 accumstats.awake_dur);
965 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_ts,
966 laststats.awake_last_entry_us);
967 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "ASLEEP:\n");
968 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
969 accumstats.pm_cnt);
970 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
971 accumstats.pm_dur);
972 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_ts,
973 laststats.pm_last_entry_us);
974
975 update_pwrstats_cum(&accumstats.l0_cnt, &laststats.l0_cnt, &pwrstats_sysfs.l0_cnt,
976 TRUE);
977 update_pwrstats_cum(&accumstats.l0_dur_us, &laststats.l0_dur_us,
978 &pwrstats_sysfs.l0_dur_us, TRUE);
979 update_pwrstats_cum(&accumstats.l1_cnt, &laststats.l1_cnt, &pwrstats_sysfs.l1_cnt,
980 TRUE);
981 update_pwrstats_cum(&accumstats.l1_dur_us, &laststats.l1_dur_us,
982 &pwrstats_sysfs.l1_dur_us, TRUE);
983 update_pwrstats_cum(&accumstats.l1_1_cnt, &laststats.l1_1_cnt,
984 &pwrstats_sysfs.l1_1_cnt, TRUE);
985 update_pwrstats_cum(&accumstats.l1_1_dur_us, &laststats.l1_1_dur_us,
986 &pwrstats_sysfs.l1_1_dur_us, TRUE);
987 update_pwrstats_cum(&accumstats.l1_2_cnt, &laststats.l1_2_cnt,
988 &pwrstats_sysfs.l1_2_cnt, TRUE);
989 update_pwrstats_cum(&accumstats.l1_2_dur_us, &laststats.l1_2_dur_us,
990 &pwrstats_sysfs.l1_2_dur_us, TRUE);
991 update_pwrstats_cum(&accumstats.l2_cnt, &laststats.l2_cnt, &pwrstats_sysfs.l2_cnt,
992 TRUE);
993 update_pwrstats_cum(&accumstats.l2_dur_us, &laststats.l2_dur_us,
994 &pwrstats_sysfs.l2_dur_us, TRUE);
995
996 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "L0:\n");
997 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
998 accumstats.l0_cnt);
999 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
1000 accumstats.l0_dur_us);
1001 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "L1:\n");
1002 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
1003 accumstats.l1_cnt);
1004 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
1005 accumstats.l1_dur_us);
1006 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "L1_1:\n");
1007 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
1008 accumstats.l1_1_cnt);
1009 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
1010 accumstats.l1_1_dur_us);
1011 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "L1_2:\n");
1012 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
1013 accumstats.l1_2_cnt);
1014 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
1015 accumstats.l1_2_dur_us);
1016 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "L2:\n");
1017 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_cnt,
1018 accumstats.l2_cnt);
1019 ret += scnprintf(buf + ret, PAGE_SIZE - 1 - ret, "%s 0x%0llx\n", pwrstr_dur,
1020 accumstats.l2_dur_us);
1021
1022 done:
1023 if (p_query) {
1024 MFREE(g_dhd_pub->osh, p_query, len);
1025 }
1026 if (iovar_buf) {
1027 MFREE(g_dhd_pub->osh, iovar_buf, PWRSTATS_IOV_BUF_LEN);
1028 }
1029
1030 return ret;
1031 }
1032 #endif /* PWRSTATS_SYSFS */
1033
1034 /*
1035 * Generic Attribute Structure for DHD.
1036 * If we have to add a new sysfs entry under /sys/bcm-dhd/, we have
1037 * to instantiate an object of type dhd_attr, populate it with
1038 * the required show/store functions (ex:- dhd_attr_cpumask_primary)
1039 * and add the object to default_attrs[] array, that gets registered
1040 * to the kobject of dhd (named bcm-dhd).
1041 */
1042
1043 struct dhd_attr {
1044 struct attribute attr;
1045 ssize_t(*show)(struct dhd_info *, char *);
1046 ssize_t(*store)(struct dhd_info *, const char *, size_t count);
1047 };
1048
1049 #if defined(DHD_TRACE_WAKE_LOCK)
1050 static struct dhd_attr dhd_attr_wklock =
1051 __ATTR(wklock_trace, 0660, show_wklock_trace, wklock_trace_onoff);
1052 #endif /* defined(DHD_TRACE_WAKE_LOCK */
1053
1054 #ifdef DHD_LOG_DUMP
1055 static struct dhd_attr dhd_attr_logdump_periodic_flush =
1056 __ATTR(logdump_periodic_flush, 0660, show_logdump_periodic_flush,
1057 logdump_periodic_flush_onoff);
1058 static struct dhd_attr dhd_attr_logdump_ecntr =
1059 __ATTR(logdump_ecntr_enable, 0660, show_logdump_ecntr,
1060 logdump_ecntr_onoff);
1061 #endif /* DHD_LOG_DUMP */
1062
1063 static struct dhd_attr dhd_attr_ecounters =
1064 __ATTR(ecounters, 0660, show_enable_ecounter, ecounter_onoff);
1065
1066 #if defined(DHD_QOS_ON_SOCK_FLOW)
1067 static struct dhd_attr dhd_attr_sock_qos_onoff =
1068 __ATTR(sock_qos_onoff, 0660, show_sock_qos_onoff, update_sock_qos_onoff);
1069
1070 static struct dhd_attr dhd_attr_sock_qos_stats =
1071 __ATTR(sock_qos_stats, 0660, show_sock_qos_stats, clear_sock_qos_stats);
1072
1073 static struct dhd_attr dhd_attr_sock_qos_upgrade =
1074 __ATTR(sock_qos_upgrade, 0660, show_sock_qos_upgrade, update_sock_qos_upgrade);
1075
1076 static struct dhd_attr dhd_attr_sock_qos_numfl_upgrd_thresh =
1077 __ATTR(sock_qos_numfl_upgrd_thresh, 0660, show_sock_qos_numfl_upgrd_thresh,
1078 update_sock_qos_numfl_upgrd_thresh);
1079
1080 static struct dhd_attr dhd_attr_sock_qos_avgpktsize_thresh =
1081 __ATTR(sock_qos_avgpktsize_thresh, 0660, show_sock_qos_avgpktsize_thresh,
1082 update_sock_qos_avgpktsize_thresh);
1083
1084 static struct dhd_attr dhd_attr_sock_qos_numpkts_thresh =
1085 __ATTR(sock_qos_numpkts_thresh, 0660, show_sock_qos_numpkts_thresh,
1086 update_sock_qos_numpkts_thresh);
1087
1088 static struct dhd_attr dhd_attr_sock_qos_detectcnt_thresh =
1089 __ATTR(sock_qos_detectcnt_thresh, 0660, show_sock_qos_detectcnt_thresh,
1090 update_sock_qos_detectcnt_thresh);
1091
1092 static struct dhd_attr dhd_attr_sock_qos_detectcnt_upgrd_thresh =
1093 __ATTR(sock_qos_detectcnt_upgrd_thresh, 0660, show_sock_qos_detectcnt_upgrd_thresh,
1094 update_sock_qos_detectcnt_upgrd_thresh);
1095
1096 static struct dhd_attr dhd_attr_sock_qos_maxfl =
1097 __ATTR(sock_qos_maxfl, 0660, show_sock_qos_maxfl,
1098 update_sock_qos_maxfl);
1099 #if defined(DHD_QOS_ON_SOCK_FLOW_UT)
1100 static struct dhd_attr dhd_attr_sock_qos_unit_test =
1101 __ATTR(sock_qos_unit_test, 0660, NULL, do_sock_qos_unit_test);
1102 #endif
1103 #endif /* DHD_QOS_ON_SOCK_FLOW */
1104
1105 #ifdef DHD_SSSR_DUMP
1106 static struct dhd_attr dhd_attr_sssr_enab =
1107 __ATTR(sssr_enab, 0660, show_sssr_enab, set_sssr_enab);
1108 static struct dhd_attr dhd_attr_fis_enab =
1109 __ATTR(fis_enab, 0660, show_fis_enab, set_fis_enab);
1110 #endif /* DHD_SSSR_DUMP */
1111
1112 static struct dhd_attr dhd_attr_firmware_path =
1113 __ATTR(firmware_path, 0660, show_firmware_path, store_firmware_path);
1114
1115 static struct dhd_attr dhd_attr_nvram_path =
1116 __ATTR(nvram_path, 0660, show_nvram_path, store_nvram_path);
1117
1118 #ifdef PWRSTATS_SYSFS
1119 static struct dhd_attr dhd_attr_pwrstats_path =
1120 __ATTR(power_stats, 0660, show_pwrstats_path, NULL);
1121 #endif /* PWRSTATS_SYSFS */
1122
1123 #define to_dhd(k) container_of(k, struct dhd_info, dhd_kobj)
1124 #define to_attr(a) container_of(a, struct dhd_attr, attr)
1125
1126 #ifdef DHD_MAC_ADDR_EXPORT
1127 struct ether_addr sysfs_mac_addr;
1128 static ssize_t
show_mac_addr(struct dhd_info * dev,char * buf)1129 show_mac_addr(struct dhd_info *dev, char *buf)
1130 {
1131 ssize_t ret = 0;
1132
1133 ret = scnprintf(buf, PAGE_SIZE - 1, MACF,
1134 (uint32)sysfs_mac_addr.octet[0], (uint32)sysfs_mac_addr.octet[1],
1135 (uint32)sysfs_mac_addr.octet[2], (uint32)sysfs_mac_addr.octet[3],
1136 (uint32)sysfs_mac_addr.octet[4], (uint32)sysfs_mac_addr.octet[5]);
1137
1138 return ret;
1139 }
1140
1141 static ssize_t
set_mac_addr(struct dhd_info * dev,const char * buf,size_t count)1142 set_mac_addr(struct dhd_info *dev, const char *buf, size_t count)
1143 {
1144 if (!bcm_ether_atoe(buf, &sysfs_mac_addr)) {
1145 DHD_ERROR(("Invalid Mac Address \n"));
1146 return -EINVAL;
1147 }
1148
1149 DHD_ERROR(("Mac Address set with "MACDBG"\n", MAC2STRDBG(&sysfs_mac_addr)));
1150
1151 return count;
1152 }
1153
1154 static struct dhd_attr dhd_attr_macaddr =
1155 __ATTR(mac_addr, 0660, show_mac_addr, set_mac_addr);
1156 #endif /* DHD_MAC_ADDR_EXPORT */
1157
1158 #ifdef DHD_FW_COREDUMP
1159 /*
1160 * XXX The filename to store memdump is defined for each platform.
1161 * - The default path of CUSTOMER_HW4 device is "PLATFORM_PATH/.memdump.info"
1162 * - Brix platform will take default path "/installmedia/.memdump.info"
1163 * New platforms can add their ifdefs accordingly below.
1164 */
1165
1166 #ifdef CONFIG_X86
1167 #if defined(OEM_ANDROID)
1168 #define MEMDUMPINFO_LIVE PLATFORM_PATH".memdump.info"
1169 #define MEMDUMPINFO_INST "/data/.memdump.info"
1170 #define MEMDUMPINFO MEMDUMPINFO_LIVE
1171 #else /* FC19 and Others */
1172 #define MEMDUMPINFO PLATFORM_PATH".memdump.info"
1173 #endif /* OEM_ANDROID */
1174 #else /* For non x86 platforms */
1175 #define MEMDUMPINFO PLATFORM_PATH".memdump.info"
1176 #endif /* CONFIG_X86 */
1177
1178 uint32
get_mem_val_from_file(void)1179 get_mem_val_from_file(void)
1180 {
1181 struct file *fp = NULL;
1182 uint32 mem_val = DUMP_MEMFILE_MAX;
1183 char *p_mem_val = NULL;
1184 char *filepath = MEMDUMPINFO;
1185 int ret = 0;
1186
1187 /* Read memdump info from the file */
1188 fp = filp_open(filepath, O_RDONLY, 0);
1189 if (IS_ERR(fp)) {
1190 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
1191 #if defined(CONFIG_X86) && defined(OEM_ANDROID)
1192 /* Check if it is Live Brix Image */
1193 if (strcmp(filepath, MEMDUMPINFO_LIVE) != 0) {
1194 goto done;
1195 }
1196 /* Try if it is Installed Brix Image */
1197 filepath = MEMDUMPINFO_INST;
1198 DHD_ERROR(("%s: Try File [%s]\n", __FUNCTION__, filepath));
1199 fp = filp_open(filepath, O_RDONLY, 0);
1200 if (IS_ERR(fp)) {
1201 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
1202 goto done;
1203 }
1204 #else /* Non Brix Android platform */
1205 goto done;
1206 #endif /* CONFIG_X86 && OEM_ANDROID */
1207 }
1208
1209 /* Handle success case */
1210 ret = kernel_read_compat(fp, 0, (char *)&mem_val, sizeof(uint32));
1211 if (ret < 0) {
1212 DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
1213 filp_close(fp, NULL);
1214 goto done;
1215 }
1216
1217 p_mem_val = (char*)&mem_val;
1218 p_mem_val[sizeof(uint32) - 1] = '\0';
1219 mem_val = bcm_atoi(p_mem_val);
1220
1221 filp_close(fp, NULL);
1222
1223 done:
1224 return mem_val;
1225 }
1226
dhd_get_memdump_info(dhd_pub_t * dhd)1227 void dhd_get_memdump_info(dhd_pub_t *dhd)
1228 {
1229 #ifndef DHD_EXPORT_CNTL_FILE
1230 uint32 mem_val = DUMP_MEMFILE_MAX;
1231
1232 mem_val = get_mem_val_from_file();
1233 if (mem_val != DUMP_MEMFILE_MAX)
1234 dhd->memdump_enabled = mem_val;
1235 #ifdef DHD_INIT_DEFAULT_MEMDUMP
1236 if (mem_val == 0 || mem_val == DUMP_MEMFILE_MAX)
1237 mem_val = DUMP_MEMFILE_BUGON;
1238 #endif /* DHD_INIT_DEFAULT_MEMDUMP */
1239 #else
1240 #ifdef DHD_INIT_DEFAULT_MEMDUMP
1241 if (dhd->memdump_enabled == 0 || dhd->memdump_enabled == DUMP_MEMFILE_MAX)
1242 dhd->memdump_enabled = DUMP_MEMFILE;
1243 #endif /* DHD_INIT_DEFAULT_MEMDUMP */
1244 #endif /* !DHD_EXPORT_CNTL_FILE */
1245 #ifdef BCMQT
1246 /* In QT environment collecting memdump on FW TRAP, IOVAR timeouts,
1247 * is taking more time and makes system unresponsive so disabling it.
1248 * if needed memdump can be collected through 'dhd upload' command.
1249 */
1250 dhd->memdump_enabled = DUMP_DISABLED;
1251 #endif
1252 #ifdef DHD_DETECT_CONSECUTIVE_MFG_HANG
1253 /* override memdump_enabled value to avoid once trap issues */
1254 if (dhd_bus_get_fw_mode(dhd) == DHD_FLAG_MFG_MODE &&
1255 (dhd->memdump_enabled == DUMP_MEMONLY ||
1256 dhd->memdump_enabled == DUMP_MEMFILE_BUGON)) {
1257 dhd->memdump_enabled = DUMP_MEMFILE;
1258 DHD_ERROR(("%s : Override memdump_value to %d\n",
1259 __FUNCTION__, dhd->memdump_enabled));
1260 }
1261 #endif /* DHD_DETECT_CONSECUTIVE_MFG_HANG */
1262 DHD_ERROR(("%s: MEMDUMP ENABLED = %u\n", __FUNCTION__, dhd->memdump_enabled));
1263 }
1264
1265 #ifdef DHD_EXPORT_CNTL_FILE
1266 static ssize_t
show_memdump_info(struct dhd_info * dev,char * buf)1267 show_memdump_info(struct dhd_info *dev, char *buf)
1268 {
1269 ssize_t ret = 0;
1270 dhd_pub_t *dhdp;
1271
1272 if (!dev) {
1273 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
1274 return ret;
1275 }
1276
1277 dhdp = &dev->pub;
1278 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", dhdp->memdump_enabled);
1279 return ret;
1280 }
1281
1282 static ssize_t
set_memdump_info(struct dhd_info * dev,const char * buf,size_t count)1283 set_memdump_info(struct dhd_info *dev, const char *buf, size_t count)
1284 {
1285 unsigned long memval;
1286 dhd_pub_t *dhdp;
1287
1288 if (!dev) {
1289 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
1290 return count;
1291 }
1292 dhdp = &dev->pub;
1293
1294 memval = bcm_strtoul(buf, NULL, 10);
1295 sscanf(buf, "%lu", &memval);
1296
1297 dhdp->memdump_enabled = (uint32)memval;
1298
1299 DHD_ERROR(("%s: MEMDUMP ENABLED = %u\n", __FUNCTION__, dhdp->memdump_enabled));
1300 return count;
1301 }
1302
1303 static struct dhd_attr dhd_attr_memdump =
1304 __ATTR(memdump, 0660, show_memdump_info, set_memdump_info);
1305 #endif /* DHD_EXPORT_CNTL_FILE */
1306 #endif /* DHD_FW_COREDUMP */
1307
1308 #ifdef BCMASSERT_LOG
1309 /*
1310 * XXX The filename to store assert type is defined for each platform.
1311 * New platforms can add their ifdefs accordingly below.
1312 */
1313 #define ASSERTINFO PLATFORM_PATH".assert.info"
1314
1315 int
get_assert_val_from_file(void)1316 get_assert_val_from_file(void)
1317 {
1318 struct file *fp = NULL;
1319 char *filepath = ASSERTINFO;
1320 char *p_mem_val = NULL;
1321 int mem_val = -1;
1322
1323 /*
1324 * Read assert info from the file
1325 * 0: Trigger Kernel crash by panic()
1326 * 1: Print out the logs and don't trigger Kernel panic. (default)
1327 * 2: Trigger Kernel crash by BUG()
1328 * File doesn't exist: Keep default value (1).
1329 */
1330 fp = filp_open(filepath, O_RDONLY, 0);
1331 if (IS_ERR(fp)) {
1332 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
1333 } else {
1334 int ret = kernel_read_compat(fp, 0, (char *)&mem_val, sizeof(uint32));
1335 if (ret < 0) {
1336 DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
1337 } else {
1338 p_mem_val = (char *)&mem_val;
1339 p_mem_val[sizeof(uint32) - 1] = '\0';
1340 mem_val = bcm_atoi(p_mem_val);
1341 DHD_ERROR(("%s: ASSERT ENABLED = %d\n", __FUNCTION__, mem_val));
1342 }
1343 filp_close(fp, NULL);
1344 }
1345
1346 #ifdef CUSTOMER_HW4_DEBUG
1347 mem_val = (mem_val >= 0) ? mem_val : 1;
1348 #else
1349 mem_val = (mem_val >= 0) ? mem_val : 0;
1350 #endif /* CUSTOMER_HW4_DEBUG */
1351 return mem_val;
1352 }
1353
dhd_get_assert_info(dhd_pub_t * dhd)1354 void dhd_get_assert_info(dhd_pub_t *dhd)
1355 {
1356 #ifndef DHD_EXPORT_CNTL_FILE
1357 int mem_val = -1;
1358
1359 mem_val = get_assert_val_from_file();
1360
1361 g_assert_type = mem_val;
1362 #endif /* !DHD_EXPORT_CNTL_FILE */
1363 }
1364
1365 #ifdef DHD_EXPORT_CNTL_FILE
1366 static ssize_t
show_assert_info(struct dhd_info * dev,char * buf)1367 show_assert_info(struct dhd_info *dev, char *buf)
1368 {
1369 ssize_t ret = 0;
1370
1371 if (!dev) {
1372 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
1373 return ret;
1374 }
1375
1376 ret = scnprintf(buf, PAGE_SIZE -1, "%d\n", g_assert_type);
1377 return ret;
1378
1379 }
1380
1381 static ssize_t
set_assert_info(struct dhd_info * dev,const char * buf,size_t count)1382 set_assert_info(struct dhd_info *dev, const char *buf, size_t count)
1383 {
1384 unsigned long assert_val;
1385
1386 assert_val = bcm_strtoul(buf, NULL, 10);
1387 sscanf(buf, "%lu", &assert_val);
1388
1389 g_assert_type = (uint32)assert_val;
1390
1391 DHD_ERROR(("%s: ASSERT ENABLED = %lu\n", __FUNCTION__, assert_val));
1392 return count;
1393
1394 }
1395
1396 static struct dhd_attr dhd_attr_assert =
1397 __ATTR(assert, 0660, show_assert_info, set_assert_info);
1398 #endif /* DHD_EXPORT_CNTL_FILE */
1399 #endif /* BCMASSERT_LOG */
1400
1401 #ifdef DHD_EXPORT_CNTL_FILE
1402 #if defined(WRITE_WLANINFO)
1403 static ssize_t
show_wifiver_info(struct dhd_info * dev,char * buf)1404 show_wifiver_info(struct dhd_info *dev, char *buf)
1405 {
1406 ssize_t ret = 0;
1407
1408 ret = scnprintf(buf, PAGE_SIZE -1, "%s", version_info);
1409 return ret;
1410 }
1411
1412 static ssize_t
set_wifiver_info(struct dhd_info * dev,const char * buf,size_t count)1413 set_wifiver_info(struct dhd_info *dev, const char *buf, size_t count)
1414 {
1415 DHD_ERROR(("Do not set version info\n"));
1416 return -EINVAL;
1417 }
1418
1419 static struct dhd_attr dhd_attr_wifiver =
1420 __ATTR(wifiver, 0660, show_wifiver_info, set_wifiver_info);
1421 #endif /* WRITE_WLANINFO */
1422
1423 #if defined(USE_CID_CHECK) || defined(USE_DIRECT_VID_TAG)
1424 char cidinfostr[MAX_VNAME_LEN];
1425
1426 static ssize_t
show_cid_info(struct dhd_info * dev,char * buf)1427 show_cid_info(struct dhd_info *dev, char *buf)
1428 {
1429 ssize_t ret = 0;
1430
1431 #ifdef USE_DIRECT_VID_TAG
1432 ret = scnprintf(buf, PAGE_SIZE -1, "%x%x", cidinfostr[VENDOR_OFF], cidinfostr[MD_REV_OFF]);
1433 #endif /* USE_DIRECT_VID_TAG */
1434 #ifdef USE_CID_CHECK
1435 ret = scnprintf(buf, PAGE_SIZE -1, "%s", cidinfostr);
1436 #endif /* USE_CID_CHECK */
1437 return ret;
1438 }
1439
1440 static ssize_t
set_cid_info(struct dhd_info * dev,const char * buf,size_t count)1441 set_cid_info(struct dhd_info *dev, const char *buf, size_t count)
1442 {
1443 #ifdef USE_DIRECT_VID_TAG
1444 uint32 stored_vid = 0, md_rev = 0, vendor = 0;
1445 uint32 vendor_mask = 0x00FF;
1446
1447 stored_vid = bcm_strtoul(buf, NULL, 16);
1448
1449 DHD_ERROR(("%s : stored_vid : 0x%x\n", __FUNCTION__, stored_vid));
1450 md_rev = stored_vid & vendor_mask;
1451 vendor = stored_vid >> 8;
1452
1453 memset(cidinfostr, 0, sizeof(cidinfostr));
1454
1455 cidinfostr[MD_REV_OFF] = (char)md_rev;
1456 cidinfostr[VENDOR_OFF] = (char)vendor;
1457 DHD_INFO(("CID string %x%x\n", cidinfostr[VENDOR_OFF], cidinfostr[MD_REV_OFF]));
1458 #endif /* USE_DIRECT_VID_TAG */
1459 #ifdef USE_CID_CHECK
1460 int len = strlen(buf) + 1;
1461 int maxstrsz;
1462 maxstrsz = MAX_VNAME_LEN;
1463
1464 scnprintf(cidinfostr, ((len > maxstrsz) ? maxstrsz : len), "%s", buf);
1465 DHD_INFO(("%s : CID info string\n", cidinfostr));
1466 #endif /* USE_CID_CHECK */
1467 return count;
1468 }
1469
1470 static struct dhd_attr dhd_attr_cidinfo =
1471 __ATTR(cid, 0660, show_cid_info, set_cid_info);
1472 #endif /* USE_CID_CHECK || USE_DIRECT_VID_TAG */
1473
1474 #if defined(GEN_SOFTAP_INFO_FILE)
1475 char softapinfostr[SOFTAP_INFO_BUF_SZ];
1476 static ssize_t
show_softap_info(struct dhd_info * dev,char * buf)1477 show_softap_info(struct dhd_info *dev, char *buf)
1478 {
1479 ssize_t ret = 0;
1480
1481 ret = scnprintf(buf, PAGE_SIZE -1, "%s", softapinfostr);
1482 return ret;
1483 }
1484
1485 static ssize_t
set_softap_info(struct dhd_info * dev,const char * buf,size_t count)1486 set_softap_info(struct dhd_info *dev, const char *buf, size_t count)
1487 {
1488 DHD_ERROR(("Do not set sofap related info\n"));
1489 return -EINVAL;
1490 }
1491
1492 static struct dhd_attr dhd_attr_softapinfo =
1493 __ATTR(softap, 0660, show_softap_info, set_softap_info);
1494 #endif /* GEN_SOFTAP_INFO_FILE */
1495
1496 #if defined(MIMO_ANT_SETTING)
1497 unsigned long antsel;
1498
1499 static ssize_t
show_ant_info(struct dhd_info * dev,char * buf)1500 show_ant_info(struct dhd_info *dev, char *buf)
1501 {
1502 ssize_t ret = 0;
1503
1504 ret = scnprintf(buf, PAGE_SIZE -1, "%lu\n", antsel);
1505 return ret;
1506 }
1507
1508 static ssize_t
set_ant_info(struct dhd_info * dev,const char * buf,size_t count)1509 set_ant_info(struct dhd_info *dev, const char *buf, size_t count)
1510 {
1511 unsigned long ant_val;
1512
1513 ant_val = bcm_strtoul(buf, NULL, 10);
1514 sscanf(buf, "%lu", &ant_val);
1515
1516 /*
1517 * Check value
1518 * 0 - Not set, handle same as file not exist
1519 */
1520 if (ant_val > 3) {
1521 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
1522 __FUNCTION__, ant_val));
1523 return -EINVAL;
1524 }
1525
1526 antsel = ant_val;
1527 DHD_ERROR(("[WIFI_SEC] %s: Set Antinfo val = %lu \n", __FUNCTION__, antsel));
1528 return count;
1529 }
1530
1531 static struct dhd_attr dhd_attr_antinfo =
1532 __ATTR(ant, 0660, show_ant_info, set_ant_info);
1533 #endif /* MIMO_ANT_SETTING */
1534
1535 #ifdef DHD_PM_CONTROL_FROM_FILE
1536 extern uint32 pmmode_val;
1537 static ssize_t
show_pm_info(struct dhd_info * dev,char * buf)1538 show_pm_info(struct dhd_info *dev, char *buf)
1539 {
1540 ssize_t ret = 0;
1541
1542 if (pmmode_val == 0xFF) {
1543 ret = scnprintf(buf, PAGE_SIZE -1, "PM mode is not set\n");
1544 } else {
1545 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", pmmode_val);
1546 }
1547 return ret;
1548 }
1549
1550 static ssize_t
set_pm_info(struct dhd_info * dev,const char * buf,size_t count)1551 set_pm_info(struct dhd_info *dev, const char *buf, size_t count)
1552 {
1553 unsigned long pm_val;
1554
1555 pm_val = bcm_strtoul(buf, NULL, 10);
1556 sscanf(buf, "%lu", &pm_val);
1557
1558 if (pm_val > 2) {
1559 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
1560 __FUNCTION__, pm_val));
1561 return -EINVAL;
1562 }
1563
1564 pmmode_val = (uint32)pm_val;
1565 DHD_ERROR(("[WIFI_SEC] %s: Set pminfo val = %u\n", __FUNCTION__, pmmode_val));
1566 return count;
1567 }
1568
1569 static struct dhd_attr dhd_attr_pminfo =
1570 __ATTR(pm, 0660, show_pm_info, set_pm_info);
1571 #endif /* DHD_PM_CONTROL_FROM_FILE */
1572
1573 #ifdef LOGTRACE_FROM_FILE
1574 unsigned long logtrace_val = 1;
1575
1576 static ssize_t
show_logtrace_info(struct dhd_info * dev,char * buf)1577 show_logtrace_info(struct dhd_info *dev, char *buf)
1578 {
1579 ssize_t ret = 0;
1580
1581 ret = scnprintf(buf, PAGE_SIZE -1, "%lu\n", logtrace_val);
1582 return ret;
1583 }
1584
1585 static ssize_t
set_logtrace_info(struct dhd_info * dev,const char * buf,size_t count)1586 set_logtrace_info(struct dhd_info *dev, const char *buf, size_t count)
1587 {
1588 unsigned long onoff;
1589
1590 onoff = bcm_strtoul(buf, NULL, 10);
1591 sscanf(buf, "%lu", &onoff);
1592
1593 if (onoff > 2) {
1594 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
1595 __FUNCTION__, onoff));
1596 return -EINVAL;
1597 }
1598
1599 logtrace_val = onoff;
1600 DHD_ERROR(("[WIFI_SEC] %s: LOGTRACE On/Off from sysfs = %lu\n",
1601 __FUNCTION__, logtrace_val));
1602 return count;
1603 }
1604
1605 static struct dhd_attr dhd_attr_logtraceinfo =
1606 __ATTR(logtrace, 0660, show_logtrace_info, set_logtrace_info);
1607 #endif /* LOGTRACE_FROM_FILE */
1608
1609 #ifdef USE_WFA_CERT_CONF
1610 #ifdef BCMSDIO
1611 uint32 bus_txglom = VALUENOTSET;
1612
1613 static ssize_t
show_bustxglom(struct dhd_info * dev,char * buf)1614 show_bustxglom(struct dhd_info *dev, char *buf)
1615 {
1616 ssize_t ret = 0;
1617
1618 if (bus_txglom == VALUENOTSET) {
1619 ret = scnprintf(buf, PAGE_SIZE - 1, "%s\n", "bustxglom not set from sysfs");
1620 } else {
1621 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", bus_txglom);
1622 }
1623 return ret;
1624 }
1625
1626 static ssize_t
set_bustxglom(struct dhd_info * dev,const char * buf,size_t count)1627 set_bustxglom(struct dhd_info *dev, const char *buf, size_t count)
1628 {
1629 uint32 onoff;
1630
1631 onoff = (uint32)bcm_atoi(buf);
1632 sscanf(buf, "%u", &onoff);
1633
1634 if (onoff > 2) {
1635 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1636 __FUNCTION__, onoff));
1637 return -EINVAL;
1638 }
1639
1640 bus_txglom = onoff;
1641 DHD_ERROR(("[WIFI_SEC] %s: BUS TXGLOM On/Off from sysfs = %u\n",
1642 __FUNCTION__, bus_txglom));
1643 return count;
1644 }
1645
1646 static struct dhd_attr dhd_attr_bustxglom =
1647 __ATTR(bustxglom, 0660, show_bustxglom, set_bustxglom);
1648 #endif /* BCMSDIO */
1649
1650 #if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM)
1651 uint32 roam_off = VALUENOTSET;
1652
1653 static ssize_t
show_roamoff(struct dhd_info * dev,char * buf)1654 show_roamoff(struct dhd_info *dev, char *buf)
1655 {
1656 ssize_t ret = 0;
1657
1658 if (roam_off == VALUENOTSET) {
1659 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "roam_off not set from sysfs");
1660 } else {
1661 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", roam_off);
1662 }
1663 return ret;
1664 }
1665
1666 static ssize_t
set_roamoff(struct dhd_info * dev,const char * buf,size_t count)1667 set_roamoff(struct dhd_info *dev, const char *buf, size_t count)
1668 {
1669 uint32 onoff;
1670
1671 onoff = bcm_atoi(buf);
1672 sscanf(buf, "%u", &onoff);
1673
1674 if (onoff > 2) {
1675 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1676 __FUNCTION__, onoff));
1677 return -EINVAL;
1678 }
1679
1680 roam_off = onoff;
1681 DHD_ERROR(("[WIFI_SEC] %s: ROAM On/Off from sysfs = %u\n",
1682 __FUNCTION__, roam_off));
1683 return count;
1684 }
1685
1686 static struct dhd_attr dhd_attr_roamoff =
1687 __ATTR(roamoff, 0660, show_roamoff, set_roamoff);
1688 #endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */
1689
1690 #ifdef USE_WL_FRAMEBURST
1691 uint32 frameburst = VALUENOTSET;
1692
1693 static ssize_t
show_frameburst(struct dhd_info * dev,char * buf)1694 show_frameburst(struct dhd_info *dev, char *buf)
1695 {
1696 ssize_t ret = 0;
1697
1698 if (frameburst == VALUENOTSET) {
1699 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "frameburst not set from sysfs");
1700 } else {
1701 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", frameburst);
1702 }
1703 return ret;
1704 }
1705
1706 static ssize_t
set_frameburst(struct dhd_info * dev,const char * buf,size_t count)1707 set_frameburst(struct dhd_info *dev, const char *buf, size_t count)
1708 {
1709 uint32 onoff;
1710
1711 onoff = bcm_atoi(buf);
1712 sscanf(buf, "%u", &onoff);
1713
1714 if (onoff > 2) {
1715 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1716 __FUNCTION__, onoff));
1717 return -EINVAL;
1718 }
1719
1720 frameburst = onoff;
1721 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1722 __FUNCTION__, frameburst));
1723 return count;
1724 }
1725
1726 static struct dhd_attr dhd_attr_frameburst =
1727 __ATTR(frameburst, 0660, show_frameburst, set_frameburst);
1728 #endif /* USE_WL_FRAMEBURST */
1729
1730 #ifdef USE_WL_TXBF
1731 uint32 txbf = VALUENOTSET;
1732
1733 static ssize_t
show_txbf(struct dhd_info * dev,char * buf)1734 show_txbf(struct dhd_info *dev, char *buf)
1735 {
1736 ssize_t ret = 0;
1737
1738 if (txbf == VALUENOTSET) {
1739 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "txbf not set from sysfs");
1740 } else {
1741 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", txbf);
1742 }
1743 return ret;
1744 }
1745
1746 static ssize_t
set_txbf(struct dhd_info * dev,const char * buf,size_t count)1747 set_txbf(struct dhd_info *dev, const char *buf, size_t count)
1748 {
1749 uint32 onoff;
1750
1751 onoff = bcm_atoi(buf);
1752 sscanf(buf, "%u", &onoff);
1753
1754 if (onoff > 2) {
1755 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1756 __FUNCTION__, onoff));
1757 return -EINVAL;
1758 }
1759
1760 txbf = onoff;
1761 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1762 __FUNCTION__, txbf));
1763 return count;
1764 }
1765
1766 static struct dhd_attr dhd_attr_txbf =
1767 __ATTR(txbf, 0660, show_txbf, set_txbf);
1768 #endif /* USE_WL_TXBF */
1769
1770 #ifdef PROP_TXSTATUS
1771 uint32 proptx = VALUENOTSET;
1772
1773 static ssize_t
show_proptx(struct dhd_info * dev,char * buf)1774 show_proptx(struct dhd_info *dev, char *buf)
1775 {
1776 ssize_t ret = 0;
1777
1778 if (proptx == VALUENOTSET) {
1779 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "proptx not set from sysfs");
1780 } else {
1781 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", proptx);
1782 }
1783 return ret;
1784 }
1785
1786 static ssize_t
set_proptx(struct dhd_info * dev,const char * buf,size_t count)1787 set_proptx(struct dhd_info *dev, const char *buf, size_t count)
1788 {
1789 uint32 onoff;
1790
1791 onoff = bcm_strtoul(buf, NULL, 10);
1792 sscanf(buf, "%u", &onoff);
1793
1794 if (onoff > 2) {
1795 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1796 __FUNCTION__, onoff));
1797 return -EINVAL;
1798 }
1799
1800 proptx = onoff;
1801 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1802 __FUNCTION__, txbf));
1803 return count;
1804 }
1805
1806 static struct dhd_attr dhd_attr_proptx =
1807 __ATTR(proptx, 0660, show_proptx, set_proptx);
1808
1809 #endif /* PROP_TXSTATUS */
1810 #endif /* USE_WFA_CERT_CONF */
1811 #endif /* DHD_EXPORT_CNTL_FILE */
1812
1813 #if defined(DHD_ADPS_BAM_EXPORT) && defined(WL_BAM)
1814 #define BAD_AP_MAC_ADDR_ELEMENT_NUM 6
1815 wl_bad_ap_mngr_t *g_bad_ap_mngr = NULL;
1816
1817 static ssize_t
show_adps_bam_list(struct dhd_info * dev,char * buf)1818 show_adps_bam_list(struct dhd_info *dev, char *buf)
1819 {
1820 int offset = 0;
1821 ssize_t ret = 0;
1822
1823 wl_bad_ap_info_t *bad_ap;
1824 wl_bad_ap_info_entry_t *entry;
1825
1826 if (g_bad_ap_mngr == NULL)
1827 return ret;
1828
1829 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1830 list_for_each_entry(entry, &g_bad_ap_mngr->list, list) {
1831 bad_ap = &entry->bad_ap;
1832
1833 ret = scnprintf(buf + offset, PAGE_SIZE - 1, MACF"\n",
1834 bad_ap->bssid.octet[0], bad_ap->bssid.octet[1],
1835 bad_ap->bssid.octet[2], bad_ap->bssid.octet[3],
1836 bad_ap->bssid.octet[4], bad_ap->bssid.octet[5]);
1837
1838 offset += ret;
1839 }
1840 GCC_DIAGNOSTIC_POP();
1841
1842 return offset;
1843 }
1844
1845 static ssize_t
store_adps_bam_list(struct dhd_info * dev,const char * buf,size_t count)1846 store_adps_bam_list(struct dhd_info *dev, const char *buf, size_t count)
1847 {
1848 int ret;
1849 size_t len;
1850 int offset;
1851 char tmp[128];
1852 wl_bad_ap_info_t bad_ap;
1853
1854 if (g_bad_ap_mngr == NULL)
1855 return count;
1856
1857 len = count;
1858 offset = 0;
1859 do {
1860 ret = sscanf(buf + offset, MACF"\n",
1861 (uint32 *)&bad_ap.bssid.octet[0], (uint32 *)&bad_ap.bssid.octet[1],
1862 (uint32 *)&bad_ap.bssid.octet[2], (uint32 *)&bad_ap.bssid.octet[3],
1863 (uint32 *)&bad_ap.bssid.octet[4], (uint32 *)&bad_ap.bssid.octet[5]);
1864 if (ret != BAD_AP_MAC_ADDR_ELEMENT_NUM) {
1865 DHD_ERROR(("%s - fail to parse bad ap data\n", __FUNCTION__));
1866 return -EINVAL;
1867 }
1868
1869 ret = wl_bad_ap_mngr_add(g_bad_ap_mngr, &bad_ap);
1870 if (ret < 0)
1871 return ret;
1872
1873 ret = snprintf(tmp, ARRAYSIZE(tmp), MACF"\n",
1874 bad_ap.bssid.octet[0], bad_ap.bssid.octet[1],
1875 bad_ap.bssid.octet[2], bad_ap.bssid.octet[3],
1876 bad_ap.bssid.octet[4], bad_ap.bssid.octet[5]);
1877 if (ret < 0) {
1878 DHD_ERROR(("%s - fail to get bad ap data length(%d)\n", __FUNCTION__, ret));
1879 return ret;
1880 }
1881
1882 len -= ret;
1883 offset += ret;
1884 } while (len > 0);
1885
1886 return count;
1887 }
1888
1889 static struct dhd_attr dhd_attr_adps_bam =
1890 __ATTR(bad_ap_list, 0660, show_adps_bam_list, store_adps_bam_list);
1891 #endif /* DHD_ADPS_BAM_EXPORT && WL_BAM */
1892
1893 #ifdef DHD_SEND_HANG_PRIVCMD_ERRORS
1894 uint32 report_hang_privcmd_err = 1;
1895
1896 static ssize_t
show_hang_privcmd_err(struct dhd_info * dev,char * buf)1897 show_hang_privcmd_err(struct dhd_info *dev, char *buf)
1898 {
1899 ssize_t ret = 0;
1900
1901 ret = scnprintf(buf, PAGE_SIZE - 1, "%u\n", report_hang_privcmd_err);
1902 return ret;
1903 }
1904
1905 static ssize_t
set_hang_privcmd_err(struct dhd_info * dev,const char * buf,size_t count)1906 set_hang_privcmd_err(struct dhd_info *dev, const char *buf, size_t count)
1907 {
1908 uint32 val;
1909
1910 val = bcm_atoi(buf);
1911 sscanf(buf, "%u", &val);
1912
1913 report_hang_privcmd_err = val ? 1 : 0;
1914 DHD_INFO(("%s: Set report HANG for private cmd error: %d\n",
1915 __FUNCTION__, report_hang_privcmd_err));
1916 return count;
1917 }
1918
1919 static struct dhd_attr dhd_attr_hang_privcmd_err =
1920 __ATTR(hang_privcmd_err, 0660, show_hang_privcmd_err, set_hang_privcmd_err);
1921 #endif /* DHD_SEND_HANG_PRIVCMD_ERRORS */
1922
1923 #if defined(SHOW_LOGTRACE)
1924 static ssize_t
show_control_logtrace(struct dhd_info * dev,char * buf)1925 show_control_logtrace(struct dhd_info *dev, char *buf)
1926 {
1927 ssize_t ret = 0;
1928
1929 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", control_logtrace);
1930 return ret;
1931 }
1932
1933 static ssize_t
set_control_logtrace(struct dhd_info * dev,const char * buf,size_t count)1934 set_control_logtrace(struct dhd_info *dev, const char *buf, size_t count)
1935 {
1936 uint32 val;
1937
1938 val = bcm_atoi(buf);
1939
1940 control_logtrace = val;
1941 DHD_ERROR(("%s: Set control logtrace: %d\n", __FUNCTION__, control_logtrace));
1942 return count;
1943 }
1944
1945 static struct dhd_attr dhd_attr_control_logtrace =
1946 __ATTR(control_logtrace, 0660, show_control_logtrace, set_control_logtrace);
1947 #endif /* SHOW_LOGTRACE */
1948
1949 #if defined(DISABLE_HE_ENAB) || defined(CUSTOM_CONTROL_HE_ENAB)
1950 uint8 control_he_enab = 1;
1951 #endif /* DISABLE_HE_ENAB || CUSTOM_CONTROL_HE_ENAB */
1952
1953 #if defined(CUSTOM_CONTROL_HE_ENAB)
1954 static ssize_t
show_control_he_enab(struct dhd_info * dev,char * buf)1955 show_control_he_enab(struct dhd_info *dev, char *buf)
1956 {
1957 ssize_t ret = 0;
1958
1959 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", control_he_enab);
1960 return ret;
1961 }
1962
1963 static ssize_t
set_control_he_enab(struct dhd_info * dev,const char * buf,size_t count)1964 set_control_he_enab(struct dhd_info *dev, const char *buf, size_t count)
1965 {
1966 uint32 val;
1967
1968 val = bcm_atoi(buf);
1969
1970 control_he_enab = val ? 1 : 0;
1971 DHD_ERROR(("%s: Set control he enab: %d\n", __FUNCTION__, control_he_enab));
1972 return count;
1973 }
1974
1975 static struct dhd_attr dhd_attr_control_he_enab=
1976 __ATTR(control_he_enab, 0660, show_control_he_enab, set_control_he_enab);
1977 #endif /* CUSTOM_CONTROL_HE_ENAB */
1978
1979 #if defined(WLAN_ACCEL_BOOT)
1980 static ssize_t
show_wl_accel_force_reg_on(struct dhd_info * dhd,char * buf)1981 show_wl_accel_force_reg_on(struct dhd_info *dhd, char *buf)
1982 {
1983 ssize_t ret = 0;
1984 if (!dhd) {
1985 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
1986 return ret;
1987 }
1988
1989 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", dhd->wl_accel_force_reg_on);
1990 return ret;
1991 }
1992
1993 static ssize_t
set_wl_accel_force_reg_on(struct dhd_info * dhd,const char * buf,size_t count)1994 set_wl_accel_force_reg_on(struct dhd_info *dhd, const char *buf, size_t count)
1995 {
1996 uint32 val;
1997
1998 if (!dhd) {
1999 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2000 return count;
2001 }
2002
2003 val = bcm_atoi(buf);
2004
2005 dhd->wl_accel_force_reg_on = val ? 1 : 0;
2006 DHD_ERROR(("%s: wl_accel_force_reg_on: %d\n", __FUNCTION__, dhd->wl_accel_force_reg_on));
2007 return count;
2008 }
2009
2010 static struct dhd_attr dhd_attr_wl_accel_force_reg_on=
2011 __ATTR(wl_accel_force_reg_on, 0660, show_wl_accel_force_reg_on, set_wl_accel_force_reg_on);
2012 #endif /* WLAN_ACCEL_BOOT */
2013
2014 #if defined(AGG_H2D_DB)
2015 extern bool agg_h2d_db_enab;
2016 extern uint32 agg_h2d_db_timeout;
2017 extern uint32 agg_h2d_db_inflight_thresh;
2018
2019 static ssize_t
show_agg_h2d_db_enab(struct dhd_info * dhd,char * buf)2020 show_agg_h2d_db_enab(struct dhd_info *dhd, char *buf)
2021 {
2022 ssize_t ret = 0;
2023 if (!dhd) {
2024 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2025 return ret;
2026 }
2027
2028 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", agg_h2d_db_enab);
2029 return ret;
2030 }
2031
2032 static ssize_t
set_agg_h2d_db_enab(struct dhd_info * dhd,const char * buf,size_t count)2033 set_agg_h2d_db_enab(struct dhd_info *dhd, const char *buf, size_t count)
2034 {
2035 uint32 val;
2036
2037 if (!dhd) {
2038 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2039 return count;
2040 }
2041
2042 val = bcm_atoi(buf);
2043
2044 agg_h2d_db_enab = val ? TRUE : FALSE;
2045 DHD_ERROR(("%s: agg_h2d_db_timeout: %d\n", __FUNCTION__, agg_h2d_db_enab));
2046 return count;
2047 }
2048
2049 static struct dhd_attr dhd_attr_agg_h2d_db_enab =
2050 __ATTR(agg_h2d_db_enab, 0660, show_agg_h2d_db_enab, set_agg_h2d_db_enab);
2051
2052 static ssize_t
show_agg_h2d_db_inflight_thresh(struct dhd_info * dhd,char * buf)2053 show_agg_h2d_db_inflight_thresh(struct dhd_info *dhd, char *buf)
2054 {
2055 ssize_t ret = 0;
2056 if (!dhd) {
2057 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2058 return ret;
2059 }
2060
2061 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", agg_h2d_db_inflight_thresh);
2062 return ret;
2063 }
2064
2065 static ssize_t
set_agg_h2d_db_inflight_thresh(struct dhd_info * dhd,const char * buf,size_t count)2066 set_agg_h2d_db_inflight_thresh(struct dhd_info *dhd, const char *buf, size_t count)
2067 {
2068 uint32 val;
2069
2070 if (!dhd) {
2071 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2072 return count;
2073 }
2074
2075 val = bcm_atoi(buf);
2076
2077 agg_h2d_db_inflight_thresh = val;
2078 DHD_ERROR(("%s: agg_h2d_db_timeout: %d\n", __FUNCTION__, agg_h2d_db_inflight_thresh));
2079 return count;
2080 }
2081
2082 static struct dhd_attr dhd_attr_agg_h2d_db_inflight_thresh =
2083 __ATTR(agg_h2d_db_inflight_thresh, 0660, show_agg_h2d_db_inflight_thresh,
2084 set_agg_h2d_db_inflight_thresh);
2085
2086 static ssize_t
show_agg_h2d_db_timeout(struct dhd_info * dhd,char * buf)2087 show_agg_h2d_db_timeout(struct dhd_info *dhd, char *buf)
2088 {
2089 ssize_t ret = 0;
2090 if (!dhd) {
2091 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2092 return ret;
2093 }
2094
2095 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", agg_h2d_db_timeout);
2096 return ret;
2097 }
2098
2099 static ssize_t
set_agg_h2d_db_timeout(struct dhd_info * dhd,const char * buf,size_t count)2100 set_agg_h2d_db_timeout(struct dhd_info *dhd, const char *buf, size_t count)
2101 {
2102 uint32 val;
2103
2104 if (!dhd) {
2105 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2106 return count;
2107 }
2108
2109 val = bcm_atoi(buf);
2110
2111 agg_h2d_db_timeout = val;
2112 DHD_ERROR(("%s: agg_h2d_db_timeout: %d\n", __FUNCTION__, agg_h2d_db_timeout));
2113 return count;
2114 }
2115
2116 static struct dhd_attr dhd_attr_agg_h2d_db_timeout =
2117 __ATTR(agg_h2d_db_timeout, 0660, show_agg_h2d_db_timeout, set_agg_h2d_db_timeout);
2118 #endif /* WLAN_ACCEL_BOOT */
2119 /*
2120 * Dumps the lock and other state information useful for debug
2121 *
2122 */
2123 static ssize_t
dhd_debug_dump_stateinfo(struct dhd_info * dhd,char * buf)2124 dhd_debug_dump_stateinfo(struct dhd_info *dhd, char *buf)
2125 {
2126 u32 buf_size = PAGE_SIZE - 1;
2127 u8 *ptr = buf;
2128 ssize_t len = 0;
2129
2130 len += scnprintf(ptr, buf_size, "[DHD]\nlock info:\n");
2131 #ifdef BT_OVER_SDIO
2132 len += scnprintf((ptr+len), (buf_size-len), "bus_user_lock:\n",
2133 mutex_is_locked(&dhd->bus_user_lock));
2134 #endif /* BT_OVER_SDIO */
2135
2136 #ifdef WL_CFG80211
2137 len += wl_cfg80211_debug_data_dump(dhd_linux_get_primary_netdev(&dhd->pub),
2138 (ptr + len), (buf_size - len));
2139 #endif /* WL_CFG80211 */
2140
2141 /* Ensure buffer ends with null char */
2142 buf[len] = '\0';
2143 return len + 1;
2144 }
2145 static struct dhd_attr dhd_attr_dhd_debug_data =
2146 __ATTR(dump_stateinfo, 0660, dhd_debug_dump_stateinfo, NULL);
2147
2148 #ifdef WL_CFG80211
2149 #define _S(x) #x
2150 #define S(x) _S(x)
2151 #define SUBLOGLEVEL 20
2152 #define SUBLOGLEVELZ ((SUBLOGLEVEL) + (1))
2153 static const struct {
2154 u32 log_level;
2155 char *sublogname;
2156 } sublogname_map[] = {
2157 {WL_DBG_ERR, "ERR"},
2158 {WL_DBG_INFO, "INFO"},
2159 {WL_DBG_DBG, "DBG"},
2160 {WL_DBG_SCAN, "SCAN"},
2161 {WL_DBG_TRACE, "TRACE"},
2162 {WL_DBG_P2P_ACTION, "P2PACTION"}
2163 };
2164
2165 /**
2166 * Format : echo "SCAN:1 DBG:1" > /sys/wifi/wl_dbg_level
2167 * to turn on SCAN and DBG log.
2168 * To turn off SCAN partially, echo "SCAN:0" > /sys/wifi/wl_dbg_level
2169 * To see current setting of debug level,
2170 * cat /sys/wifi/wl_dbg_level
2171 */
2172 static ssize_t
show_wl_debug_level(struct dhd_info * dhd,char * buf)2173 show_wl_debug_level(struct dhd_info *dhd, char *buf)
2174 {
2175 char *param;
2176 char tbuf[SUBLOGLEVELZ * ARRAYSIZE(sublogname_map)];
2177 uint i;
2178 ssize_t ret = 0;
2179
2180 bzero(tbuf, sizeof(tbuf));
2181 param = &tbuf[0];
2182 for (i = 0; i < ARRAYSIZE(sublogname_map); i++) {
2183 param += snprintf(param, sizeof(tbuf) - 1, "%s:%d ",
2184 sublogname_map[i].sublogname,
2185 (wl_dbg_level & sublogname_map[i].log_level) ? 1 : 0);
2186 }
2187 ret = scnprintf(buf, PAGE_SIZE - 1, "%s \n", tbuf);
2188 return ret;
2189 }
2190
2191 static ssize_t
set_wl_debug_level(struct dhd_info * dhd,const char * buf,size_t count)2192 set_wl_debug_level(struct dhd_info *dhd, const char *buf, size_t count)
2193 {
2194 char tbuf[SUBLOGLEVELZ * ARRAYSIZE(sublogname_map)], sublog[SUBLOGLEVELZ];
2195 char *params, *token, *colon;
2196 uint i, tokens, log_on = 0;
2197 size_t minsize = min_t(size_t, (sizeof(tbuf) - 1), count);
2198
2199 bzero(tbuf, sizeof(tbuf));
2200 bzero(sublog, sizeof(sublog));
2201 strlcpy(tbuf, buf, minsize);
2202
2203 DHD_INFO(("current wl_dbg_level %d \n", wl_dbg_level));
2204
2205 tbuf[minsize] = '\0';
2206 params = &tbuf[0];
2207 colon = strchr(params, '\n');
2208 if (colon != NULL)
2209 *colon = '\0';
2210 while ((token = strsep(¶ms, " ")) != NULL) {
2211 bzero(sublog, sizeof(sublog));
2212 if (token == NULL || !*token)
2213 break;
2214 if (*token == '\0')
2215 continue;
2216 colon = strchr(token, ':');
2217 if (colon != NULL) {
2218 *colon = ' ';
2219 }
2220 tokens = sscanf(token, "%"S(SUBLOGLEVEL)"s %u", sublog, &log_on);
2221 if (colon != NULL)
2222 *colon = ':';
2223
2224 if (tokens == 2) {
2225 for (i = 0; i < ARRAYSIZE(sublogname_map); i++) {
2226 if (!strncmp(sublog, sublogname_map[i].sublogname,
2227 strlen(sublogname_map[i].sublogname))) {
2228 if (log_on)
2229 wl_dbg_level |=
2230 (sublogname_map[i].log_level);
2231 else
2232 wl_dbg_level &=
2233 ~(sublogname_map[i].log_level);
2234 }
2235 }
2236 } else
2237 WL_ERR(("%s: can't parse '%s' as a "
2238 "SUBMODULE:LEVEL (%d tokens)\n",
2239 tbuf, token, tokens));
2240
2241 }
2242 DHD_INFO(("changed wl_dbg_level %d \n", wl_dbg_level));
2243 return count;
2244 }
2245
2246 static struct dhd_attr dhd_attr_wl_dbg_level =
2247 __ATTR(wl_dbg_level, 0660, show_wl_debug_level, set_wl_debug_level);
2248 #endif /* WL_CFG80211 */
2249
2250 /* Attribute object that gets registered with "wifi" kobject tree */
2251 static struct attribute *default_file_attrs[] = {
2252 #ifdef DHD_MAC_ADDR_EXPORT
2253 &dhd_attr_macaddr.attr,
2254 #endif /* DHD_MAC_ADDR_EXPORT */
2255 #ifdef DHD_EXPORT_CNTL_FILE
2256 #ifdef DHD_FW_COREDUMP
2257 &dhd_attr_memdump.attr,
2258 #endif /* DHD_FW_COREDUMP */
2259 #ifdef BCMASSERT_LOG
2260 &dhd_attr_assert.attr,
2261 #endif /* BCMASSERT_LOG */
2262 #ifdef WRITE_WLANINFO
2263 &dhd_attr_wifiver.attr,
2264 #endif /* WRITE_WLANINFO */
2265 #if defined(USE_CID_CHECK) || defined(USE_DIRECT_VID_TAG)
2266 &dhd_attr_cidinfo.attr,
2267 #endif /* USE_CID_CHECK || USE_DIRECT_VID_TAG */
2268 #ifdef GEN_SOFTAP_INFO_FILE
2269 &dhd_attr_softapinfo.attr,
2270 #endif /* GEN_SOFTAP_INFO_FILE */
2271 #ifdef MIMO_ANT_SETTING
2272 &dhd_attr_antinfo.attr,
2273 #endif /* MIMO_ANT_SETTING */
2274 #ifdef DHD_PM_CONTROL_FROM_FILE
2275 &dhd_attr_pminfo.attr,
2276 #endif /* DHD_PM_CONTROL_FROM_FILE */
2277 #ifdef LOGTRACE_FROM_FILE
2278 &dhd_attr_logtraceinfo.attr,
2279 #endif /* LOGTRACE_FROM_FILE */
2280 #ifdef USE_WFA_CERT_CONF
2281 #ifdef BCMSDIO
2282 &dhd_attr_bustxglom.attr,
2283 #endif /* BCMSDIO */
2284 &dhd_attr_roamoff.attr,
2285 #ifdef USE_WL_FRAMEBURST
2286 &dhd_attr_frameburst.attr,
2287 #endif /* USE_WL_FRAMEBURST */
2288 #ifdef USE_WL_TXBF
2289 &dhd_attr_txbf.attr,
2290 #endif /* USE_WL_TXBF */
2291 #ifdef PROP_TXSTATUS
2292 &dhd_attr_proptx.attr,
2293 #endif /* PROP_TXSTATUS */
2294 #endif /* USE_WFA_CERT_CONF */
2295 #endif /* DHD_EXPORT_CNTL_FILE */
2296 #if defined(DHD_ADPS_BAM_EXPORT) && defined(WL_BAM)
2297 &dhd_attr_adps_bam.attr,
2298 #endif /* DHD_ADPS_BAM_EXPORT && WL_BAM */
2299 #ifdef DHD_SEND_HANG_PRIVCMD_ERRORS
2300 &dhd_attr_hang_privcmd_err.attr,
2301 #endif /* DHD_SEND_HANG_PRIVCMD_ERRORS */
2302 #if defined(SHOW_LOGTRACE)
2303 &dhd_attr_control_logtrace.attr,
2304 #endif /* SHOW_LOGTRACE */
2305 #if defined(DHD_TRACE_WAKE_LOCK)
2306 &dhd_attr_wklock.attr,
2307 #endif
2308 #ifdef DHD_LOG_DUMP
2309 &dhd_attr_logdump_periodic_flush.attr,
2310 &dhd_attr_logdump_ecntr.attr,
2311 #endif
2312 &dhd_attr_ecounters.attr,
2313 #ifdef DHD_QOS_ON_SOCK_FLOW
2314 &dhd_attr_sock_qos_onoff.attr,
2315 &dhd_attr_sock_qos_stats.attr,
2316 &dhd_attr_sock_qos_upgrade.attr,
2317 &dhd_attr_sock_qos_numfl_upgrd_thresh.attr,
2318 &dhd_attr_sock_qos_avgpktsize_thresh.attr,
2319 &dhd_attr_sock_qos_numpkts_thresh.attr,
2320 &dhd_attr_sock_qos_detectcnt_thresh.attr,
2321 &dhd_attr_sock_qos_detectcnt_upgrd_thresh.attr,
2322 &dhd_attr_sock_qos_maxfl.attr,
2323 #ifdef DHD_QOS_ON_SOCK_FLOW_UT
2324 &dhd_attr_sock_qos_unit_test.attr,
2325 #endif /* DHD_QOS_ON_SOCK_FLOW_UT */
2326 #endif /* DHD_QOS_ON_SOCK_FLOW */
2327 #ifdef DHD_SSSR_DUMP
2328 &dhd_attr_sssr_enab.attr,
2329 &dhd_attr_fis_enab.attr,
2330 #endif /* DHD_SSSR_DUMP */
2331 &dhd_attr_firmware_path.attr,
2332 &dhd_attr_nvram_path.attr,
2333 #if defined(CUSTOM_CONTROL_HE_ENAB)
2334 &dhd_attr_control_he_enab.attr,
2335 #endif /* CUSTOM_CONTROL_HE_ENAB */
2336 #if defined(WLAN_ACCEL_BOOT)
2337 &dhd_attr_wl_accel_force_reg_on.attr,
2338 #endif /* WLAN_ACCEL_BOOT */
2339 #ifdef PWRSTATS_SYSFS
2340 &dhd_attr_pwrstats_path.attr,
2341 #endif /* PWRSTATS_SYSFS */
2342 #if defined(WL_CFG80211)
2343 &dhd_attr_wl_dbg_level.attr,
2344 #endif /* WL_CFG80211 */
2345 &dhd_attr_dhd_debug_data.attr,
2346 #if defined(AGG_H2D_DB)
2347 &dhd_attr_agg_h2d_db_enab.attr,
2348 &dhd_attr_agg_h2d_db_inflight_thresh.attr,
2349 &dhd_attr_agg_h2d_db_timeout.attr,
2350 #endif /* AGG_H2D_DB */
2351 NULL
2352 };
2353
2354 /*
2355 * wifi kobject show function, the "attr" attribute specifices to which
2356 * node under "sys/wifi" the show function is called.
2357 */
dhd_show(struct kobject * kobj,struct attribute * attr,char * buf)2358 static ssize_t dhd_show(struct kobject *kobj, struct attribute *attr, char *buf)
2359 {
2360 dhd_info_t *dhd;
2361 struct dhd_attr *d_attr;
2362 int ret;
2363
2364 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2365 dhd = to_dhd(kobj);
2366 d_attr = to_attr(attr);
2367 GCC_DIAGNOSTIC_POP();
2368
2369 if (d_attr->show)
2370 ret = d_attr->show(dhd, buf);
2371 else
2372 ret = -EIO;
2373
2374 return ret;
2375 }
2376
2377 /*
2378 * wifi kobject show function, the "attr" attribute specifices to which
2379 * node under "sys/wifi" the store function is called.
2380 */
dhd_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)2381 static ssize_t dhd_store(struct kobject *kobj, struct attribute *attr,
2382 const char *buf, size_t count)
2383 {
2384 dhd_info_t *dhd;
2385 struct dhd_attr *d_attr;
2386 int ret;
2387
2388 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2389 dhd = to_dhd(kobj);
2390 d_attr = to_attr(attr);
2391 GCC_DIAGNOSTIC_POP();
2392
2393 if (d_attr->store)
2394 ret = d_attr->store(dhd, buf, count);
2395 else
2396 ret = -EIO;
2397
2398 return ret;
2399
2400 }
2401
2402 static struct sysfs_ops dhd_sysfs_ops = {
2403 .show = dhd_show,
2404 .store = dhd_store,
2405 };
2406
2407 static struct kobj_type dhd_ktype = {
2408 .sysfs_ops = &dhd_sysfs_ops,
2409 .default_attrs = default_file_attrs,
2410 };
2411
2412 #ifdef CSI_SUPPORT
2413 /* Function to show current ccode */
read_csi_data(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)2414 static ssize_t read_csi_data(struct file *filp, struct kobject *kobj,
2415 struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
2416 {
2417 dhd_info_t *dhd = to_dhd(kobj);
2418 int n = 0;
2419
2420 n = dhd_csi_dump_list(&dhd->pub, buf);
2421 DHD_INFO(("Dump data to file, size %d\n", n));
2422 dhd_csi_clean_list(&dhd->pub);
2423
2424 return n;
2425 }
2426
2427 static struct bin_attribute dhd_attr_csi = {
2428 .attr = { .name = "csi",
2429 .mode = 0660, },
2430 .size = MAX_CSI_FILESZ,
2431 .read = read_csi_data,
2432 };
2433 #endif /* CSI_SUPPORT */
2434
2435 /*
2436 * sysfs for dhd_lb
2437 */
2438 #ifdef DHD_LB
2439 #if defined(DHD_LB_TXP)
2440 static ssize_t
show_lbtxp(struct dhd_info * dev,char * buf)2441 show_lbtxp(struct dhd_info *dev, char *buf)
2442 {
2443 ssize_t ret = 0;
2444 unsigned long onoff;
2445 dhd_info_t *dhd = (dhd_info_t *)dev;
2446
2447 onoff = atomic_read(&dhd->lb_txp_active);
2448 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
2449 onoff);
2450 return ret;
2451 }
2452
2453 static ssize_t
lbtxp_onoff(struct dhd_info * dev,const char * buf,size_t count)2454 lbtxp_onoff(struct dhd_info *dev, const char *buf, size_t count)
2455 {
2456 unsigned long onoff;
2457 dhd_info_t *dhd = (dhd_info_t *)dev;
2458 int i;
2459
2460 onoff = bcm_strtoul(buf, NULL, 10);
2461
2462 sscanf(buf, "%lu", &onoff);
2463 if (onoff != 0 && onoff != 1) {
2464 return -EINVAL;
2465 }
2466 atomic_set(&dhd->lb_txp_active, onoff);
2467
2468 /* Since the scheme is changed clear the counters */
2469 for (i = 0; i < NR_CPUS; i++) {
2470 DHD_LB_STATS_CLR(dhd->txp_percpu_run_cnt[i]);
2471 DHD_LB_STATS_CLR(dhd->tx_start_percpu_run_cnt[i]);
2472 }
2473
2474 return count;
2475 }
2476
2477 static struct dhd_attr dhd_attr_lbtxp =
2478 __ATTR(lbtxp, 0660, show_lbtxp, lbtxp_onoff);
2479 #endif /* DHD_LB_TXP */
2480
2481 #if defined(DHD_LB_RXP)
2482 static ssize_t
show_lbrxp(struct dhd_info * dev,char * buf)2483 show_lbrxp(struct dhd_info *dev, char *buf)
2484 {
2485 ssize_t ret = 0;
2486 unsigned long onoff;
2487 dhd_info_t *dhd = (dhd_info_t *)dev;
2488
2489 onoff = atomic_read(&dhd->lb_rxp_active);
2490 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
2491 onoff);
2492 return ret;
2493 }
2494
2495 static ssize_t
lbrxp_onoff(struct dhd_info * dev,const char * buf,size_t count)2496 lbrxp_onoff(struct dhd_info *dev, const char *buf, size_t count)
2497 {
2498 unsigned long onoff;
2499 dhd_info_t *dhd = (dhd_info_t *)dev;
2500
2501 onoff = bcm_strtoul(buf, NULL, 10);
2502
2503 sscanf(buf, "%lu", &onoff);
2504 if (onoff != 0 && onoff != 1) {
2505 return -EINVAL;
2506 }
2507 atomic_set(&dhd->lb_rxp_active, onoff);
2508
2509 return count;
2510 }
2511 static struct dhd_attr dhd_attr_lbrxp =
2512 __ATTR(lbrxp, 0660, show_lbrxp, lbrxp_onoff);
2513
2514 static ssize_t
get_lb_rxp_stop_thr(struct dhd_info * dev,char * buf)2515 get_lb_rxp_stop_thr(struct dhd_info *dev, char *buf)
2516 {
2517 dhd_info_t *dhd = (dhd_info_t *)dev;
2518 dhd_pub_t *dhdp;
2519 ssize_t ret = 0;
2520
2521 if (!dhd) {
2522 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2523 return -EINVAL;
2524 }
2525 dhdp = &dhd->pub;
2526
2527 ret = scnprintf(buf, PAGE_SIZE - 1, "%u \n",
2528 (dhdp->lb_rxp_stop_thr / D2HRING_RXCMPLT_MAX_ITEM));
2529 return ret;
2530 }
2531
2532 #define ONE_GB (1024 * 1024 * 1024)
2533
2534 static ssize_t
set_lb_rxp_stop_thr(struct dhd_info * dev,const char * buf,size_t count)2535 set_lb_rxp_stop_thr(struct dhd_info *dev, const char *buf, size_t count)
2536 {
2537 dhd_info_t *dhd = (dhd_info_t *)dev;
2538 dhd_pub_t *dhdp;
2539 uint32 lb_rxp_stop_thr;
2540
2541 if (!dhd) {
2542 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2543 return -EINVAL;
2544 }
2545 dhdp = &dhd->pub;
2546
2547 lb_rxp_stop_thr = bcm_strtoul(buf, NULL, 10);
2548 sscanf(buf, "%u", &lb_rxp_stop_thr);
2549
2550 /* disable lb_rxp flow ctrl */
2551 if (lb_rxp_stop_thr == 0) {
2552 dhdp->lb_rxp_stop_thr = 0;
2553 dhdp->lb_rxp_strt_thr = 0;
2554 atomic_set(&dhd->pub.lb_rxp_flow_ctrl, FALSE);
2555 return count;
2556 }
2557 /* 1. by the time lb_rxp_stop_thr gets into picture,
2558 * DHD RX path should not consume more than 1GB
2559 * 2. lb_rxp_stop_thr should always be more than dhdp->lb_rxp_strt_thr
2560 */
2561 if (((lb_rxp_stop_thr *
2562 D2HRING_RXCMPLT_MAX_ITEM *
2563 dhd_prot_get_rxbufpost_sz(dhdp)) > ONE_GB) ||
2564 (lb_rxp_stop_thr <= (dhdp->lb_rxp_strt_thr / D2HRING_RXCMPLT_MAX_ITEM))) {
2565 return -EINVAL;
2566 }
2567
2568 dhdp->lb_rxp_stop_thr = (D2HRING_RXCMPLT_MAX_ITEM * lb_rxp_stop_thr);
2569 return count;
2570 }
2571
2572 static struct dhd_attr dhd_attr_lb_rxp_stop_thr =
2573 __ATTR(lbrxp_stop_thr, 0660, get_lb_rxp_stop_thr, set_lb_rxp_stop_thr);
2574
2575 static ssize_t
get_lb_rxp_strt_thr(struct dhd_info * dev,char * buf)2576 get_lb_rxp_strt_thr(struct dhd_info *dev, char *buf)
2577 {
2578 dhd_info_t *dhd = (dhd_info_t *)dev;
2579 dhd_pub_t *dhdp;
2580 ssize_t ret = 0;
2581
2582 if (!dhd) {
2583 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2584 return -EINVAL;
2585 }
2586 dhdp = &dhd->pub;
2587
2588 ret = scnprintf(buf, PAGE_SIZE - 1, "%u \n",
2589 (dhdp->lb_rxp_strt_thr / D2HRING_RXCMPLT_MAX_ITEM));
2590 return ret;
2591 }
2592
2593 static ssize_t
set_lb_rxp_strt_thr(struct dhd_info * dev,const char * buf,size_t count)2594 set_lb_rxp_strt_thr(struct dhd_info *dev, const char *buf, size_t count)
2595 {
2596 dhd_info_t *dhd = (dhd_info_t *)dev;
2597 dhd_pub_t *dhdp;
2598 uint32 lb_rxp_strt_thr;
2599
2600 if (!dhd) {
2601 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
2602 return -EINVAL;
2603 }
2604 dhdp = &dhd->pub;
2605
2606 lb_rxp_strt_thr = bcm_strtoul(buf, NULL, 10);
2607 sscanf(buf, "%u", &lb_rxp_strt_thr);
2608
2609 /* disable lb_rxp flow ctrl */
2610 if (lb_rxp_strt_thr == 0) {
2611 dhdp->lb_rxp_strt_thr = 0;
2612 dhdp->lb_rxp_stop_thr = 0;
2613 atomic_set(&dhd->pub.lb_rxp_flow_ctrl, FALSE);
2614 return count;
2615 }
2616 /* should be less than dhdp->lb_rxp_stop_thr */
2617 if ((lb_rxp_strt_thr <= 0) ||
2618 (lb_rxp_strt_thr >= (dhdp->lb_rxp_stop_thr / D2HRING_RXCMPLT_MAX_ITEM))) {
2619 return -EINVAL;
2620 }
2621 dhdp->lb_rxp_strt_thr = (D2HRING_RXCMPLT_MAX_ITEM * lb_rxp_strt_thr);
2622 return count;
2623 }
2624 static struct dhd_attr dhd_attr_lb_rxp_strt_thr =
2625 __ATTR(lbrxp_strt_thr, 0660, get_lb_rxp_strt_thr, set_lb_rxp_strt_thr);
2626
2627 #endif /* DHD_LB_RXP */
2628
2629 static ssize_t
show_candidacy_override(struct dhd_info * dev,char * buf)2630 show_candidacy_override(struct dhd_info *dev, char *buf)
2631 {
2632 ssize_t ret = 0;
2633
2634 ret = scnprintf(buf, PAGE_SIZE - 1,
2635 "%d\n", (int)dev->dhd_lb_candidacy_override);
2636 return ret;
2637 }
2638
2639 static ssize_t
set_candidacy_override(struct dhd_info * dev,const char * buf,size_t count)2640 set_candidacy_override(struct dhd_info *dev, const char *buf, size_t count)
2641 {
2642
2643 int val = 0;
2644 val = bcm_atoi(buf);
2645
2646 if (val > 0) {
2647 dev->dhd_lb_candidacy_override = TRUE;
2648 } else {
2649 dev->dhd_lb_candidacy_override = FALSE;
2650 }
2651
2652 DHD_ERROR(("set dhd_lb_candidacy_override %d\n", dev->dhd_lb_candidacy_override));
2653 return count;
2654 }
2655
2656 static struct dhd_attr dhd_candidacy_override =
2657 __ATTR(candidacy_override, 0660, show_candidacy_override, set_candidacy_override);
2658
2659 static ssize_t
show_primary_mask(struct dhd_info * dev,char * buf)2660 show_primary_mask(struct dhd_info *dev, char *buf)
2661 {
2662 ssize_t ret = 0;
2663
2664 ret = scnprintf(buf, PAGE_SIZE - 1,
2665 "%02lx\n", *cpumask_bits(dev->cpumask_primary));
2666 return ret;
2667 }
2668
2669 static ssize_t
set_primary_mask(struct dhd_info * dev,const char * buf,size_t count)2670 set_primary_mask(struct dhd_info *dev, const char *buf, size_t count)
2671 {
2672 int ret;
2673
2674 cpumask_var_t primary_mask;
2675
2676 if (!alloc_cpumask_var(&primary_mask, GFP_KERNEL)) {
2677 DHD_ERROR(("Can't allocate cpumask vars\n"));
2678 return count;
2679 }
2680
2681 cpumask_clear(primary_mask);
2682 ret = cpumask_parse(buf, primary_mask);
2683 if (ret < 0) {
2684 DHD_ERROR(("Setting cpumask failed ret = %d\n", ret));
2685 return count;
2686 }
2687
2688 cpumask_clear(dev->cpumask_primary);
2689 cpumask_or(dev->cpumask_primary, dev->cpumask_primary, primary_mask);
2690
2691 DHD_ERROR(("set cpumask results cpumask_primary 0x%2lx\n",
2692 *cpumask_bits(dev->cpumask_primary)));
2693
2694 dhd_select_cpu_candidacy(dev);
2695 return count;
2696 }
2697
2698 static struct dhd_attr dhd_primary_mask =
2699 __ATTR(primary_mask, 0660, show_primary_mask, set_primary_mask);
2700
2701 static ssize_t
show_secondary_mask(struct dhd_info * dev,char * buf)2702 show_secondary_mask(struct dhd_info *dev, char *buf)
2703 {
2704 ssize_t ret = 0;
2705
2706 ret = scnprintf(buf, PAGE_SIZE - 1,
2707 "%02lx\n", *cpumask_bits(dev->cpumask_secondary));
2708 return ret;
2709 }
2710
2711 static ssize_t
set_secondary_mask(struct dhd_info * dev,const char * buf,size_t count)2712 set_secondary_mask(struct dhd_info *dev, const char *buf, size_t count)
2713 {
2714 int ret;
2715
2716 cpumask_var_t secondary_mask;
2717
2718 if (!alloc_cpumask_var(&secondary_mask, GFP_KERNEL)) {
2719 DHD_ERROR(("Can't allocate cpumask vars\n"));
2720 return count;
2721 }
2722
2723 cpumask_clear(secondary_mask);
2724
2725 ret = cpumask_parse(buf, secondary_mask);
2726
2727 if (ret < 0) {
2728 DHD_ERROR(("Setting cpumask failed ret = %d\n", ret));
2729 return count;
2730 }
2731
2732 cpumask_clear(dev->cpumask_secondary);
2733 cpumask_or(dev->cpumask_secondary, dev->cpumask_secondary, secondary_mask);
2734
2735 DHD_ERROR(("set cpumask results cpumask_secondary 0x%2lx\n",
2736 *cpumask_bits(dev->cpumask_secondary)));
2737
2738 dhd_select_cpu_candidacy(dev);
2739
2740 return count;
2741 }
2742
2743 static struct dhd_attr dhd_secondary_mask =
2744 __ATTR(secondary_mask, 0660, show_secondary_mask, set_secondary_mask);
2745
2746 static ssize_t
show_rx_cpu(struct dhd_info * dev,char * buf)2747 show_rx_cpu(struct dhd_info *dev, char *buf)
2748 {
2749 ssize_t ret = 0;
2750
2751 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", atomic_read(&dev->rx_napi_cpu));
2752 return ret;
2753 }
2754
2755 static ssize_t
set_rx_cpu(struct dhd_info * dev,const char * buf,size_t count)2756 set_rx_cpu(struct dhd_info *dev, const char *buf, size_t count)
2757 {
2758 uint32 val;
2759
2760 if (!dev->dhd_lb_candidacy_override) {
2761 DHD_ERROR(("dhd_lb_candidacy_override is required %d\n",
2762 dev->dhd_lb_candidacy_override));
2763 return count;
2764 }
2765
2766 val = (uint32)bcm_atoi(buf);
2767 if (val >= nr_cpu_ids)
2768 {
2769 DHD_ERROR(("%s : can't set the value out of number of cpus, val = %u\n",
2770 __FUNCTION__, val));
2771 }
2772
2773 atomic_set(&dev->rx_napi_cpu, val);
2774 DHD_ERROR(("%s: rx_napi_cpu = %d\n", __FUNCTION__, atomic_read(&dev->rx_napi_cpu)));
2775 return count;
2776 }
2777
2778 static struct dhd_attr dhd_rx_cpu =
2779 __ATTR(rx_cpu, 0660, show_rx_cpu, set_rx_cpu);
2780
2781 static ssize_t
show_tx_cpu(struct dhd_info * dev,char * buf)2782 show_tx_cpu(struct dhd_info *dev, char *buf)
2783 {
2784 ssize_t ret = 0;
2785
2786 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", atomic_read(&dev->tx_cpu));
2787 return ret;
2788 }
2789
2790 static ssize_t
set_tx_cpu(struct dhd_info * dev,const char * buf,size_t count)2791 set_tx_cpu(struct dhd_info *dev, const char *buf, size_t count)
2792 {
2793 uint32 val;
2794
2795 if (!dev->dhd_lb_candidacy_override) {
2796 DHD_ERROR(("dhd_lb_candidacy_override is required %d\n",
2797 dev->dhd_lb_candidacy_override));
2798 return count;
2799 }
2800
2801 val = (uint32)bcm_atoi(buf);
2802 if (val >= nr_cpu_ids)
2803 {
2804 DHD_ERROR(("%s : can't set the value out of number of cpus, val = %u\n",
2805 __FUNCTION__, val));
2806 return count;
2807 }
2808
2809 atomic_set(&dev->tx_cpu, val);
2810 DHD_ERROR(("%s: tx_cpu = %d\n", __FUNCTION__, atomic_read(&dev->tx_cpu)));
2811 return count;
2812 }
2813
2814 static struct dhd_attr dhd_tx_cpu =
2815 __ATTR(tx_cpu, 0660, show_tx_cpu, set_tx_cpu);
2816
2817 static struct attribute *debug_lb_attrs[] = {
2818 #if defined(DHD_LB_TXP)
2819 &dhd_attr_lbtxp.attr,
2820 #endif /* DHD_LB_TXP */
2821 #if defined(DHD_LB_RXP)
2822 &dhd_attr_lbrxp.attr,
2823 &dhd_attr_lb_rxp_stop_thr.attr,
2824 &dhd_attr_lb_rxp_strt_thr.attr,
2825 #endif /* DHD_LB_RXP */
2826 &dhd_candidacy_override.attr,
2827 &dhd_primary_mask.attr,
2828 &dhd_secondary_mask.attr,
2829 &dhd_rx_cpu.attr,
2830 &dhd_tx_cpu.attr,
2831 NULL
2832 };
2833
2834 #define to_dhd_lb(k) container_of(k, struct dhd_info, dhd_lb_kobj)
2835
2836 /*
2837 * wifi/lb kobject show function, the "attr" attribute specifices to which
2838 * node under "sys/wifi/lb" the show function is called.
2839 */
dhd_lb_show(struct kobject * kobj,struct attribute * attr,char * buf)2840 static ssize_t dhd_lb_show(struct kobject *kobj, struct attribute *attr, char *buf)
2841 {
2842 dhd_info_t *dhd;
2843 struct dhd_attr *d_attr;
2844 int ret;
2845
2846 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2847 dhd = to_dhd_lb(kobj);
2848 d_attr = to_attr(attr);
2849 GCC_DIAGNOSTIC_POP();
2850
2851 if (d_attr->show)
2852 ret = d_attr->show(dhd, buf);
2853 else
2854 ret = -EIO;
2855
2856 return ret;
2857 }
2858
2859 /*
2860 * wifi kobject show function, the "attr" attribute specifices to which
2861 * node under "sys/wifi/lb" the store function is called.
2862 */
dhd_lb_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)2863 static ssize_t dhd_lb_store(struct kobject *kobj, struct attribute *attr,
2864 const char *buf, size_t count)
2865 {
2866 dhd_info_t *dhd;
2867 struct dhd_attr *d_attr;
2868 int ret;
2869
2870 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2871 dhd = to_dhd_lb(kobj);
2872 d_attr = to_attr(attr);
2873 GCC_DIAGNOSTIC_POP();
2874
2875 if (d_attr->store)
2876 ret = d_attr->store(dhd, buf, count);
2877 else
2878 ret = -EIO;
2879
2880 return ret;
2881
2882 }
2883
2884 static struct sysfs_ops dhd_sysfs_lb_ops = {
2885 .show = dhd_lb_show,
2886 .store = dhd_lb_store,
2887 };
2888
2889 static struct kobj_type dhd_lb_ktype = {
2890 .sysfs_ops = &dhd_sysfs_lb_ops,
2891 .default_attrs = debug_lb_attrs,
2892 };
2893 #endif /* DHD_LB */
2894
2895 /* Create a kobject and attach to sysfs interface */
dhd_sysfs_init(dhd_info_t * dhd)2896 int dhd_sysfs_init(dhd_info_t *dhd)
2897 {
2898 int ret = -1;
2899
2900 if (dhd == NULL) {
2901 DHD_ERROR(("%s(): dhd is NULL \r\n", __FUNCTION__));
2902 return ret;
2903 }
2904
2905 /* Initialize the kobject */
2906 ret = kobject_init_and_add(&dhd->dhd_kobj, &dhd_ktype, NULL, "wifi");
2907 if (ret) {
2908 kobject_put(&dhd->dhd_kobj);
2909 DHD_ERROR(("%s(): Unable to allocate kobject \r\n", __FUNCTION__));
2910 return ret;
2911 }
2912
2913 #ifdef CSI_SUPPORT
2914 ret = sysfs_create_bin_file(&dhd->dhd_kobj, &dhd_attr_csi);
2915 if (ret) {
2916 DHD_ERROR(("%s: can't create %s\n", __FUNCTION__, dhd_attr_csi.attr.name));
2917 kobject_put(&dhd->dhd_kobj);
2918 return ret;
2919 }
2920 #endif /* CSI_SUPPORT */
2921
2922 /*
2923 * We are always responsible for sending the uevent that the kobject
2924 * was added to the system.
2925 */
2926 kobject_uevent(&dhd->dhd_kobj, KOBJ_ADD);
2927
2928 #ifdef DHD_LB
2929 ret = kobject_init_and_add(&dhd->dhd_lb_kobj,
2930 &dhd_lb_ktype, &dhd->dhd_kobj, "lb");
2931 if (ret) {
2932 kobject_put(&dhd->dhd_lb_kobj);
2933 DHD_ERROR(("%s(): Unable to allocate kobject \r\n", __FUNCTION__));
2934 return ret;
2935 }
2936
2937 kobject_uevent(&dhd->dhd_lb_kobj, KOBJ_ADD);
2938 #endif /* DHD_LB */
2939
2940 return ret;
2941 }
2942
2943 /* Done with the kobject and detach the sysfs interface */
dhd_sysfs_exit(dhd_info_t * dhd)2944 void dhd_sysfs_exit(dhd_info_t *dhd)
2945 {
2946 if (dhd == NULL) {
2947 DHD_ERROR(("%s(): dhd is NULL \r\n", __FUNCTION__));
2948 return;
2949 }
2950
2951 #ifdef DHD_LB
2952 kobject_put(&dhd->dhd_lb_kobj);
2953 #endif /* DHD_LB */
2954
2955 /* Releae the kobject */
2956 if (dhd->dhd_kobj.state_initialized)
2957 kobject_put(&dhd->dhd_kobj);
2958 }
2959
2960 #ifdef DHD_SUPPORT_HDM
2961 static ssize_t
hdm_load_module(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)2962 hdm_load_module(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
2963 {
2964 int val = bcm_atoi(buf);
2965
2966 if (val == 1) {
2967 DHD_ERROR(("%s : Load module from the hdm %d\n", __FUNCTION__, val));
2968 dhd_module_init_hdm();
2969 } else {
2970 DHD_ERROR(("Module load triggered with invalid value : %d\n", val));
2971 }
2972
2973 return count;
2974 }
2975
2976 static struct kobj_attribute hdm_wlan_attr =
2977 __ATTR(hdm_wlan_loader, 0660, NULL, hdm_load_module);
2978
2979 void
dhd_hdm_wlan_sysfs_init()2980 dhd_hdm_wlan_sysfs_init()
2981 {
2982 DHD_ERROR(("export hdm_wlan_loader\n"));
2983 if (sysfs_create_file(kernel_kobj, &hdm_wlan_attr.attr)) {
2984 DHD_ERROR(("export hdm_load failed\n"));
2985 }
2986 }
2987
2988 void
dhd_hdm_wlan_sysfs_deinit(struct work_struct * work)2989 dhd_hdm_wlan_sysfs_deinit(struct work_struct *work)
2990 {
2991 sysfs_remove_file(kernel_kobj, &hdm_wlan_attr.attr);
2992
2993 }
2994 #endif /* DHD_SUPPORT_HDM */
2995