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