1/*
2 * NXP Wireless LAN device driver: debugfs
3 *
4 * Copyright 2011-2020 NXP
5 *
6 * This software file (the "File") is distributed by NXP
7 * under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License").  You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available by writing to the Free Software Foundation, Inc.,
11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13 *
14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
16 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
17 * this warranty disclaimer.
18 */
19
20#include <linux/debugfs.h>
21
22#include "main.h"
23#include "11n.h"
24
25
26static struct dentry *mwifiex_dfs_dir;
27
28static char *bss_modes[] = {
29	"UNSPECIFIED",
30	"ADHOC",
31	"STATION",
32	"AP",
33	"AP_VLAN",
34	"WDS",
35	"MONITOR",
36	"MESH_POINT",
37	"P2P_CLIENT",
38	"P2P_GO",
39	"P2P_DEVICE",
40};
41
42/*
43 * Proc info file read handler.
44 *
45 * This function is called when the 'info' file is opened for reading.
46 * It prints the following driver related information -
47 *      - Driver name
48 *      - Driver version
49 *      - Driver extended version
50 *      - Interface name
51 *      - BSS mode
52 *      - Media state (connected or disconnected)
53 *      - MAC address
54 *      - Total number of Tx bytes
55 *      - Total number of Rx bytes
56 *      - Total number of Tx packets
57 *      - Total number of Rx packets
58 *      - Total number of dropped Tx packets
59 *      - Total number of dropped Rx packets
60 *      - Total number of corrupted Tx packets
61 *      - Total number of corrupted Rx packets
62 *      - Carrier status (on or off)
63 *      - Tx queue status (started or stopped)
64 *
65 * For STA mode drivers, it also prints the following extra -
66 *      - ESSID
67 *      - BSSID
68 *      - Channel
69 *      - Region code
70 *      - Multicast count
71 *      - Multicast addresses
72 */
73static ssize_t
74mwifiex_info_read(struct file *file, char __user *ubuf,
75		  size_t count, loff_t *ppos)
76{
77	struct mwifiex_private *priv =
78		(struct mwifiex_private *) file->private_data;
79	struct net_device *netdev = priv->netdev;
80	struct netdev_hw_addr *ha;
81	struct netdev_queue *txq;
82	unsigned long page = get_zeroed_page(GFP_KERNEL);
83	char *p = (char *) page, fmt[64];
84	struct mwifiex_bss_info info;
85	ssize_t ret;
86	int i = 0;
87
88	if (!p)
89		return -ENOMEM;
90
91	memset(&info, 0, sizeof(info));
92	ret = mwifiex_get_bss_info(priv, &info);
93	if (ret)
94		goto free_and_exit;
95
96	mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);
97
98	mwifiex_get_ver_ext(priv, 0);
99
100	p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
101	p += sprintf(p, "driver_version = %s", fmt);
102	p += sprintf(p, "\nverext = %s", priv->version_str);
103	p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name);
104
105	if (info.bss_mode >= ARRAY_SIZE(bss_modes))
106		p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);
107	else
108		p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
109
110	p += sprintf(p, "media_state=\"%s\"\n",
111		     (!priv->media_connected ? "Disconnected" : "Connected"));
112	p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
113
114	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
115		p += sprintf(p, "multicast_count=\"%d\"\n",
116			     netdev_mc_count(netdev));
117		p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len,
118			     info.ssid.ssid);
119		p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
120		p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
121		p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
122		p += sprintf(p, "region_code=\"0x%x\"\n",
123			     priv->adapter->region_code);
124
125		netdev_for_each_mc_addr(ha, netdev)
126			p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
127					i++, ha->addr);
128	}
129
130	p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
131	p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);
132	p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);
133	p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);
134	p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);
135	p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);
136	p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);
137	p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);
138	p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))
139					 ? "on" : "off"));
140	p += sprintf(p, "tx queue");
141	for (i = 0; i < netdev->num_tx_queues; i++) {
142		txq = netdev_get_tx_queue(netdev, i);
143		p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?
144			     "stopped" : "started");
145	}
146	p += sprintf(p, "\n");
147
148	ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
149				      (unsigned long) p - page);
150
151free_and_exit:
152	free_page(page);
153	return ret;
154}
155
156/*
157 * Proc getlog file read handler.
158 *
159 * This function is called when the 'getlog' file is opened for reading
160 * It prints the following log information -
161 *      - Number of multicast Tx frames
162 *      - Number of failed packets
163 *      - Number of Tx retries
164 *      - Number of multicast Tx retries
165 *      - Number of duplicate frames
166 *      - Number of RTS successes
167 *      - Number of RTS failures
168 *      - Number of ACK failures
169 *      - Number of fragmented Rx frames
170 *      - Number of multicast Rx frames
171 *      - Number of FCS errors
172 *      - Number of Tx frames
173 *      - WEP ICV error counts
174 *      - Number of received beacons
175 *      - Number of missed beacons
176 */
177static ssize_t
178mwifiex_getlog_read(struct file *file, char __user *ubuf,
179		    size_t count, loff_t *ppos)
180{
181	struct mwifiex_private *priv =
182		(struct mwifiex_private *) file->private_data;
183	unsigned long page = get_zeroed_page(GFP_KERNEL);
184	char *p = (char *) page;
185	ssize_t ret;
186	struct mwifiex_ds_get_stats stats;
187
188	if (!p)
189		return -ENOMEM;
190
191	memset(&stats, 0, sizeof(stats));
192	ret = mwifiex_get_stats_info(priv, &stats);
193	if (ret)
194		goto free_and_exit;
195
196	p += sprintf(p, "\n"
197		     "mcasttxframe     %u\n"
198		     "failed           %u\n"
199		     "retry            %u\n"
200		     "multiretry       %u\n"
201		     "framedup         %u\n"
202		     "rtssuccess       %u\n"
203		     "rtsfailure       %u\n"
204		     "ackfailure       %u\n"
205		     "rxfrag           %u\n"
206		     "mcastrxframe     %u\n"
207		     "fcserror         %u\n"
208		     "txframe          %u\n"
209		     "wepicverrcnt-1   %u\n"
210		     "wepicverrcnt-2   %u\n"
211		     "wepicverrcnt-3   %u\n"
212		     "wepicverrcnt-4   %u\n"
213		     "bcn_rcv_cnt   %u\n"
214		     "bcn_miss_cnt   %u\n",
215		     stats.mcast_tx_frame,
216		     stats.failed,
217		     stats.retry,
218		     stats.multi_retry,
219		     stats.frame_dup,
220		     stats.rts_success,
221		     stats.rts_failure,
222		     stats.ack_failure,
223		     stats.rx_frag,
224		     stats.mcast_rx_frame,
225		     stats.fcs_error,
226		     stats.tx_frame,
227		     stats.wep_icv_error[0],
228		     stats.wep_icv_error[1],
229		     stats.wep_icv_error[2],
230		     stats.wep_icv_error[3],
231		     stats.bcn_rcv_cnt,
232		     stats.bcn_miss_cnt);
233
234
235	ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
236				      (unsigned long) p - page);
237
238free_and_exit:
239	free_page(page);
240	return ret;
241}
242
243/* Sysfs histogram file read handler.
244 *
245 * This function is called when the 'histogram' file is opened for reading
246 * It prints the following histogram information -
247 *      - Number of histogram samples
248 *      - Receive packet number of each rx_rate
249 *      - Receive packet number of each snr
250 *      - Receive packet number of each nosie_flr
251 *      - Receive packet number of each signal streath
252 */
253static ssize_t
254mwifiex_histogram_read(struct file *file, char __user *ubuf,
255		       size_t count, loff_t *ppos)
256{
257	struct mwifiex_private *priv =
258		(struct mwifiex_private *)file->private_data;
259	ssize_t ret;
260	struct mwifiex_histogram_data *phist_data;
261	int i, value;
262	unsigned long page = get_zeroed_page(GFP_KERNEL);
263	char *p = (char *)page;
264
265	if (!p)
266		return -ENOMEM;
267
268	if (!priv || !priv->hist_data) {
269		ret = -EFAULT;
270		goto free_and_exit;
271	}
272
273	phist_data = priv->hist_data;
274
275	p += sprintf(p, "\n"
276		     "total samples = %d\n",
277		     atomic_read(&phist_data->num_samples));
278
279	p += sprintf(p,
280		     "rx rates (in Mbps): 0=1M   1=2M 2=5.5M  3=11M   4=6M   5=9M  6=12M\n"
281		     "7=18M  8=24M  9=36M  10=48M  11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
282
283	if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
284		p += sprintf(p,
285			     "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n");
286	} else {
287		p += sprintf(p, "\n");
288	}
289
290	for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {
291		value = atomic_read(&phist_data->rx_rate[i]);
292		if (value)
293			p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
294	}
295
296	if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
297		for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;
298		     i++) {
299			value = atomic_read(&phist_data->rx_rate[i]);
300			if (value)
301				p += sprintf(p, "rx_rate[%02d] = %d\n",
302					   i, value);
303		}
304	}
305
306	for (i = 0; i < MWIFIEX_MAX_SNR; i++) {
307		value =  atomic_read(&phist_data->snr[i]);
308		if (value)
309			p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
310	}
311	for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
312		value = atomic_read(&phist_data->noise_flr[i]);
313		if (value)
314			p += sprintf(p, "noise_flr[%02ddBm] = %d\n",
315				(int)(i-128), value);
316	}
317	for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
318		value = atomic_read(&phist_data->sig_str[i]);
319		if (value)
320			p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
321				i, value);
322	}
323
324	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
325				      (unsigned long)p - page);
326
327free_and_exit:
328	free_page(page);
329	return ret;
330}
331
332static ssize_t
333mwifiex_histogram_write(struct file *file, const char __user *ubuf,
334			size_t count, loff_t *ppos)
335{
336	struct mwifiex_private *priv = (void *)file->private_data;
337
338	if (priv && priv->hist_data)
339		mwifiex_hist_data_reset(priv);
340	return 0;
341}
342
343static struct mwifiex_debug_info info;
344
345/*
346 * Proc debug file read handler.
347 *
348 * This function is called when the 'debug' file is opened for reading
349 * It prints the following log information -
350 *      - Interrupt count
351 *      - WMM AC VO packets count
352 *      - WMM AC VI packets count
353 *      - WMM AC BE packets count
354 *      - WMM AC BK packets count
355 *      - Maximum Tx buffer size
356 *      - Tx buffer size
357 *      - Current Tx buffer size
358 *      - Power Save mode
359 *      - Power Save state
360 *      - Deep Sleep status
361 *      - Device wakeup required status
362 *      - Number of wakeup tries
363 *      - Host Sleep configured status
364 *      - Host Sleep activated status
365 *      - Number of Tx timeouts
366 *      - Number of command timeouts
367 *      - Last timed out command ID
368 *      - Last timed out command action
369 *      - Last command ID
370 *      - Last command action
371 *      - Last command index
372 *      - Last command response ID
373 *      - Last command response index
374 *      - Last event
375 *      - Last event index
376 *      - Number of host to card command failures
377 *      - Number of sleep confirm command failures
378 *      - Number of host to card data failure
379 *      - Number of deauthentication events
380 *      - Number of disassociation events
381 *      - Number of link lost events
382 *      - Number of deauthentication commands
383 *      - Number of association success commands
384 *      - Number of association failure commands
385 *      - Number of commands sent
386 *      - Number of data packets sent
387 *      - Number of command responses received
388 *      - Number of events received
389 *      - Tx BA stream table (TID, RA)
390 *      - Rx reorder table (TID, TA, Start window, Window size, Buffer)
391 */
392static ssize_t
393mwifiex_debug_read(struct file *file, char __user *ubuf,
394		   size_t count, loff_t *ppos)
395{
396	struct mwifiex_private *priv =
397		(struct mwifiex_private *) file->private_data;
398	unsigned long page = get_zeroed_page(GFP_KERNEL);
399	char *p = (char *) page;
400	ssize_t ret;
401
402	if (!p)
403		return -ENOMEM;
404
405	ret = mwifiex_get_debug_info(priv, &info);
406	if (ret)
407		goto free_and_exit;
408
409	p += mwifiex_debug_info_to_buffer(priv, p, &info);
410
411	ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
412				      (unsigned long) p - page);
413
414free_and_exit:
415	free_page(page);
416	return ret;
417}
418
419static u32 saved_reg_type, saved_reg_offset, saved_reg_value;
420
421/*
422 * Proc regrdwr file write handler.
423 *
424 * This function is called when the 'regrdwr' file is opened for writing
425 *
426 * This function can be used to write to a register.
427 */
428static ssize_t
429mwifiex_regrdwr_write(struct file *file,
430		      const char __user *ubuf, size_t count, loff_t *ppos)
431{
432	char *buf;
433	int ret;
434	u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
435
436	buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
437	if (IS_ERR(buf))
438		return PTR_ERR(buf);
439
440	sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
441
442	if (reg_type == 0 || reg_offset == 0) {
443		ret = -EINVAL;
444		goto done;
445	} else {
446		saved_reg_type = reg_type;
447		saved_reg_offset = reg_offset;
448		saved_reg_value = reg_value;
449		ret = count;
450	}
451done:
452	kfree(buf);
453	return ret;
454}
455
456/*
457 * Proc regrdwr file read handler.
458 *
459 * This function is called when the 'regrdwr' file is opened for reading
460 *
461 * This function can be used to read from a register.
462 */
463static ssize_t
464mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
465		     size_t count, loff_t *ppos)
466{
467	struct mwifiex_private *priv =
468		(struct mwifiex_private *) file->private_data;
469	unsigned long addr = get_zeroed_page(GFP_KERNEL);
470	char *buf = (char *) addr;
471	int pos = 0, ret = 0;
472	u32 reg_value;
473
474	if (!buf)
475		return -ENOMEM;
476
477	if (!saved_reg_type) {
478		/* No command has been given */
479		pos += snprintf(buf, PAGE_SIZE, "0");
480		goto done;
481	}
482	/* Set command has been given */
483	if (saved_reg_value != UINT_MAX) {
484		ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset,
485					saved_reg_value);
486
487		pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n",
488				saved_reg_type, saved_reg_offset,
489				saved_reg_value);
490
491		ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
492
493		goto done;
494	}
495	/* Get command has been given */
496	ret = mwifiex_reg_read(priv, saved_reg_type,
497			       saved_reg_offset, &reg_value);
498	if (ret) {
499		ret = -EINVAL;
500		goto done;
501	}
502
503	pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type,
504			saved_reg_offset, reg_value);
505
506	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
507
508done:
509	free_page(addr);
510	return ret;
511}
512
513/* Proc debug_mask file read handler.
514 * This function is called when the 'debug_mask' file is opened for reading
515 * This function can be used read driver debugging mask value.
516 */
517static ssize_t
518mwifiex_debug_mask_read(struct file *file, char __user *ubuf,
519			size_t count, loff_t *ppos)
520{
521	struct mwifiex_private *priv =
522		(struct mwifiex_private *)file->private_data;
523	unsigned long page = get_zeroed_page(GFP_KERNEL);
524	char *buf = (char *)page;
525	size_t ret = 0;
526	int pos = 0;
527
528	if (!buf)
529		return -ENOMEM;
530
531	pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n",
532			priv->adapter->debug_mask);
533	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
534
535	free_page(page);
536	return ret;
537}
538
539/* Proc debug_mask file read handler.
540 * This function is called when the 'debug_mask' file is opened for reading
541 * This function can be used read driver debugging mask value.
542 */
543static ssize_t
544mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,
545			 size_t count, loff_t *ppos)
546{
547	int ret;
548	unsigned long debug_mask;
549	struct mwifiex_private *priv = (void *)file->private_data;
550	char *buf;
551
552	buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
553	if (IS_ERR(buf))
554		return PTR_ERR(buf);
555
556	if (kstrtoul(buf, 0, &debug_mask)) {
557		ret = -EINVAL;
558		goto done;
559	}
560
561	priv->adapter->debug_mask = debug_mask;
562	ret = count;
563done:
564	kfree(buf);
565	return ret;
566}
567
568/* debugfs verext file write handler.
569 * This function is called when the 'verext' file is opened for write
570 */
571static ssize_t
572mwifiex_verext_write(struct file *file, const char __user *ubuf,
573		     size_t count, loff_t *ppos)
574{
575	int ret;
576	u32 versionstrsel;
577	struct mwifiex_private *priv = (void *)file->private_data;
578	char buf[16];
579
580	memset(buf, 0, sizeof(buf));
581
582	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
583		return -EFAULT;
584
585	ret = kstrtou32(buf, 10, &versionstrsel);
586	if (ret)
587		return ret;
588
589	priv->versionstrsel = versionstrsel;
590
591	return count;
592}
593
594/* Proc verext file read handler.
595 * This function is called when the 'verext' file is opened for reading
596 * This function can be used read driver exteneed verion string.
597 */
598static ssize_t
599mwifiex_verext_read(struct file *file, char __user *ubuf,
600		    size_t count, loff_t *ppos)
601{
602	struct mwifiex_private *priv =
603		(struct mwifiex_private *)file->private_data;
604	char buf[256];
605	int ret;
606
607	mwifiex_get_ver_ext(priv, priv->versionstrsel);
608	ret = snprintf(buf, sizeof(buf), "version string: %s\n",
609		       priv->version_str);
610
611	return simple_read_from_buffer(ubuf, count, ppos, buf, ret);
612}
613
614/* Proc memrw file write handler.
615 * This function is called when the 'memrw' file is opened for writing
616 * This function can be used to write to a memory location.
617 */
618static ssize_t
619mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,
620		    loff_t *ppos)
621{
622	int ret;
623	char cmd;
624	struct mwifiex_ds_mem_rw mem_rw;
625	u16 cmd_action;
626	struct mwifiex_private *priv = (void *)file->private_data;
627	char *buf;
628
629	buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
630	if (IS_ERR(buf))
631		return PTR_ERR(buf);
632
633	ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);
634	if (ret != 3) {
635		ret = -EINVAL;
636		goto done;
637	}
638
639	if ((cmd == 'r') || (cmd == 'R')) {
640		cmd_action = HostCmd_ACT_GEN_GET;
641		mem_rw.value = 0;
642	} else if ((cmd == 'w') || (cmd == 'W')) {
643		cmd_action = HostCmd_ACT_GEN_SET;
644	} else {
645		ret = -EINVAL;
646		goto done;
647	}
648
649	memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw));
650	if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0,
651			     &mem_rw, true))
652		ret = -1;
653	else
654		ret = count;
655
656done:
657	kfree(buf);
658	return ret;
659}
660
661/* Proc memrw file read handler.
662 * This function is called when the 'memrw' file is opened for reading
663 * This function can be used to read from a memory location.
664 */
665static ssize_t
666mwifiex_memrw_read(struct file *file, char __user *ubuf,
667		   size_t count, loff_t *ppos)
668{
669	struct mwifiex_private *priv = (void *)file->private_data;
670	unsigned long addr = get_zeroed_page(GFP_KERNEL);
671	char *buf = (char *)addr;
672	int ret, pos = 0;
673
674	if (!buf)
675		return -ENOMEM;
676
677	pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr,
678			priv->mem_rw.value);
679	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
680
681	free_page(addr);
682	return ret;
683}
684
685static u32 saved_offset = -1, saved_bytes = -1;
686
687/*
688 * Proc rdeeprom file write handler.
689 *
690 * This function is called when the 'rdeeprom' file is opened for writing
691 *
692 * This function can be used to write to a RDEEPROM location.
693 */
694static ssize_t
695mwifiex_rdeeprom_write(struct file *file,
696		       const char __user *ubuf, size_t count, loff_t *ppos)
697{
698	char *buf;
699	int ret = 0;
700	int offset = -1, bytes = -1;
701
702	buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
703	if (IS_ERR(buf))
704		return PTR_ERR(buf);
705
706	sscanf(buf, "%d %d", &offset, &bytes);
707
708	if (offset == -1 || bytes == -1) {
709		ret = -EINVAL;
710		goto done;
711	} else {
712		saved_offset = offset;
713		saved_bytes = bytes;
714		ret = count;
715	}
716done:
717	kfree(buf);
718	return ret;
719}
720
721/*
722 * Proc rdeeprom read write handler.
723 *
724 * This function is called when the 'rdeeprom' file is opened for reading
725 *
726 * This function can be used to read from a RDEEPROM location.
727 */
728static ssize_t
729mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
730		      size_t count, loff_t *ppos)
731{
732	struct mwifiex_private *priv =
733		(struct mwifiex_private *) file->private_data;
734	unsigned long addr = get_zeroed_page(GFP_KERNEL);
735	char *buf = (char *) addr;
736	int pos, ret, i;
737	u8 value[MAX_EEPROM_DATA];
738
739	if (!buf)
740		return -ENOMEM;
741
742	if (saved_offset == -1) {
743		/* No command has been given */
744		pos = snprintf(buf, PAGE_SIZE, "0");
745		goto done;
746	}
747
748	/* Get command has been given */
749	ret = mwifiex_eeprom_read(priv, (u16) saved_offset,
750				  (u16) saved_bytes, value);
751	if (ret) {
752		ret = -EINVAL;
753		goto out_free;
754	}
755
756	pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
757
758	for (i = 0; i < saved_bytes; i++)
759		pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
760
761done:
762	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
763out_free:
764	free_page(addr);
765	return ret;
766}
767
768/* Proc hscfg file write handler
769 * This function can be used to configure the host sleep parameters.
770 */
771static ssize_t
772mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
773		    size_t count, loff_t *ppos)
774{
775	struct mwifiex_private *priv = (void *)file->private_data;
776	char *buf;
777	int ret, arg_num;
778	struct mwifiex_ds_hs_cfg hscfg;
779	int conditions = HS_CFG_COND_DEF;
780	u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
781
782	buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
783	if (IS_ERR(buf))
784		return PTR_ERR(buf);
785
786	arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
787
788	memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
789
790	if (arg_num > 3) {
791		mwifiex_dbg(priv->adapter, ERROR,
792			    "Too many arguments\n");
793		ret = -EINVAL;
794		goto done;
795	}
796
797	if (arg_num >= 1 && arg_num < 3)
798		mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
799				      MWIFIEX_SYNC_CMD, &hscfg);
800
801	if (arg_num) {
802		if (conditions == HS_CFG_CANCEL) {
803			mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
804			ret = count;
805			goto done;
806		}
807		hscfg.conditions = conditions;
808	}
809	if (arg_num >= 2)
810		hscfg.gpio = gpio;
811	if (arg_num == 3)
812		hscfg.gap = gap;
813
814	hscfg.is_invoke_hostcmd = false;
815	mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
816			      MWIFIEX_SYNC_CMD, &hscfg);
817
818	mwifiex_enable_hs(priv->adapter);
819	clear_bit(MWIFIEX_IS_HS_ENABLING, &priv->adapter->work_flags);
820	ret = count;
821done:
822	kfree(buf);
823	return ret;
824}
825
826/* Proc hscfg file read handler
827 * This function can be used to read host sleep configuration
828 * parameters from driver.
829 */
830static ssize_t
831mwifiex_hscfg_read(struct file *file, char __user *ubuf,
832		   size_t count, loff_t *ppos)
833{
834	struct mwifiex_private *priv = (void *)file->private_data;
835	unsigned long addr = get_zeroed_page(GFP_KERNEL);
836	char *buf = (char *)addr;
837	int pos, ret;
838	struct mwifiex_ds_hs_cfg hscfg;
839
840	if (!buf)
841		return -ENOMEM;
842
843	mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
844			      MWIFIEX_SYNC_CMD, &hscfg);
845
846	pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
847		       hscfg.gpio, hscfg.gap);
848
849	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
850
851	free_page(addr);
852	return ret;
853}
854
855static ssize_t
856mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,
857			    size_t count, loff_t *ppos)
858{
859	struct mwifiex_private *priv = file->private_data;
860	char buf[3];
861	bool timeshare_coex;
862	int ret;
863	unsigned int len;
864
865	if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
866		return -EOPNOTSUPP;
867
868	ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
869			       HostCmd_ACT_GEN_GET, 0, &timeshare_coex, true);
870	if (ret)
871		return ret;
872
873	len = sprintf(buf, "%d\n", timeshare_coex);
874	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
875}
876
877static ssize_t
878mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,
879			     size_t count, loff_t *ppos)
880{
881	bool timeshare_coex;
882	struct mwifiex_private *priv = file->private_data;
883	char kbuf[16];
884	int ret;
885
886	if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)
887		return -EOPNOTSUPP;
888
889	memset(kbuf, 0, sizeof(kbuf));
890
891	if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count)))
892		return -EFAULT;
893
894	if (strtobool(kbuf, &timeshare_coex))
895		return -EINVAL;
896
897	ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,
898			       HostCmd_ACT_GEN_SET, 0, &timeshare_coex, true);
899	if (ret)
900		return ret;
901	else
902		return count;
903}
904
905static ssize_t
906mwifiex_reset_write(struct file *file,
907		    const char __user *ubuf, size_t count, loff_t *ppos)
908{
909	struct mwifiex_private *priv = file->private_data;
910	struct mwifiex_adapter *adapter = priv->adapter;
911	bool result;
912	int rc;
913
914	rc = kstrtobool_from_user(ubuf, count, &result);
915	if (rc)
916		return rc;
917
918	if (!result)
919		return -EINVAL;
920
921	if (adapter->if_ops.card_reset) {
922		dev_info(adapter->dev, "Resetting per request\n");
923		adapter->if_ops.card_reset(adapter);
924	}
925
926	return count;
927}
928
929#define MWIFIEX_DFS_ADD_FILE(name) do {                                 \
930	debugfs_create_file(#name, 0644, priv->dfs_dev_dir, priv,       \
931			    &mwifiex_dfs_##name##_fops);                \
932} while (0);
933
934#define MWIFIEX_DFS_FILE_OPS(name)                                      \
935static const struct file_operations mwifiex_dfs_##name##_fops = {       \
936	.read = mwifiex_##name##_read,                                  \
937	.write = mwifiex_##name##_write,                                \
938	.open = simple_open,                                            \
939};
940
941#define MWIFIEX_DFS_FILE_READ_OPS(name)                                 \
942static const struct file_operations mwifiex_dfs_##name##_fops = {       \
943	.read = mwifiex_##name##_read,                                  \
944	.open = simple_open,                                            \
945};
946
947#define MWIFIEX_DFS_FILE_WRITE_OPS(name)                                \
948static const struct file_operations mwifiex_dfs_##name##_fops = {       \
949	.write = mwifiex_##name##_write,                                \
950	.open = simple_open,                                            \
951};
952
953
954MWIFIEX_DFS_FILE_READ_OPS(info);
955MWIFIEX_DFS_FILE_READ_OPS(debug);
956MWIFIEX_DFS_FILE_READ_OPS(getlog);
957MWIFIEX_DFS_FILE_OPS(regrdwr);
958MWIFIEX_DFS_FILE_OPS(rdeeprom);
959MWIFIEX_DFS_FILE_OPS(memrw);
960MWIFIEX_DFS_FILE_OPS(hscfg);
961MWIFIEX_DFS_FILE_OPS(histogram);
962MWIFIEX_DFS_FILE_OPS(debug_mask);
963MWIFIEX_DFS_FILE_OPS(timeshare_coex);
964MWIFIEX_DFS_FILE_WRITE_OPS(reset);
965MWIFIEX_DFS_FILE_OPS(verext);
966
967/*
968 * This function creates the debug FS directory structure and the files.
969 */
970void
971mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
972{
973	if (!mwifiex_dfs_dir || !priv)
974		return;
975
976	priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,
977					       mwifiex_dfs_dir);
978
979	if (!priv->dfs_dev_dir)
980		return;
981
982	MWIFIEX_DFS_ADD_FILE(info);
983	MWIFIEX_DFS_ADD_FILE(debug);
984	MWIFIEX_DFS_ADD_FILE(getlog);
985	MWIFIEX_DFS_ADD_FILE(regrdwr);
986	MWIFIEX_DFS_ADD_FILE(rdeeprom);
987
988	MWIFIEX_DFS_ADD_FILE(memrw);
989	MWIFIEX_DFS_ADD_FILE(hscfg);
990	MWIFIEX_DFS_ADD_FILE(histogram);
991	MWIFIEX_DFS_ADD_FILE(debug_mask);
992	MWIFIEX_DFS_ADD_FILE(timeshare_coex);
993	MWIFIEX_DFS_ADD_FILE(reset);
994	MWIFIEX_DFS_ADD_FILE(verext);
995}
996
997/*
998 * This function removes the debug FS directory structure and the files.
999 */
1000void
1001mwifiex_dev_debugfs_remove(struct mwifiex_private *priv)
1002{
1003	if (!priv)
1004		return;
1005
1006	debugfs_remove_recursive(priv->dfs_dev_dir);
1007}
1008
1009/*
1010 * This function creates the top level proc directory.
1011 */
1012void
1013mwifiex_debugfs_init(void)
1014{
1015	if (!mwifiex_dfs_dir)
1016		mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL);
1017}
1018
1019/*
1020 * This function removes the top level proc directory.
1021 */
1022void
1023mwifiex_debugfs_remove(void)
1024{
1025	debugfs_remove(mwifiex_dfs_dir);
1026}
1027