1// SPDX-License-Identifier: GPL-2.0
2#include <linux/dcache.h>
3#include <linux/debugfs.h>
4#include <linux/delay.h>
5#include <linux/hardirq.h>
6#include <linux/mm.h>
7#include <linux/string.h>
8#include <linux/slab.h>
9#include <linux/export.h>
10
11#include "decl.h"
12#include "cmd.h"
13#include "debugfs.h"
14
15static struct dentry *lbs_dir;
16static char *szStates[] = {
17	"Connected",
18	"Disconnected"
19};
20
21#ifdef PROC_DEBUG
22static void lbs_debug_init(struct lbs_private *priv);
23#endif
24
25static ssize_t write_file_dummy(struct file *file, const char __user *buf,
26                                size_t count, loff_t *ppos)
27{
28        return -EINVAL;
29}
30
31static const size_t len = PAGE_SIZE;
32
33static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
34				  size_t count, loff_t *ppos)
35{
36	struct lbs_private *priv = file->private_data;
37	size_t pos = 0;
38	unsigned long addr = get_zeroed_page(GFP_KERNEL);
39	char *buf = (char *)addr;
40	ssize_t res;
41	if (!buf)
42		return -ENOMEM;
43
44	pos += snprintf(buf+pos, len-pos, "state = %s\n",
45				szStates[priv->connect_status]);
46	pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
47				(u32) priv->regioncode);
48
49	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
50
51	free_page(addr);
52	return res;
53}
54
55static ssize_t lbs_sleepparams_write(struct file *file,
56				const char __user *user_buf, size_t count,
57				loff_t *ppos)
58{
59	struct lbs_private *priv = file->private_data;
60	ssize_t ret;
61	struct sleep_params sp;
62	int p1, p2, p3, p4, p5, p6;
63	char *buf;
64
65	buf = memdup_user_nul(user_buf, min(count, len - 1));
66	if (IS_ERR(buf))
67		return PTR_ERR(buf);
68
69	ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
70	if (ret != 6) {
71		ret = -EINVAL;
72		goto out_unlock;
73	}
74	sp.sp_error = p1;
75	sp.sp_offset = p2;
76	sp.sp_stabletime = p3;
77	sp.sp_calcontrol = p4;
78	sp.sp_extsleepclk = p5;
79	sp.sp_reserved = p6;
80
81	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp);
82	if (!ret)
83		ret = count;
84	else if (ret > 0)
85		ret = -EINVAL;
86
87out_unlock:
88	kfree(buf);
89	return ret;
90}
91
92static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
93				  size_t count, loff_t *ppos)
94{
95	struct lbs_private *priv = file->private_data;
96	ssize_t ret;
97	size_t pos = 0;
98	struct sleep_params sp;
99	unsigned long addr = get_zeroed_page(GFP_KERNEL);
100	char *buf = (char *)addr;
101	if (!buf)
102		return -ENOMEM;
103
104	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
105	if (ret)
106		goto out_unlock;
107
108	pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error,
109			sp.sp_offset, sp.sp_stabletime,
110			sp.sp_calcontrol, sp.sp_extsleepclk,
111			sp.sp_reserved);
112
113	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
114
115out_unlock:
116	free_page(addr);
117	return ret;
118}
119
120static ssize_t lbs_host_sleep_write(struct file *file,
121				const char __user *user_buf, size_t count,
122				loff_t *ppos)
123{
124	struct lbs_private *priv = file->private_data;
125	ssize_t ret;
126	int host_sleep;
127	char *buf;
128
129	buf = memdup_user_nul(user_buf, min(count, len - 1));
130	if (IS_ERR(buf))
131		return PTR_ERR(buf);
132
133	ret = sscanf(buf, "%d", &host_sleep);
134	if (ret != 1) {
135		ret = -EINVAL;
136		goto out_unlock;
137	}
138
139	if (host_sleep == 0)
140		ret = lbs_set_host_sleep(priv, 0);
141	else if (host_sleep == 1) {
142		if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
143			netdev_info(priv->dev,
144				    "wake parameters not configured\n");
145			ret = -EINVAL;
146			goto out_unlock;
147		}
148		ret = lbs_set_host_sleep(priv, 1);
149	} else {
150		netdev_err(priv->dev, "invalid option\n");
151		ret = -EINVAL;
152	}
153
154	if (!ret)
155		ret = count;
156
157out_unlock:
158	kfree(buf);
159	return ret;
160}
161
162static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
163				  size_t count, loff_t *ppos)
164{
165	struct lbs_private *priv = file->private_data;
166	ssize_t ret;
167	size_t pos = 0;
168	unsigned long addr = get_zeroed_page(GFP_KERNEL);
169	char *buf = (char *)addr;
170	if (!buf)
171		return -ENOMEM;
172
173	pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);
174
175	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
176
177	free_page(addr);
178	return ret;
179}
180
181/*
182 * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
183 * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
184 * firmware. Here's an example:
185 *	04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00
186 *	00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03
187 *	00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
188 *
189 * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length,
190 * 00 00 are the data bytes of this TLV. For this TLV, their meaning is
191 * defined in mrvlietypes_thresholds
192 *
193 * This function searches in this TLV data chunk for a given TLV type
194 * and returns a pointer to the first data byte of the TLV, or to NULL
195 * if the TLV hasn't been found.
196 */
197static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size)
198{
199	struct mrvl_ie_header *tlv_h;
200	uint16_t length;
201	ssize_t pos = 0;
202
203	while (pos < size) {
204		tlv_h = (struct mrvl_ie_header *) tlv;
205		if (!tlv_h->len)
206			return NULL;
207		if (tlv_h->type == cpu_to_le16(tlv_type))
208			return tlv_h;
209		length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h);
210		pos += length;
211		tlv += length;
212	}
213	return NULL;
214}
215
216
217static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
218				  struct file *file, char __user *userbuf,
219				  size_t count, loff_t *ppos)
220{
221	struct cmd_ds_802_11_subscribe_event *subscribed;
222	struct mrvl_ie_thresholds *got;
223	struct lbs_private *priv = file->private_data;
224	ssize_t ret = 0;
225	size_t pos = 0;
226	char *buf;
227	u8 value;
228	u8 freq;
229	int events = 0;
230
231	buf = (char *)get_zeroed_page(GFP_KERNEL);
232	if (!buf)
233		return -ENOMEM;
234
235	subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL);
236	if (!subscribed) {
237		ret = -ENOMEM;
238		goto out_page;
239	}
240
241	subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed));
242	subscribed->action = cpu_to_le16(CMD_ACT_GET);
243
244	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed);
245	if (ret)
246		goto out_cmd;
247
248	got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv));
249	if (got) {
250		value = got->value;
251		freq  = got->freq;
252		events = le16_to_cpu(subscribed->events);
253
254		pos += snprintf(buf, len, "%d %d %d\n", value, freq,
255				!!(events & event_mask));
256	}
257
258	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
259
260 out_cmd:
261	kfree(subscribed);
262
263 out_page:
264	free_page((unsigned long)buf);
265	return ret;
266}
267
268
269static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
270				   struct file *file,
271				   const char __user *userbuf, size_t count,
272				   loff_t *ppos)
273{
274	struct cmd_ds_802_11_subscribe_event *events;
275	struct mrvl_ie_thresholds *tlv;
276	struct lbs_private *priv = file->private_data;
277	int value, freq, new_mask;
278	uint16_t curr_mask;
279	char *buf;
280	int ret;
281
282	buf = memdup_user_nul(userbuf, min(count, len - 1));
283	if (IS_ERR(buf))
284		return PTR_ERR(buf);
285
286	ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask);
287	if (ret != 3) {
288		ret = -EINVAL;
289		goto out_page;
290	}
291	events = kzalloc(sizeof(*events), GFP_KERNEL);
292	if (!events) {
293		ret = -ENOMEM;
294		goto out_page;
295	}
296
297	events->hdr.size = cpu_to_le16(sizeof(*events));
298	events->action = cpu_to_le16(CMD_ACT_GET);
299
300	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
301	if (ret)
302		goto out_events;
303
304	curr_mask = le16_to_cpu(events->events);
305
306	if (new_mask)
307		new_mask = curr_mask | event_mask;
308	else
309		new_mask = curr_mask & ~event_mask;
310
311	/* Now everything is set and we can send stuff down to the firmware */
312
313	tlv = (void *)events->tlv;
314
315	events->action = cpu_to_le16(CMD_ACT_SET);
316	events->events = cpu_to_le16(new_mask);
317	tlv->header.type = cpu_to_le16(tlv_type);
318	tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header));
319	tlv->value = value;
320	if (tlv_type != TLV_TYPE_BCNMISS)
321		tlv->freq = freq;
322
323	/* The command header, the action, the event mask, and one TLV */
324	events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv));
325
326	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
327
328	if (!ret)
329		ret = count;
330 out_events:
331	kfree(events);
332 out_page:
333	kfree(buf);
334	return ret;
335}
336
337
338static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf,
339				size_t count, loff_t *ppos)
340{
341	return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
342				  file, userbuf, count, ppos);
343}
344
345
346static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf,
347				 size_t count, loff_t *ppos)
348{
349	return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
350				   file, userbuf, count, ppos);
351}
352
353
354static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf,
355			       size_t count, loff_t *ppos)
356{
357	return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
358				  file, userbuf, count, ppos);
359}
360
361
362static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf,
363				size_t count, loff_t *ppos)
364{
365	return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
366				   file, userbuf, count, ppos);
367}
368
369
370static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf,
371				  size_t count, loff_t *ppos)
372{
373	return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
374				  file, userbuf, count, ppos);
375}
376
377
378static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf,
379				   size_t count, loff_t *ppos)
380{
381	return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
382				   file, userbuf, count, ppos);
383}
384
385
386static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf,
387				 size_t count, loff_t *ppos)
388{
389	return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
390				  file, userbuf, count, ppos);
391}
392
393
394static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf,
395				  size_t count, loff_t *ppos)
396{
397	return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
398				   file, userbuf, count, ppos);
399}
400
401
402static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf,
403				size_t count, loff_t *ppos)
404{
405	return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
406				  file, userbuf, count, ppos);
407}
408
409
410static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf,
411				 size_t count, loff_t *ppos)
412{
413	return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
414				   file, userbuf, count, ppos);
415}
416
417static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf,
418				size_t count, loff_t *ppos)
419{
420	return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
421				  file, userbuf, count, ppos);
422}
423
424
425static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf,
426				 size_t count, loff_t *ppos)
427{
428	return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
429				   file, userbuf, count, ppos);
430}
431
432
433static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
434				  size_t count, loff_t *ppos)
435{
436	struct lbs_private *priv = file->private_data;
437	ssize_t pos = 0;
438	int ret;
439	unsigned long addr = get_zeroed_page(GFP_KERNEL);
440	char *buf = (char *)addr;
441	u32 val = 0;
442
443	if (!buf)
444		return -ENOMEM;
445
446	ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val);
447	mdelay(10);
448	if (!ret) {
449		pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n",
450				priv->mac_offset, val);
451		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
452	}
453	free_page(addr);
454	return ret;
455}
456
457static ssize_t lbs_rdmac_write(struct file *file,
458				    const char __user *userbuf,
459				    size_t count, loff_t *ppos)
460{
461	struct lbs_private *priv = file->private_data;
462	char *buf;
463
464	buf = memdup_user_nul(userbuf, min(count, len - 1));
465	if (IS_ERR(buf))
466		return PTR_ERR(buf);
467
468	priv->mac_offset = simple_strtoul(buf, NULL, 16);
469	kfree(buf);
470	return count;
471}
472
473static ssize_t lbs_wrmac_write(struct file *file,
474				    const char __user *userbuf,
475				    size_t count, loff_t *ppos)
476{
477
478	struct lbs_private *priv = file->private_data;
479	ssize_t res;
480	u32 offset, value;
481	char *buf;
482
483	buf = memdup_user_nul(userbuf, min(count, len - 1));
484	if (IS_ERR(buf))
485		return PTR_ERR(buf);
486
487	res = sscanf(buf, "%x %x", &offset, &value);
488	if (res != 2) {
489		res = -EFAULT;
490		goto out_unlock;
491	}
492
493	res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value);
494	mdelay(10);
495
496	if (!res)
497		res = count;
498out_unlock:
499	kfree(buf);
500	return res;
501}
502
503static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
504				  size_t count, loff_t *ppos)
505{
506	struct lbs_private *priv = file->private_data;
507	ssize_t pos = 0;
508	int ret;
509	unsigned long addr = get_zeroed_page(GFP_KERNEL);
510	char *buf = (char *)addr;
511	u32 val;
512
513	if (!buf)
514		return -ENOMEM;
515
516	ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val);
517	mdelay(10);
518	if (!ret) {
519		pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n",
520				priv->bbp_offset, val);
521		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
522	}
523	free_page(addr);
524
525	return ret;
526}
527
528static ssize_t lbs_rdbbp_write(struct file *file,
529				    const char __user *userbuf,
530				    size_t count, loff_t *ppos)
531{
532	struct lbs_private *priv = file->private_data;
533	char *buf;
534
535	buf = memdup_user_nul(userbuf, min(count, len - 1));
536	if (IS_ERR(buf))
537		return PTR_ERR(buf);
538
539	priv->bbp_offset = simple_strtoul(buf, NULL, 16);
540	kfree(buf);
541
542	return count;
543}
544
545static ssize_t lbs_wrbbp_write(struct file *file,
546				    const char __user *userbuf,
547				    size_t count, loff_t *ppos)
548{
549
550	struct lbs_private *priv = file->private_data;
551	ssize_t res;
552	u32 offset, value;
553	char *buf;
554
555	buf = memdup_user_nul(userbuf, min(count, len - 1));
556	if (IS_ERR(buf))
557		return PTR_ERR(buf);
558
559	res = sscanf(buf, "%x %x", &offset, &value);
560	if (res != 2) {
561		res = -EFAULT;
562		goto out_unlock;
563	}
564
565	res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value);
566	mdelay(10);
567
568	if (!res)
569		res = count;
570out_unlock:
571	kfree(buf);
572	return res;
573}
574
575static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
576				  size_t count, loff_t *ppos)
577{
578	struct lbs_private *priv = file->private_data;
579	ssize_t pos = 0;
580	int ret;
581	unsigned long addr = get_zeroed_page(GFP_KERNEL);
582	char *buf = (char *)addr;
583	u32 val;
584
585	if (!buf)
586		return -ENOMEM;
587
588	ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val);
589	mdelay(10);
590	if (!ret) {
591		pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n",
592				priv->rf_offset, val);
593		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
594	}
595	free_page(addr);
596
597	return ret;
598}
599
600static ssize_t lbs_rdrf_write(struct file *file,
601				    const char __user *userbuf,
602				    size_t count, loff_t *ppos)
603{
604	struct lbs_private *priv = file->private_data;
605	char *buf;
606
607	buf = memdup_user_nul(userbuf, min(count, len - 1));
608	if (IS_ERR(buf))
609		return PTR_ERR(buf);
610
611	priv->rf_offset = simple_strtoul(buf, NULL, 16);
612	kfree(buf);
613	return count;
614}
615
616static ssize_t lbs_wrrf_write(struct file *file,
617				    const char __user *userbuf,
618				    size_t count, loff_t *ppos)
619{
620
621	struct lbs_private *priv = file->private_data;
622	ssize_t res;
623	u32 offset, value;
624	char *buf;
625
626	buf = memdup_user_nul(userbuf, min(count, len - 1));
627	if (IS_ERR(buf))
628		return PTR_ERR(buf);
629
630	res = sscanf(buf, "%x %x", &offset, &value);
631	if (res != 2) {
632		res = -EFAULT;
633		goto out_unlock;
634	}
635
636	res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value);
637	mdelay(10);
638
639	if (!res)
640		res = count;
641out_unlock:
642	kfree(buf);
643	return res;
644}
645
646#define FOPS(fread, fwrite) { \
647	.owner = THIS_MODULE, \
648	.open = simple_open, \
649	.read = (fread), \
650	.write = (fwrite), \
651	.llseek = generic_file_llseek, \
652}
653
654struct lbs_debugfs_files {
655	const char *name;
656	umode_t perm;
657	struct file_operations fops;
658};
659
660static const struct lbs_debugfs_files debugfs_files[] = {
661	{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
662	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
663				lbs_sleepparams_write), },
664	{ "hostsleep", 0644, FOPS(lbs_host_sleep_read,
665				lbs_host_sleep_write), },
666};
667
668static const struct lbs_debugfs_files debugfs_events_files[] = {
669	{"low_rssi", 0644, FOPS(lbs_lowrssi_read,
670				lbs_lowrssi_write), },
671	{"low_snr", 0644, FOPS(lbs_lowsnr_read,
672				lbs_lowsnr_write), },
673	{"failure_count", 0644, FOPS(lbs_failcount_read,
674				lbs_failcount_write), },
675	{"beacon_missed", 0644, FOPS(lbs_bcnmiss_read,
676				lbs_bcnmiss_write), },
677	{"high_rssi", 0644, FOPS(lbs_highrssi_read,
678				lbs_highrssi_write), },
679	{"high_snr", 0644, FOPS(lbs_highsnr_read,
680				lbs_highsnr_write), },
681};
682
683static const struct lbs_debugfs_files debugfs_regs_files[] = {
684	{"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), },
685	{"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), },
686	{"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), },
687	{"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), },
688	{"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), },
689	{"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), },
690};
691
692void lbs_debugfs_init(void)
693{
694	if (!lbs_dir)
695		lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
696}
697
698void lbs_debugfs_remove(void)
699{
700	debugfs_remove(lbs_dir);
701}
702
703void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
704{
705	int i;
706	const struct lbs_debugfs_files *files;
707	if (!lbs_dir)
708		goto exit;
709
710	priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir);
711
712	for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
713		files = &debugfs_files[i];
714		priv->debugfs_files[i] = debugfs_create_file(files->name,
715							     files->perm,
716							     priv->debugfs_dir,
717							     priv,
718							     &files->fops);
719	}
720
721	priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
722
723	for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
724		files = &debugfs_events_files[i];
725		priv->debugfs_events_files[i] = debugfs_create_file(files->name,
726							     files->perm,
727							     priv->events_dir,
728							     priv,
729							     &files->fops);
730	}
731
732	priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
733
734	for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
735		files = &debugfs_regs_files[i];
736		priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
737							     files->perm,
738							     priv->regs_dir,
739							     priv,
740							     &files->fops);
741	}
742
743#ifdef PROC_DEBUG
744	lbs_debug_init(priv);
745#endif
746exit:
747	return;
748}
749
750void lbs_debugfs_remove_one(struct lbs_private *priv)
751{
752	int i;
753
754	for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
755		debugfs_remove(priv->debugfs_regs_files[i]);
756
757	debugfs_remove(priv->regs_dir);
758
759	for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++)
760		debugfs_remove(priv->debugfs_events_files[i]);
761
762	debugfs_remove(priv->events_dir);
763#ifdef PROC_DEBUG
764	debugfs_remove(priv->debugfs_debug);
765#endif
766	for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
767		debugfs_remove(priv->debugfs_files[i]);
768	debugfs_remove(priv->debugfs_dir);
769}
770
771
772
773/* debug entry */
774
775#ifdef PROC_DEBUG
776
777#define item_size(n)	(sizeof_field(struct lbs_private, n))
778#define item_addr(n)	(offsetof(struct lbs_private, n))
779
780
781struct debug_data {
782	char name[32];
783	u32 size;
784	size_t addr;
785};
786
787/* To debug any member of struct lbs_private, simply add one line here.
788 */
789static struct debug_data items[] = {
790	{"psmode", item_size(psmode), item_addr(psmode)},
791	{"psstate", item_size(psstate), item_addr(psstate)},
792};
793
794static int num_of_items = ARRAY_SIZE(items);
795
796/**
797 * lbs_debugfs_read - proc read function
798 *
799 * @file:	file to read
800 * @userbuf:	pointer to buffer
801 * @count:	number of bytes to read
802 * @ppos:	read data starting position
803 *
804 * returns:	amount of data read or negative error code
805 */
806static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
807			size_t count, loff_t *ppos)
808{
809	int val = 0;
810	size_t pos = 0;
811	ssize_t res;
812	char *p;
813	int i;
814	struct debug_data *d;
815	unsigned long addr = get_zeroed_page(GFP_KERNEL);
816	char *buf = (char *)addr;
817	if (!buf)
818		return -ENOMEM;
819
820	p = buf;
821
822	d = file->private_data;
823
824	for (i = 0; i < num_of_items; i++) {
825		if (d[i].size == 1)
826			val = *((u8 *) d[i].addr);
827		else if (d[i].size == 2)
828			val = *((u16 *) d[i].addr);
829		else if (d[i].size == 4)
830			val = *((u32 *) d[i].addr);
831		else if (d[i].size == 8)
832			val = *((u64 *) d[i].addr);
833
834		pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
835	}
836
837	res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
838
839	free_page(addr);
840	return res;
841}
842
843/**
844 * lbs_debugfs_write - proc write function
845 *
846 * @f:		file pointer
847 * @buf:	pointer to data buffer
848 * @cnt:	data number to write
849 * @ppos:	file position
850 *
851 * returns:	amount of data written
852 */
853static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
854			    size_t cnt, loff_t *ppos)
855{
856	int r, i;
857	char *pdata;
858	char *p;
859	char *p0;
860	char *p1;
861	char *p2;
862	struct debug_data *d = f->private_data;
863
864	if (cnt == 0)
865		return 0;
866
867	pdata = memdup_user_nul(buf, cnt);
868	if (IS_ERR(pdata))
869		return PTR_ERR(pdata);
870
871	p0 = pdata;
872	for (i = 0; i < num_of_items; i++) {
873		do {
874			p = strstr(p0, d[i].name);
875			if (p == NULL)
876				break;
877			p1 = strchr(p, '\n');
878			if (p1 == NULL)
879				break;
880			p0 = p1++;
881			p2 = strchr(p, '=');
882			if (!p2)
883				break;
884			p2++;
885			r = simple_strtoul(p2, NULL, 0);
886			if (d[i].size == 1)
887				*((u8 *) d[i].addr) = (u8) r;
888			else if (d[i].size == 2)
889				*((u16 *) d[i].addr) = (u16) r;
890			else if (d[i].size == 4)
891				*((u32 *) d[i].addr) = (u32) r;
892			else if (d[i].size == 8)
893				*((u64 *) d[i].addr) = (u64) r;
894			break;
895		} while (1);
896	}
897	kfree(pdata);
898
899	return (ssize_t)cnt;
900}
901
902static const struct file_operations lbs_debug_fops = {
903	.owner = THIS_MODULE,
904	.open = simple_open,
905	.write = lbs_debugfs_write,
906	.read = lbs_debugfs_read,
907	.llseek = default_llseek,
908};
909
910/**
911 * lbs_debug_init - create debug proc file
912 *
913 * @priv:	pointer to &struct lbs_private
914 *
915 * returns:	N/A
916 */
917static void lbs_debug_init(struct lbs_private *priv)
918{
919	int i;
920
921	if (!priv->debugfs_dir)
922		return;
923
924	for (i = 0; i < num_of_items; i++)
925		items[i].addr += (size_t) priv;
926
927	priv->debugfs_debug = debugfs_create_file("debug", 0644,
928						  priv->debugfs_dir, &items[0],
929						  &lbs_debug_fops);
930}
931#endif
932