1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
4 * Basically selected code segments from usb-cdc.c and usb-rndis.c
5 *
6 * Copyright (C) 1999-2019, Broadcom.
7 *
8 * Unless you and Broadcom execute a separate written software license
9 * agreement governing use of this software, this software is licensed to you
10 * under the terms of the GNU General Public License version 2 (the "GPL"),
11 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12 * following added to such license:
13 *
14 * As a special exception, the copyright holders of this software give you
15 * permission to link this software with independent modules, and to copy and
16 * distribute the resulting executable under terms of your choice, provided that
17 * you also meet, for each linked independent module, the terms and conditions of
18 * the license of that module. An independent module is a module which is not
19 * derived from this software. The special exception does not apply to any
20 * modifications of the software.
21 *
22 * Notwithstanding the above, under no circumstances may you combine this
23 * software in any way with any other Broadcom software provided under a license
24 * other than the GPL, without Broadcom's express prior written consent.
25 *
26 *
27 * <<Broadcom-WL-IPTag/Open:>>
28 *
29 * $Id: dhd_linux_exportfs.c 808905 2019-03-11 10:32:39Z $
30 */
31 #include <linux/kobject.h>
32 #include <linux/proc_fs.h>
33 #include <linux/sysfs.h>
34 #include <osl.h>
35 #include <dhd_dbg.h>
36 #include <dhd_linux_priv.h>
37 #ifdef DHD_ADPS_BAM_EXPORT
38 #include <wl_bam.h>
39 #endif // endif
40 #ifdef CSI_SUPPORT
41 #include <dhd_csi.h>
42 #endif /* CSI_SUPPORT */
43
44 #ifdef SHOW_LOGTRACE
45 extern dhd_pub_t* g_dhd_pub;
46 static int dhd_ring_proc_open(struct inode *inode, struct file *file);
47 ssize_t dhd_ring_proc_read(struct file *file, char *buffer, size_t tt, loff_t *loff);
48
49 static const struct file_operations dhd_ring_proc_fops = {
50 .open = dhd_ring_proc_open,
51 .read = dhd_ring_proc_read,
52 .release = single_release,
53 };
54
55 static int
dhd_ring_proc_open(struct inode * inode,struct file * file)56 dhd_ring_proc_open(struct inode *inode, struct file *file)
57 {
58 int ret = BCME_ERROR;
59 if (inode) {
60 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
61 ret = single_open(file, 0, PDE_DATA(inode));
62 #else
63 /* This feature is not supported for lower kernel versions */
64 ret = single_open(file, 0, NULL);
65 #endif // endif
66 } else {
67 DHD_ERROR(("%s: inode is NULL\n", __FUNCTION__));
68 }
69 return ret;
70 }
71
72 ssize_t
dhd_ring_proc_read(struct file * file,char __user * buffer,size_t tt,loff_t * loff)73 dhd_ring_proc_read(struct file *file, char __user *buffer, size_t tt, loff_t *loff)
74 {
75 trace_buf_info_t *trace_buf_info;
76 int ret = BCME_ERROR;
77 dhd_dbg_ring_t *ring = (dhd_dbg_ring_t *)((struct seq_file *)(file->private_data))->private;
78
79 if (ring == NULL) {
80 DHD_ERROR(("%s: ring is NULL\n", __FUNCTION__));
81 return ret;
82 }
83
84 ASSERT(g_dhd_pub);
85
86 trace_buf_info = (trace_buf_info_t *)MALLOCZ(g_dhd_pub->osh, sizeof(trace_buf_info_t));
87 if (trace_buf_info) {
88 dhd_dbg_read_ring_into_trace_buf(ring, trace_buf_info);
89 if (copy_to_user(buffer, (void*)trace_buf_info->buf, MIN(trace_buf_info->size, tt)))
90 {
91 ret = -EFAULT;
92 goto exit;
93 }
94 if (trace_buf_info->availability == BUF_NOT_AVAILABLE)
95 ret = BUF_NOT_AVAILABLE;
96 else
97 ret = trace_buf_info->size;
98 } else
99 DHD_ERROR(("Memory allocation Failed\n"));
100
101 exit:
102 if (trace_buf_info) {
103 MFREE(g_dhd_pub->osh, trace_buf_info, sizeof(trace_buf_info_t));
104 }
105 return ret;
106 }
107
108 void
dhd_dbg_ring_proc_create(dhd_pub_t * dhdp)109 dhd_dbg_ring_proc_create(dhd_pub_t *dhdp)
110 {
111 #ifdef DEBUGABILITY
112 dhd_dbg_ring_t *dbg_verbose_ring = NULL;
113
114 dbg_verbose_ring = dhd_dbg_get_ring_from_ring_id(dhdp, FW_VERBOSE_RING_ID);
115 if (dbg_verbose_ring) {
116 if (!proc_create_data("dhd_trace", S_IRUSR, NULL, &dhd_ring_proc_fops,
117 dbg_verbose_ring)) {
118 DHD_ERROR(("Failed to create /proc/dhd_trace procfs interface\n"));
119 } else {
120 DHD_ERROR(("Created /proc/dhd_trace procfs interface\n"));
121 }
122 } else {
123 DHD_ERROR(("dbg_verbose_ring is NULL, /proc/dhd_trace not created\n"));
124 }
125 #endif /* DEBUGABILITY */
126
127 #ifdef EWP_ECNTRS_LOGGING
128 if (!proc_create_data("dhd_ecounters", S_IRUSR, NULL, &dhd_ring_proc_fops,
129 dhdp->ecntr_dbg_ring)) {
130 DHD_ERROR(("Failed to create /proc/dhd_ecounters procfs interface\n"));
131 } else {
132 DHD_ERROR(("Created /proc/dhd_ecounters procfs interface\n"));
133 }
134 #endif /* EWP_ECNTRS_LOGGING */
135
136 #ifdef EWP_RTT_LOGGING
137 if (!proc_create_data("dhd_rtt", S_IRUSR, NULL, &dhd_ring_proc_fops,
138 dhdp->rtt_dbg_ring)) {
139 DHD_ERROR(("Failed to create /proc/dhd_rtt procfs interface\n"));
140 } else {
141 DHD_ERROR(("Created /proc/dhd_rtt procfs interface\n"));
142 }
143 #endif /* EWP_RTT_LOGGING */
144 }
145
146 void
dhd_dbg_ring_proc_destroy(dhd_pub_t * dhdp)147 dhd_dbg_ring_proc_destroy(dhd_pub_t *dhdp)
148 {
149 #ifdef DEBUGABILITY
150 remove_proc_entry("dhd_trace", NULL);
151 #endif /* DEBUGABILITY */
152
153 #ifdef EWP_ECNTRS_LOGGING
154 remove_proc_entry("dhd_ecounters", NULL);
155 #endif /* EWP_ECNTRS_LOGGING */
156
157 #ifdef EWP_RTT_LOGGING
158 remove_proc_entry("dhd_rtt", NULL);
159 #endif /* EWP_RTT_LOGGING */
160
161 }
162 #endif /* SHOW_LOGTRACE */
163
164 /* ----------------------------------------------------------------------------
165 * Infrastructure code for sysfs interface support for DHD
166 *
167 * What is sysfs interface?
168 * https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt
169 *
170 * Why sysfs interface?
171 * This is the Linux standard way of changing/configuring Run Time parameters
172 * for a driver. We can use this interface to control "linux" specific driver
173 * parameters.
174 *
175 * -----------------------------------------------------------------------------
176 */
177
178 #if defined(DHD_TRACE_WAKE_LOCK)
179 extern atomic_t trace_wklock_onoff;
180
181 /* Function to show the history buffer */
182 static ssize_t
show_wklock_trace(struct dhd_info * dev,char * buf)183 show_wklock_trace(struct dhd_info *dev, char *buf)
184 {
185 ssize_t ret = 0;
186 dhd_info_t *dhd = (dhd_info_t *)dev;
187
188 buf[ret] = '\n';
189 buf[ret+1] = 0;
190
191 dhd_wk_lock_stats_dump(&dhd->pub);
192 return ret+1;
193 }
194
195 /* Function to enable/disable wakelock trace */
196 static ssize_t
wklock_trace_onoff(struct dhd_info * dev,const char * buf,size_t count)197 wklock_trace_onoff(struct dhd_info *dev, const char *buf, size_t count)
198 {
199 unsigned long onoff;
200 dhd_info_t *dhd = (dhd_info_t *)dev;
201 BCM_REFERENCE(dhd);
202
203 onoff = bcm_strtoul(buf, NULL, 10);
204 if (onoff != 0 && onoff != 1) {
205 return -EINVAL;
206 }
207
208 atomic_set(&trace_wklock_onoff, onoff);
209 if (atomic_read(&trace_wklock_onoff)) {
210 printk("ENABLE WAKLOCK TRACE\n");
211 } else {
212 printk("DISABLE WAKELOCK TRACE\n");
213 }
214
215 return (ssize_t)(onoff+1);
216 }
217 #endif /* DHD_TRACE_WAKE_LOCK */
218
219 #if defined(DHD_LB_TXP)
220 static ssize_t
show_lbtxp(struct dhd_info * dev,char * buf)221 show_lbtxp(struct dhd_info *dev, char *buf)
222 {
223 ssize_t ret = 0;
224 unsigned long onoff;
225 dhd_info_t *dhd = (dhd_info_t *)dev;
226
227 onoff = atomic_read(&dhd->lb_txp_active);
228 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
229 onoff);
230 return ret;
231 }
232
233 static ssize_t
lbtxp_onoff(struct dhd_info * dev,const char * buf,size_t count)234 lbtxp_onoff(struct dhd_info *dev, const char *buf, size_t count)
235 {
236 unsigned long onoff;
237 dhd_info_t *dhd = (dhd_info_t *)dev;
238 int i;
239
240 onoff = bcm_strtoul(buf, NULL, 10);
241
242 sscanf(buf, "%lu", &onoff);
243 if (onoff != 0 && onoff != 1) {
244 return -EINVAL;
245 }
246 atomic_set(&dhd->lb_txp_active, onoff);
247
248 /* Since the scheme is changed clear the counters */
249 for (i = 0; i < NR_CPUS; i++) {
250 DHD_LB_STATS_CLR(dhd->txp_percpu_run_cnt[i]);
251 DHD_LB_STATS_CLR(dhd->tx_start_percpu_run_cnt[i]);
252 }
253
254 return count;
255 }
256
257 #endif /* DHD_LB_TXP */
258
259 #if defined(DHD_LB_RXP)
260 static ssize_t
show_lbrxp(struct dhd_info * dev,char * buf)261 show_lbrxp(struct dhd_info *dev, char *buf)
262 {
263 ssize_t ret = 0;
264 unsigned long onoff;
265 dhd_info_t *dhd = (dhd_info_t *)dev;
266
267 onoff = atomic_read(&dhd->lb_rxp_active);
268 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
269 onoff);
270 return ret;
271 }
272
273 static ssize_t
lbrxp_onoff(struct dhd_info * dev,const char * buf,size_t count)274 lbrxp_onoff(struct dhd_info *dev, const char *buf, size_t count)
275 {
276 unsigned long onoff;
277 dhd_info_t *dhd = (dhd_info_t *)dev;
278 int i, j;
279
280 onoff = bcm_strtoul(buf, NULL, 10);
281
282 sscanf(buf, "%lu", &onoff);
283 if (onoff != 0 && onoff != 1) {
284 return -EINVAL;
285 }
286 atomic_set(&dhd->lb_rxp_active, onoff);
287
288 /* Since the scheme is changed clear the counters */
289 for (i = 0; i < NR_CPUS; i++) {
290 DHD_LB_STATS_CLR(dhd->napi_percpu_run_cnt[i]);
291 for (j = 0; j < HIST_BIN_SIZE; j++) {
292 DHD_LB_STATS_CLR(dhd->napi_rx_hist[j][i]);
293 }
294 }
295
296 return count;
297 }
298 #endif /* DHD_LB_RXP */
299
300 #ifdef DHD_LOG_DUMP
301 extern int logdump_periodic_flush;
302 extern int logdump_ecntr_enable;
303 static ssize_t
show_logdump_periodic_flush(struct dhd_info * dev,char * buf)304 show_logdump_periodic_flush(struct dhd_info *dev, char *buf)
305 {
306 ssize_t ret = 0;
307 unsigned long val;
308
309 val = logdump_periodic_flush;
310 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n", val);
311 return ret;
312 }
313
314 static ssize_t
logdump_periodic_flush_onoff(struct dhd_info * dev,const char * buf,size_t count)315 logdump_periodic_flush_onoff(struct dhd_info *dev, const char *buf, size_t count)
316 {
317 unsigned long val;
318
319 val = bcm_strtoul(buf, NULL, 10);
320
321 sscanf(buf, "%lu", &val);
322 if (val != 0 && val != 1) {
323 return -EINVAL;
324 }
325 logdump_periodic_flush = val;
326 return count;
327 }
328
329 static ssize_t
show_logdump_ecntr(struct dhd_info * dev,char * buf)330 show_logdump_ecntr(struct dhd_info *dev, char *buf)
331 {
332 ssize_t ret = 0;
333 unsigned long val;
334
335 val = logdump_ecntr_enable;
336 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n", val);
337 return ret;
338 }
339
340 static ssize_t
logdump_ecntr_onoff(struct dhd_info * dev,const char * buf,size_t count)341 logdump_ecntr_onoff(struct dhd_info *dev, const char *buf, size_t count)
342 {
343 unsigned long val;
344
345 val = bcm_strtoul(buf, NULL, 10);
346
347 sscanf(buf, "%lu", &val);
348 if (val != 0 && val != 1) {
349 return -EINVAL;
350 }
351 logdump_ecntr_enable = val;
352 return count;
353 }
354
355 #endif /* DHD_LOG_DUMP */
356
357 extern uint enable_ecounter;
358 static ssize_t
show_enable_ecounter(struct dhd_info * dev,char * buf)359 show_enable_ecounter(struct dhd_info *dev, char *buf)
360 {
361 ssize_t ret = 0;
362 unsigned long onoff;
363
364 onoff = enable_ecounter;
365 ret = scnprintf(buf, PAGE_SIZE - 1, "%lu \n",
366 onoff);
367 return ret;
368 }
369
370 static ssize_t
ecounter_onoff(struct dhd_info * dev,const char * buf,size_t count)371 ecounter_onoff(struct dhd_info *dev, const char *buf, size_t count)
372 {
373 unsigned long onoff;
374 dhd_info_t *dhd = (dhd_info_t *)dev;
375 dhd_pub_t *dhdp;
376
377 if (!dhd) {
378 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
379 return count;
380 }
381 dhdp = &dhd->pub;
382 if (!FW_SUPPORTED(dhdp, ecounters)) {
383 DHD_ERROR(("%s: ecounters not supported by FW\n", __FUNCTION__));
384 return count;
385 }
386
387 onoff = bcm_strtoul(buf, NULL, 10);
388
389 sscanf(buf, "%lu", &onoff);
390 if (onoff != 0 && onoff != 1) {
391 return -EINVAL;
392 }
393
394 if (enable_ecounter == onoff) {
395 DHD_ERROR(("%s: ecounters already %d\n", __FUNCTION__, enable_ecounter));
396 return count;
397 }
398
399 enable_ecounter = onoff;
400 dhd_ecounter_configure(dhdp, enable_ecounter);
401
402 return count;
403 }
404
405 /*
406 * Generic Attribute Structure for DHD.
407 * If we have to add a new sysfs entry under /sys/bcm-dhd/, we have
408 * to instantiate an object of type dhd_attr, populate it with
409 * the required show/store functions (ex:- dhd_attr_cpumask_primary)
410 * and add the object to default_attrs[] array, that gets registered
411 * to the kobject of dhd (named bcm-dhd).
412 */
413
414 struct dhd_attr {
415 struct attribute attr;
416 ssize_t(*show)(struct dhd_info *, char *);
417 ssize_t(*store)(struct dhd_info *, const char *, size_t count);
418 };
419
420 #if defined(DHD_TRACE_WAKE_LOCK)
421 static struct dhd_attr dhd_attr_wklock =
422 __ATTR(wklock_trace, 0660, show_wklock_trace, wklock_trace_onoff);
423 #endif /* defined(DHD_TRACE_WAKE_LOCK */
424
425 #if defined(DHD_LB_TXP)
426 static struct dhd_attr dhd_attr_lbtxp =
427 __ATTR(lbtxp, 0660, show_lbtxp, lbtxp_onoff);
428 #endif /* DHD_LB_TXP */
429
430 #if defined(DHD_LB_RXP)
431 static struct dhd_attr dhd_attr_lbrxp =
432 __ATTR(lbrxp, 0660, show_lbrxp, lbrxp_onoff);
433 #endif /* DHD_LB_RXP */
434
435 #ifdef DHD_LOG_DUMP
436 static struct dhd_attr dhd_attr_logdump_periodic_flush =
437 __ATTR(logdump_periodic_flush, 0660, show_logdump_periodic_flush,
438 logdump_periodic_flush_onoff);
439 static struct dhd_attr dhd_attr_logdump_ecntr =
440 __ATTR(logdump_ecntr_enable, 0660, show_logdump_ecntr,
441 logdump_ecntr_onoff);
442 #endif /* DHD_LOG_DUMP */
443
444 static struct dhd_attr dhd_attr_ecounters =
445 __ATTR(ecounters, 0660, show_enable_ecounter, ecounter_onoff);
446
447 /* Attribute object that gets registered with "bcm-dhd" kobject tree */
448 static struct attribute *default_attrs[] = {
449 #if defined(DHD_TRACE_WAKE_LOCK)
450 &dhd_attr_wklock.attr,
451 #endif // endif
452 #if defined(DHD_LB_TXP)
453 &dhd_attr_lbtxp.attr,
454 #endif /* DHD_LB_TXP */
455 #if defined(DHD_LB_RXP)
456 &dhd_attr_lbrxp.attr,
457 #endif /* DHD_LB_RXP */
458 #ifdef DHD_LOG_DUMP
459 &dhd_attr_logdump_periodic_flush.attr,
460 &dhd_attr_logdump_ecntr.attr,
461 #endif // endif
462 &dhd_attr_ecounters.attr,
463 NULL
464 };
465
466 #define to_dhd(k) container_of(k, struct dhd_info, dhd_kobj)
467 #define to_attr(a) container_of(a, struct dhd_attr, attr)
468
469 /*
470 * bcm-dhd kobject show function, the "attr" attribute specifices to which
471 * node under "bcm-dhd" the show function is called.
472 */
dhd_show(struct kobject * kobj,struct attribute * attr,char * buf)473 static ssize_t dhd_show(struct kobject *kobj, struct attribute *attr, char *buf)
474 {
475 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
476 #pragma GCC diagnostic push
477 #pragma GCC diagnostic ignored "-Wcast-qual"
478 #endif // endif
479 dhd_info_t *dhd = to_dhd(kobj);
480 struct dhd_attr *d_attr = to_attr(attr);
481 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
482 #pragma GCC diagnostic pop
483 #endif // endif
484 int ret;
485
486 if (d_attr->show)
487 ret = d_attr->show(dhd, buf);
488 else
489 ret = -EIO;
490
491 return ret;
492 }
493
494 /*
495 * bcm-dhd kobject show function, the "attr" attribute specifices to which
496 * node under "bcm-dhd" the store function is called.
497 */
dhd_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)498 static ssize_t dhd_store(struct kobject *kobj, struct attribute *attr,
499 const char *buf, size_t count)
500 {
501 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
502 #pragma GCC diagnostic push
503 #pragma GCC diagnostic ignored "-Wcast-qual"
504 #endif // endif
505 dhd_info_t *dhd = to_dhd(kobj);
506 struct dhd_attr *d_attr = to_attr(attr);
507 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
508 #pragma GCC diagnostic pop
509 #endif // endif
510 int ret;
511
512 if (d_attr->store)
513 ret = d_attr->store(dhd, buf, count);
514 else
515 ret = -EIO;
516
517 return ret;
518
519 }
520
521 static struct sysfs_ops dhd_sysfs_ops = {
522 .show = dhd_show,
523 .store = dhd_store,
524 };
525
526 static struct kobj_type dhd_ktype = {
527 .sysfs_ops = &dhd_sysfs_ops,
528 .default_attrs = default_attrs,
529 };
530
531 #ifdef DHD_MAC_ADDR_EXPORT
532 struct ether_addr sysfs_mac_addr;
533 static ssize_t
show_mac_addr(struct dhd_info * dev,char * buf)534 show_mac_addr(struct dhd_info *dev, char *buf)
535 {
536 ssize_t ret = 0;
537
538 ret = scnprintf(buf, PAGE_SIZE - 1, MACF,
539 (uint32)sysfs_mac_addr.octet[0], (uint32)sysfs_mac_addr.octet[1],
540 (uint32)sysfs_mac_addr.octet[2], (uint32)sysfs_mac_addr.octet[3],
541 (uint32)sysfs_mac_addr.octet[4], (uint32)sysfs_mac_addr.octet[5]);
542
543 return ret;
544 }
545
546 static ssize_t
set_mac_addr(struct dhd_info * dev,const char * buf,size_t count)547 set_mac_addr(struct dhd_info *dev, const char *buf, size_t count)
548 {
549 if (!bcm_ether_atoe(buf, &sysfs_mac_addr)) {
550 DHD_ERROR(("Invalid Mac Address \n"));
551 return -EINVAL;
552 }
553
554 DHD_ERROR(("Mac Address set with "MACDBG"\n", MAC2STRDBG(&sysfs_mac_addr)));
555
556 return count;
557 }
558
559 static struct dhd_attr dhd_attr_cntl_macaddr =
560 __ATTR(mac_addr, 0660, show_mac_addr, set_mac_addr);
561 #endif /* DHD_MAC_ADDR_EXPORT */
562
563 #ifdef DHD_FW_COREDUMP
564
565 #define MEMDUMPINFO "/data/misc/wifi/.memdump.info"
566
567 uint32
get_mem_val_from_file(void)568 get_mem_val_from_file(void)
569 {
570 struct file *fp = NULL;
571 uint32 mem_val = DUMP_MEMFILE_MAX;
572 char *p_mem_val = NULL;
573 char *filepath = MEMDUMPINFO;
574 int ret = 0;
575
576 /* Read memdump info from the file */
577 fp = filp_open(filepath, O_RDONLY, 0);
578 if (IS_ERR(fp)) {
579 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
580 #if defined(CONFIG_X86)
581 /* Check if it is Live Brix Image */
582 if (strcmp(filepath, MEMDUMPINFO_LIVE) != 0) {
583 goto done;
584 }
585 /* Try if it is Installed Brix Image */
586 filepath = MEMDUMPINFO_INST;
587 DHD_ERROR(("%s: Try File [%s]\n", __FUNCTION__, filepath));
588 fp = filp_open(filepath, O_RDONLY, 0);
589 if (IS_ERR(fp)) {
590 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
591 goto done;
592 }
593 #else /* Non Brix Android platform */
594 goto done;
595 #endif /* CONFIG_X86 && OEM_ANDROID */
596 }
597
598 /* Handle success case */
599 ret = compat_kernel_read(fp, 0, (char *)&mem_val, sizeof(uint32));
600 if (ret < 0) {
601 DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
602 filp_close(fp, NULL);
603 goto done;
604 }
605
606 p_mem_val = (char*)&mem_val;
607 p_mem_val[sizeof(uint32) - 1] = '\0';
608 mem_val = bcm_atoi(p_mem_val);
609
610 filp_close(fp, NULL);
611
612 done:
613 return mem_val;
614 }
615
dhd_get_memdump_info(dhd_pub_t * dhd)616 void dhd_get_memdump_info(dhd_pub_t *dhd)
617 {
618 #ifndef DHD_EXPORT_CNTL_FILE
619 uint32 mem_val = DUMP_MEMFILE_MAX;
620
621 mem_val = get_mem_val_from_file();
622 if (mem_val != DUMP_MEMFILE_MAX)
623 dhd->memdump_enabled = mem_val;
624 #ifdef DHD_INIT_DEFAULT_MEMDUMP
625 if (mem_val == 0 || mem_val == DUMP_MEMFILE_MAX)
626 mem_val = DUMP_MEMFILE_BUGON;
627 #endif /* DHD_INIT_DEFAULT_MEMDUMP */
628 #else
629 #ifdef DHD_INIT_DEFAULT_MEMDUMP
630 if (dhd->memdump_enabled == 0 || dhd->memdump_enabled == DUMP_MEMFILE_MAX)
631 dhd->memdump_enabled = DUMP_MEMFILE_BUGON;
632 #endif /* DHD_INIT_DEFAULT_MEMDUMP */
633 #endif /* !DHD_EXPORT_CNTL_FILE */
634 DHD_ERROR(("%s: MEMDUMP ENABLED = %d\n", __FUNCTION__, dhd->memdump_enabled));
635 }
636
637 #ifdef DHD_EXPORT_CNTL_FILE
638 static ssize_t
show_memdump_info(struct dhd_info * dev,char * buf)639 show_memdump_info(struct dhd_info *dev, char *buf)
640 {
641 ssize_t ret = 0;
642 dhd_pub_t *dhdp;
643
644 if (!dev) {
645 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
646 return ret;
647 }
648
649 dhdp = &dev->pub;
650 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", dhdp->memdump_enabled);
651 return ret;
652 }
653
654 static ssize_t
set_memdump_info(struct dhd_info * dev,const char * buf,size_t count)655 set_memdump_info(struct dhd_info *dev, const char *buf, size_t count)
656 {
657 unsigned long memval;
658 dhd_pub_t *dhdp;
659
660 if (!dev) {
661 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
662 return count;
663 }
664 dhdp = &dev->pub;
665
666 memval = bcm_strtoul(buf, NULL, 10);
667 sscanf(buf, "%lu", &memval);
668
669 dhdp->memdump_enabled = (uint32)memval;
670
671 DHD_ERROR(("%s: MEMDUMP ENABLED = %iu\n", __FUNCTION__, dhdp->memdump_enabled));
672 return count;
673 }
674
675 static struct dhd_attr dhd_attr_cntl_memdump =
676 __ATTR(memdump, 0660, show_memdump_info, set_memdump_info);
677 #endif /* DHD_EXPORT_CNTL_FILE */
678 #endif /* DHD_FW_COREDUMP */
679
680 #ifdef BCMASSERT_LOG
681 #define ASSERTINFO "/data/misc/wifi/.assert.info"
682 int
get_assert_val_from_file(void)683 get_assert_val_from_file(void)
684 {
685 struct file *fp = NULL;
686 char *filepath = ASSERTINFO;
687 char *p_mem_val = NULL;
688 int mem_val = -1;
689
690 /*
691 * Read assert info from the file
692 * 0: Trigger Kernel crash by panic()
693 * 1: Print out the logs and don't trigger Kernel panic. (default)
694 * 2: Trigger Kernel crash by BUG()
695 * File doesn't exist: Keep default value (1).
696 */
697 fp = filp_open(filepath, O_RDONLY, 0);
698 if (IS_ERR(fp)) {
699 DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
700 } else {
701 int ret = compat_kernel_read(fp, 0, (char *)&mem_val, sizeof(uint32));
702 if (ret < 0) {
703 DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
704 } else {
705 p_mem_val = (char *)&mem_val;
706 p_mem_val[sizeof(uint32) - 1] = '\0';
707 mem_val = bcm_atoi(p_mem_val);
708 DHD_ERROR(("%s: ASSERT ENABLED = %d\n", __FUNCTION__, mem_val));
709 }
710 filp_close(fp, NULL);
711 }
712
713 mem_val = (mem_val >= 0) ? mem_val : 0;
714 return mem_val;
715 }
716
dhd_get_assert_info(dhd_pub_t * dhd)717 void dhd_get_assert_info(dhd_pub_t *dhd)
718 {
719 #ifndef DHD_EXPORT_CNTL_FILE
720 int mem_val = -1;
721
722 mem_val = get_assert_val_from_file();
723
724 g_assert_type = mem_val;
725 #endif /* !DHD_EXPORT_CNTL_FILE */
726 }
727
728 #ifdef DHD_EXPORT_CNTL_FILE
729 static ssize_t
show_assert_info(struct dhd_info * dev,char * buf)730 show_assert_info(struct dhd_info *dev, char *buf)
731 {
732 ssize_t ret = 0;
733
734 if (!dev) {
735 DHD_ERROR(("%s: dhd is NULL\n", __FUNCTION__));
736 return ret;
737 }
738
739 ret = scnprintf(buf, PAGE_SIZE -1, "%d\n", g_assert_type);
740 return ret;
741
742 }
743
744 static ssize_t
set_assert_info(struct dhd_info * dev,const char * buf,size_t count)745 set_assert_info(struct dhd_info *dev, const char *buf, size_t count)
746 {
747 unsigned long assert_val;
748
749 assert_val = bcm_strtoul(buf, NULL, 10);
750 sscanf(buf, "%lu", &assert_val);
751
752 g_assert_type = (uint32)assert_val;
753
754 DHD_ERROR(("%s: ASSERT ENABLED = %lu\n", __FUNCTION__, assert_val));
755 return count;
756
757 }
758
759 static struct dhd_attr dhd_attr_cntl_assert =
760 __ATTR(assert, 0660, show_assert_info, set_assert_info);
761 #endif /* DHD_EXPORT_CNTL_FILE */
762 #endif /* BCMASSERT_LOG */
763
764 #ifdef DHD_EXPORT_CNTL_FILE
765 #if defined(WRITE_WLANINFO)
766 static ssize_t
show_wifiver_info(struct dhd_info * dev,char * buf)767 show_wifiver_info(struct dhd_info *dev, char *buf)
768 {
769 ssize_t ret = 0;
770
771 ret = scnprintf(buf, PAGE_SIZE -1, "%s", version_info);
772 return ret;
773 }
774
775 static ssize_t
set_wifiver_info(struct dhd_info * dev,const char * buf,size_t count)776 set_wifiver_info(struct dhd_info *dev, const char *buf, size_t count)
777 {
778 DHD_ERROR(("Do not set version info\n"));
779 return -EINVAL;
780 }
781
782 static struct dhd_attr dhd_attr_cntl_wifiver =
783 __ATTR(wifiver, 0660, show_wifiver_info, set_wifiver_info);
784 #endif /* WRITE_WLANINFO */
785
786 #if defined(USE_CID_CHECK)
787 char cidinfostr[MAX_VNAME_LEN];
788
789 static ssize_t
show_cid_info(struct dhd_info * dev,char * buf)790 show_cid_info(struct dhd_info *dev, char *buf)
791 {
792 ssize_t ret = 0;
793
794 ret = scnprintf(buf, PAGE_SIZE -1, "%s", cidinfostr);
795 return ret;
796 }
797
798 static ssize_t
set_cid_info(struct dhd_info * dev,const char * buf,size_t count)799 set_cid_info(struct dhd_info *dev, const char *buf, size_t count)
800 {
801 int len = strlen(buf) + 1;
802 int maxstrsz;
803 maxstrsz = MAX_VNAME_LEN;
804
805 scnprintf(cidinfostr, ((len > maxstrsz) ? maxstrsz : len), "%s", buf);
806 DHD_INFO(("%s : CID info string\n", cidinfostr));
807 return count;
808 }
809
810 static struct dhd_attr dhd_attr_cntl_cidinfo =
811 __ATTR(cid, 0660, show_cid_info, set_cid_info);
812 #endif /* USE_CID_CHECK */
813
814 #if defined(GEN_SOFTAP_INFO_FILE)
815 char softapinfostr[SOFTAP_INFO_BUF_SZ];
816 static ssize_t
show_softap_info(struct dhd_info * dev,char * buf)817 show_softap_info(struct dhd_info *dev, char *buf)
818 {
819 ssize_t ret = 0;
820
821 ret = scnprintf(buf, PAGE_SIZE -1, "%s", softapinfostr);
822 return ret;
823 }
824
825 static ssize_t
set_softap_info(struct dhd_info * dev,const char * buf,size_t count)826 set_softap_info(struct dhd_info *dev, const char *buf, size_t count)
827 {
828 DHD_ERROR(("Do not set sofap related info\n"));
829 return -EINVAL;
830 }
831
832 static struct dhd_attr dhd_attr_cntl_softapinfo =
833 __ATTR(softap, 0660, show_softap_info, set_softap_info);
834 #endif /* GEN_SOFTAP_INFO_FILE */
835
836 #if defined(MIMO_ANT_SETTING)
837 unsigned long antsel;
838
839 static ssize_t
show_ant_info(struct dhd_info * dev,char * buf)840 show_ant_info(struct dhd_info *dev, char *buf)
841 {
842 ssize_t ret = 0;
843
844 ret = scnprintf(buf, PAGE_SIZE -1, "%lu\n", antsel);
845 return ret;
846 }
847
848 static ssize_t
set_ant_info(struct dhd_info * dev,const char * buf,size_t count)849 set_ant_info(struct dhd_info *dev, const char *buf, size_t count)
850 {
851 unsigned long ant_val;
852
853 ant_val = bcm_strtoul(buf, NULL, 10);
854 sscanf(buf, "%lu", &ant_val);
855
856 /*
857 * Check value
858 * 0 - Not set, handle same as file not exist
859 */
860 if (ant_val > 3) {
861 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
862 __FUNCTION__, ant_val));
863 return -EINVAL;
864 }
865
866 antsel = ant_val;
867 DHD_ERROR(("[WIFI_SEC] %s: Set Antinfo val = %lu \n", __FUNCTION__, antsel));
868 return count;
869 }
870
871 static struct dhd_attr dhd_attr_cntl_antinfo =
872 __ATTR(ant, 0660, show_ant_info, set_ant_info);
873 #endif /* MIMO_ANT_SETTING */
874
875 #ifdef DHD_PM_CONTROL_FROM_FILE
876 extern bool g_pm_control;
877 extern uint32 pmmode_val;
878 static ssize_t
show_pm_info(struct dhd_info * dev,char * buf)879 show_pm_info(struct dhd_info *dev, char *buf)
880 {
881 ssize_t ret = 0;
882
883 if (!g_pm_control) {
884 ret = scnprintf(buf, PAGE_SIZE -1, "PM mode is not set\n");
885 } else {
886 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", pmmode_val);
887 }
888 return ret;
889 }
890
891 static ssize_t
set_pm_info(struct dhd_info * dev,const char * buf,size_t count)892 set_pm_info(struct dhd_info *dev, const char *buf, size_t count)
893 {
894 unsigned long pm_val;
895
896 pm_val = bcm_strtoul(buf, NULL, 10);
897 sscanf(buf, "%lu", &pm_val);
898
899 if (pm_val > 2) {
900 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
901 __FUNCTION__, pm_val));
902 return -EINVAL;
903 }
904
905 if (!pm_val) {
906 g_pm_control = TRUE;
907 } else {
908 g_pm_control = FALSE;
909 }
910
911 pmmode_val = (uint32)pm_val;
912 DHD_ERROR(("[WIFI_SEC] %s: Set pminfo val = %u\n", __FUNCTION__, pmmode_val));
913 return count;
914 }
915
916 static struct dhd_attr dhd_attr_cntl_pminfo =
917 __ATTR(pm, 0660, show_pm_info, set_pm_info);
918 #endif /* DHD_PM_CONTROL_FROM_FILE */
919
920 #ifdef LOGTRACE_FROM_FILE
921 unsigned long logtrace_val = 1;
922
923 static ssize_t
show_logtrace_info(struct dhd_info * dev,char * buf)924 show_logtrace_info(struct dhd_info *dev, char *buf)
925 {
926 ssize_t ret = 0;
927
928 ret = scnprintf(buf, PAGE_SIZE -1, "%lu\n", logtrace_val);
929 return ret;
930 }
931
932 static ssize_t
set_logtrace_info(struct dhd_info * dev,const char * buf,size_t count)933 set_logtrace_info(struct dhd_info *dev, const char *buf, size_t count)
934 {
935 unsigned long onoff;
936
937 onoff = bcm_strtoul(buf, NULL, 10);
938 sscanf(buf, "%lu", &onoff);
939
940 if (onoff > 2) {
941 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %lu \n",
942 __FUNCTION__, onoff));
943 return -EINVAL;
944 }
945
946 logtrace_val = onoff;
947 DHD_ERROR(("[WIFI_SEC] %s: LOGTRACE On/Off from sysfs = %lu\n",
948 __FUNCTION__, logtrace_val));
949 return count;
950 }
951
952 static struct dhd_attr dhd_attr_cntl_logtraceinfo =
953 __ATTR(logtrace, 0660, show_logtrace_info, set_logtrace_info);
954 #endif /* LOGTRACE_FROM_FILE */
955
956 #ifdef USE_WFA_CERT_CONF
957 #ifdef BCMSDIO
958 uint32 bus_txglom = VALUENOTSET;
959
960 static ssize_t
show_bustxglom(struct dhd_info * dev,char * buf)961 show_bustxglom(struct dhd_info *dev, char *buf)
962 {
963 ssize_t ret = 0;
964
965 if (bus_txglom == VALUENOTSET) {
966 ret = scnprintf(buf, PAGE_SIZE - 1, "%s\n", "bustxglom not set from sysfs");
967 } else {
968 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", bus_txglom);
969 }
970 return ret;
971 }
972
973 static ssize_t
set_bustxglom(struct dhd_info * dev,const char * buf,size_t count)974 set_bustxglom(struct dhd_info *dev, const char *buf, size_t count)
975 {
976 uint32 onoff;
977
978 onoff = (uint32)bcm_atoi(buf);
979 sscanf(buf, "%u", &onoff);
980
981 if (onoff > 2) {
982 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
983 __FUNCTION__, onoff));
984 return -EINVAL;
985 }
986
987 bus_txglom = onoff;
988 DHD_ERROR(("[WIFI_SEC] %s: BUS TXGLOM On/Off from sysfs = %u\n",
989 __FUNCTION__, bus_txglom));
990 return count;
991 }
992
993 static struct dhd_attr dhd_attr_cntl_bustxglom =
994 __ATTR(bustxglom, 0660, show_bustxglom, set_bustxglom);
995 #endif /* BCMSDIO */
996
997 #if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM)
998 uint32 roam_off = VALUENOTSET;
999
1000 static ssize_t
show_roamoff(struct dhd_info * dev,char * buf)1001 show_roamoff(struct dhd_info *dev, char *buf)
1002 {
1003 ssize_t ret = 0;
1004
1005 if (roam_off == VALUENOTSET) {
1006 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "roam_off not set from sysfs");
1007 } else {
1008 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", roam_off);
1009 }
1010 return ret;
1011 }
1012
1013 static ssize_t
set_roamoff(struct dhd_info * dev,const char * buf,size_t count)1014 set_roamoff(struct dhd_info *dev, const char *buf, size_t count)
1015 {
1016 uint32 onoff;
1017
1018 onoff = bcm_atoi(buf);
1019 sscanf(buf, "%u", &onoff);
1020
1021 if (onoff > 2) {
1022 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1023 __FUNCTION__, onoff));
1024 return -EINVAL;
1025 }
1026
1027 roam_off = onoff;
1028 DHD_ERROR(("[WIFI_SEC] %s: ROAM On/Off from sysfs = %u\n",
1029 __FUNCTION__, roam_off));
1030 return count;
1031 }
1032
1033 static struct dhd_attr dhd_attr_cntl_roamoff =
1034 __ATTR(roamoff, 0660, show_roamoff, set_roamoff);
1035 #endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */
1036
1037 #ifdef USE_WL_FRAMEBURST
1038 uint32 frameburst = VALUENOTSET;
1039
1040 static ssize_t
show_frameburst(struct dhd_info * dev,char * buf)1041 show_frameburst(struct dhd_info *dev, char *buf)
1042 {
1043 ssize_t ret = 0;
1044
1045 if (frameburst == VALUENOTSET) {
1046 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "frameburst not set from sysfs");
1047 } else {
1048 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", frameburst);
1049 }
1050 return ret;
1051 }
1052
1053 static ssize_t
set_frameburst(struct dhd_info * dev,const char * buf,size_t count)1054 set_frameburst(struct dhd_info *dev, const char *buf, size_t count)
1055 {
1056 uint32 onoff;
1057
1058 onoff = bcm_atoi(buf);
1059 sscanf(buf, "%u", &onoff);
1060
1061 if (onoff > 2) {
1062 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1063 __FUNCTION__, onoff));
1064 return -EINVAL;
1065 }
1066
1067 frameburst = onoff;
1068 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1069 __FUNCTION__, frameburst));
1070 return count;
1071 }
1072
1073 static struct dhd_attr dhd_attr_cntl_frameburst =
1074 __ATTR(frameburst, 0660, show_frameburst, set_frameburst);
1075 #endif /* USE_WL_FRAMEBURST */
1076
1077 #ifdef USE_WL_TXBF
1078 uint32 txbf = VALUENOTSET;
1079
1080 static ssize_t
show_txbf(struct dhd_info * dev,char * buf)1081 show_txbf(struct dhd_info *dev, char *buf)
1082 {
1083 ssize_t ret = 0;
1084
1085 if (txbf == VALUENOTSET) {
1086 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "txbf not set from sysfs");
1087 } else {
1088 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", txbf);
1089 }
1090 return ret;
1091 }
1092
1093 static ssize_t
set_txbf(struct dhd_info * dev,const char * buf,size_t count)1094 set_txbf(struct dhd_info *dev, const char *buf, size_t count)
1095 {
1096 uint32 onoff;
1097
1098 onoff = bcm_atoi(buf);
1099 sscanf(buf, "%u", &onoff);
1100
1101 if (onoff > 2) {
1102 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1103 __FUNCTION__, onoff));
1104 return -EINVAL;
1105 }
1106
1107 txbf = onoff;
1108 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1109 __FUNCTION__, txbf));
1110 return count;
1111 }
1112
1113 static struct dhd_attr dhd_attr_cntl_txbf =
1114 __ATTR(txbf, 0660, show_txbf, set_txbf);
1115 #endif /* USE_WL_TXBF */
1116
1117 #ifdef PROP_TXSTATUS
1118 uint32 proptx = VALUENOTSET;
1119
1120 static ssize_t
show_proptx(struct dhd_info * dev,char * buf)1121 show_proptx(struct dhd_info *dev, char *buf)
1122 {
1123 ssize_t ret = 0;
1124
1125 if (proptx == VALUENOTSET) {
1126 ret = scnprintf(buf, PAGE_SIZE -1, "%s\n", "proptx not set from sysfs");
1127 } else {
1128 ret = scnprintf(buf, PAGE_SIZE -1, "%u\n", proptx);
1129 }
1130 return ret;
1131 }
1132
1133 static ssize_t
set_proptx(struct dhd_info * dev,const char * buf,size_t count)1134 set_proptx(struct dhd_info *dev, const char *buf, size_t count)
1135 {
1136 uint32 onoff;
1137
1138 onoff = bcm_strtoul(buf, NULL, 10);
1139 sscanf(buf, "%u", &onoff);
1140
1141 if (onoff > 2) {
1142 DHD_ERROR(("[WIFI_SEC] %s: Set Invalid value %u \n",
1143 __FUNCTION__, onoff));
1144 return -EINVAL;
1145 }
1146
1147 proptx = onoff;
1148 DHD_ERROR(("[WIFI_SEC] %s: FRAMEBURST On/Off from sysfs = %u\n",
1149 __FUNCTION__, txbf));
1150 return count;
1151 }
1152
1153 static struct dhd_attr dhd_attr_cntl_proptx =
1154 __ATTR(proptx, 0660, show_proptx, set_proptx);
1155
1156 #endif /* PROP_TXSTATUS */
1157 #endif /* USE_WFA_CERT_CONF */
1158 #endif /* DHD_EXPORT_CNTL_FILE */
1159
1160 #ifdef DHD_SEND_HANG_PRIVCMD_ERRORS
1161 uint32 report_hang_privcmd_err = 1;
1162
1163 static ssize_t
show_hang_privcmd_err(struct dhd_info * dev,char * buf)1164 show_hang_privcmd_err(struct dhd_info *dev, char *buf)
1165 {
1166 ssize_t ret = 0;
1167
1168 ret = scnprintf(buf, PAGE_SIZE - 1, "%u\n", report_hang_privcmd_err);
1169 return ret;
1170 }
1171
1172 static ssize_t
set_hang_privcmd_err(struct dhd_info * dev,const char * buf,size_t count)1173 set_hang_privcmd_err(struct dhd_info *dev, const char *buf, size_t count)
1174 {
1175 uint32 val;
1176
1177 val = bcm_atoi(buf);
1178 sscanf(buf, "%u", &val);
1179
1180 report_hang_privcmd_err = val ? 1 : 0;
1181 DHD_INFO(("%s: Set report HANG for private cmd error: %d\n",
1182 __FUNCTION__, report_hang_privcmd_err));
1183 return count;
1184 }
1185
1186 static struct dhd_attr dhd_attr_hang_privcmd_err =
1187 __ATTR(hang_privcmd_err, 0660, show_hang_privcmd_err, set_hang_privcmd_err);
1188 #endif /* DHD_SEND_HANG_PRIVCMD_ERRORS */
1189
1190 #if defined(DISABLE_HE_ENAB) || defined(CUSTOM_CONTROL_HE_ENAB)
1191 uint8 control_he_enab = 1;
1192 #endif /* DISABLE_HE_ENAB || CUSTOM_CONTROL_HE_ENAB */
1193
1194 #if defined(CUSTOM_CONTROL_HE_ENAB)
1195 static ssize_t
show_control_he_enab(struct dhd_info * dev,char * buf)1196 show_control_he_enab(struct dhd_info *dev, char *buf)
1197 {
1198 ssize_t ret = 0;
1199
1200 ret = scnprintf(buf, PAGE_SIZE - 1, "%d\n", control_he_enab);
1201 return ret;
1202 }
1203
1204 static ssize_t
set_control_he_enab(struct dhd_info * dev,const char * buf,size_t count)1205 set_control_he_enab(struct dhd_info *dev, const char *buf, size_t count)
1206 {
1207 uint32 val;
1208
1209 val = bcm_atoi(buf);
1210
1211 control_he_enab = val ? 1 : 0;
1212 DHD_ERROR(("%s: Set control he enab: %d\n", __FUNCTION__, control_he_enab));
1213 return count;
1214 }
1215
1216 static struct dhd_attr dhd_attr_control_he_enab=
1217 __ATTR(control_he_enab, 0660, show_control_he_enab, set_control_he_enab);
1218 #endif /* CUSTOM_CONTROL_HE_ENAB */
1219 /* Attribute object that gets registered with "wifi" kobject tree */
1220 static struct attribute *control_file_attrs[] = {
1221 #ifdef DHD_MAC_ADDR_EXPORT
1222 &dhd_attr_cntl_macaddr.attr,
1223 #endif /* DHD_MAC_ADDR_EXPORT */
1224 #ifdef DHD_EXPORT_CNTL_FILE
1225 #ifdef DHD_FW_COREDUMP
1226 &dhd_attr_cntl_memdump.attr,
1227 #endif /* DHD_FW_COREDUMP */
1228 #ifdef BCMASSERT_LOG
1229 &dhd_attr_cntl_assert.attr,
1230 #endif /* BCMASSERT_LOG */
1231 #ifdef WRITE_WLANINFO
1232 &dhd_attr_cntl_wifiver.attr,
1233 #endif /* WRITE_WLANINFO */
1234 #ifdef USE_CID_CHECK
1235 &dhd_attr_cntl_cidinfo.attr,
1236 #endif /* USE_CID_CHECK */
1237 #ifdef GEN_SOFTAP_INFO_FILE
1238 &dhd_attr_cntl_softapinfo.attr,
1239 #endif /* GEN_SOFTAP_INFO_FILE */
1240 #ifdef MIMO_ANT_SETTING
1241 &dhd_attr_cntl_antinfo.attr,
1242 #endif /* MIMO_ANT_SETTING */
1243 #ifdef DHD_PM_CONTROL_FROM_FILE
1244 &dhd_attr_cntl_pminfo.attr,
1245 #endif /* DHD_PM_CONTROL_FROM_FILE */
1246 #ifdef LOGTRACE_FROM_FILE
1247 &dhd_attr_cntl_logtraceinfo.attr,
1248 #endif /* LOGTRACE_FROM_FILE */
1249 #ifdef USE_WFA_CERT_CONF
1250 #ifdef BCMSDIO
1251 &dhd_attr_cntl_bustxglom.attr,
1252 #endif /* BCMSDIO */
1253 &dhd_attr_cntl_roamoff.attr,
1254 #ifdef USE_WL_FRAMEBURST
1255 &dhd_attr_cntl_frameburst.attr,
1256 #endif /* USE_WL_FRAMEBURST */
1257 #ifdef USE_WL_TXBF
1258 &dhd_attr_cntl_txbf.attr,
1259 #endif /* USE_WL_TXBF */
1260 #ifdef PROP_TXSTATUS
1261 &dhd_attr_cntl_proptx.attr,
1262 #endif /* PROP_TXSTATUS */
1263 #endif /* USE_WFA_CERT_CONF */
1264 #endif /* DHD_EXPORT_CNTL_FILE */
1265 #ifdef DHD_ADPS_BAM_EXPORT
1266 &dhd_attr_adps_bam.attr,
1267 #endif /* DHD_ADPS_BAM_EXPORT */
1268 #ifdef DHD_SEND_HANG_PRIVCMD_ERRORS
1269 &dhd_attr_hang_privcmd_err.attr,
1270 #endif /* DHD_SEND_HANG_PRIVCMD_ERRORS */
1271 #if defined(CUSTOM_CONTROL_HE_ENAB)
1272 &dhd_attr_control_he_enab.attr,
1273 #endif /* CUSTOM_CONTROL_HE_ENAB */
1274 NULL
1275 };
1276
1277 #define to_cntl_dhd(k) container_of(k, struct dhd_info, dhd_conf_file_kobj)
1278
1279 /*
1280 * wifi kobject show function, the "attr" attribute specifices to which
1281 * node under "sys/wifi" the show function is called.
1282 */
dhd_cntl_show(struct kobject * kobj,struct attribute * attr,char * buf)1283 static ssize_t dhd_cntl_show(struct kobject *kobj, struct attribute *attr, char *buf)
1284 {
1285 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1286 #pragma GCC diagnostic push
1287 #pragma GCC diagnostic ignored "-Wcast-qual"
1288 #endif // endif
1289 dhd_info_t *dhd = to_cntl_dhd(kobj);
1290 struct dhd_attr *d_attr = to_attr(attr);
1291 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1292 #pragma GCC diagnostic pop
1293 #endif // endif
1294 int ret;
1295
1296 if (d_attr->show)
1297 ret = d_attr->show(dhd, buf);
1298 else
1299 ret = -EIO;
1300
1301 return ret;
1302 }
1303
1304 /*
1305 * wifi kobject show function, the "attr" attribute specifices to which
1306 * node under "sys/wifi" the store function is called.
1307 */
dhd_cntl_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)1308 static ssize_t dhd_cntl_store(struct kobject *kobj, struct attribute *attr,
1309 const char *buf, size_t count)
1310 {
1311 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1312 #pragma GCC diagnostic push
1313 #pragma GCC diagnostic ignored "-Wcast-qual"
1314 #endif // endif
1315 dhd_info_t *dhd = to_cntl_dhd(kobj);
1316 struct dhd_attr *d_attr = to_attr(attr);
1317 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1318 #pragma GCC diagnostic pop
1319 #endif // endif
1320 int ret;
1321
1322 if (d_attr->store)
1323 ret = d_attr->store(dhd, buf, count);
1324 else
1325 ret = -EIO;
1326
1327 return ret;
1328
1329 }
1330
1331 static struct sysfs_ops dhd_sysfs_cntl_ops = {
1332 .show = dhd_cntl_show,
1333 .store = dhd_cntl_store,
1334 };
1335
1336 static struct kobj_type dhd_cntl_file_ktype = {
1337 .sysfs_ops = &dhd_sysfs_cntl_ops,
1338 .default_attrs = control_file_attrs,
1339 };
1340
1341 #ifdef CSI_SUPPORT
1342 /* 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)1343 static ssize_t read_csi_data(struct file *filp, struct kobject *kobj,
1344 struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
1345 {
1346 dhd_info_t *dhd = to_dhd(kobj);
1347 int n = 0;
1348
1349 n = dhd_csi_dump_list(&dhd->pub, buf);
1350 DHD_INFO(("Dump data to file, size %d\n", n));
1351 dhd_csi_clean_list(&dhd->pub);
1352
1353 return n;
1354 }
1355
1356 static struct bin_attribute dhd_attr_csi = {
1357 .attr = { .name = "csi",
1358 .mode = 0660, },
1359 .size = MAX_CSI_FILESZ,
1360 .read = read_csi_data,
1361 };
1362 #endif /* CSI_SUPPORT */
1363
1364 /* Create a kobject and attach to sysfs interface */
dhd_sysfs_init(dhd_info_t * dhd)1365 int dhd_sysfs_init(dhd_info_t *dhd)
1366 {
1367 int ret = -1;
1368
1369 if (dhd == NULL) {
1370 DHD_ERROR(("%s(): dhd is NULL \r\n", __FUNCTION__));
1371 return ret;
1372 }
1373
1374 /* Initialize the kobject */
1375 ret = kobject_init_and_add(&dhd->dhd_kobj, &dhd_ktype, NULL, "bcm-dhd");
1376 if (ret) {
1377 kobject_put(&dhd->dhd_kobj);
1378 DHD_ERROR(("%s(): Unable to allocate kobject \r\n", __FUNCTION__));
1379 return ret;
1380 }
1381 ret = kobject_init_and_add(&dhd->dhd_conf_file_kobj, &dhd_cntl_file_ktype, NULL, "wifi");
1382 if (ret) {
1383 kobject_put(&dhd->dhd_conf_file_kobj);
1384 DHD_ERROR(("%s(): Unable to allocate kobject \r\n", __FUNCTION__));
1385 return ret;
1386 }
1387
1388 #ifdef CSI_SUPPORT
1389 ret = sysfs_create_bin_file(&dhd->dhd_kobj, &dhd_attr_csi);
1390 if (ret) {
1391 DHD_ERROR(("%s: can't create %s\n", __FUNCTION__, dhd_attr_csi.attr.name));
1392 kobject_put(&dhd->dhd_kobj);
1393 return ret;
1394 }
1395 #endif /* CSI_SUPPORT */
1396
1397 /*
1398 * We are always responsible for sending the uevent that the kobject
1399 * was added to the system.
1400 */
1401 kobject_uevent(&dhd->dhd_kobj, KOBJ_ADD);
1402 kobject_uevent(&dhd->dhd_conf_file_kobj, KOBJ_ADD);
1403
1404 return ret;
1405 }
1406
1407 /* Done with the kobject and detach the sysfs interface */
dhd_sysfs_exit(dhd_info_t * dhd)1408 void dhd_sysfs_exit(dhd_info_t *dhd)
1409 {
1410 if (dhd == NULL) {
1411 DHD_ERROR(("%s(): dhd is NULL \r\n", __FUNCTION__));
1412 return;
1413 }
1414
1415 /* Releae the kobject */
1416 if (dhd->dhd_kobj.state_initialized)
1417 kobject_put(&dhd->dhd_kobj);
1418 if (dhd->dhd_conf_file_kobj.state_initialized)
1419 kobject_put(&dhd->dhd_conf_file_kobj);
1420 }
1421