xref: /linux/drivers/net/wireless/marvell/libertas/debugfs.c (revision 1f2367a39f17bd553a75e179a747f9b257bc9478)
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 
15 static struct dentry *lbs_dir;
16 static char *szStates[] = {
17 	"Connected",
18 	"Disconnected"
19 };
20 
21 #ifdef PROC_DEBUG
22 static void lbs_debug_init(struct lbs_private *priv);
23 #endif
24 
25 static 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 
31 static const size_t len = PAGE_SIZE;
32 
33 static 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 
55 static 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 
87 out_unlock:
88 	kfree(buf);
89 	return ret;
90 }
91 
92 static 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 
115 out_unlock:
116 	free_page(addr);
117 	return ret;
118 }
119 
120 static 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 
157 out_unlock:
158 	kfree(buf);
159 	return ret;
160 }
161 
162 static 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  */
197 static 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 
217 static 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 
269 static 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 
338 static 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 
346 static 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 
354 static 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 
362 static 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 
370 static 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 
378 static 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 
386 static 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 
394 static 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 
402 static 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 
410 static 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 
417 static 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 
425 static 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 
433 static 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 
457 static 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 
473 static 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;
498 out_unlock:
499 	kfree(buf);
500 	return res;
501 }
502 
503 static 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 
528 static 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 
545 static 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;
570 out_unlock:
571 	kfree(buf);
572 	return res;
573 }
574 
575 static 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 
600 static 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 
616 static 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;
641 out_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 
654 struct lbs_debugfs_files {
655 	const char *name;
656 	umode_t perm;
657 	struct file_operations fops;
658 };
659 
660 static 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 
668 static 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 
683 static 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 
692 void lbs_debugfs_init(void)
693 {
694 	if (!lbs_dir)
695 		lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
696 }
697 
698 void lbs_debugfs_remove(void)
699 {
700 	debugfs_remove(lbs_dir);
701 }
702 
703 void 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
746 exit:
747 	return;
748 }
749 
750 void 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)	(FIELD_SIZEOF(struct lbs_private, n))
778 #define item_addr(n)	(offsetof(struct lbs_private, n))
779 
780 
781 struct 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  */
789 static struct debug_data items[] = {
790 	{"psmode", item_size(psmode), item_addr(psmode)},
791 	{"psstate", item_size(psstate), item_addr(psstate)},
792 };
793 
794 static 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  */
806 static 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  */
853 static 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 
902 static 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  */
917 static 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