• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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