xref: /linux/drivers/net/wireless/marvell/libertas/debugfs.c (revision 28336be568bb473d16ba80db0801276fb4f1bbe5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2876c9d3aSMarcelo Tosatti #include <linux/dcache.h>
3876c9d3aSMarcelo Tosatti #include <linux/debugfs.h>
4876c9d3aSMarcelo Tosatti #include <linux/delay.h>
5a6b7a407SAlexey Dobriyan #include <linux/hardirq.h>
6876c9d3aSMarcelo Tosatti #include <linux/mm.h>
7d8b0fb51SGeert Uytterhoeven #include <linux/string.h>
85a0e3ad6STejun Heo #include <linux/slab.h>
9ee40fa06SPaul Gortmaker #include <linux/export.h>
1046868202SHolger Schurig 
11876c9d3aSMarcelo Tosatti #include "decl.h"
123fbe104cSDavid Woodhouse #include "cmd.h"
13e86dc1caSKiran Divekar #include "debugfs.h"
14876c9d3aSMarcelo Tosatti 
1510078321SHolger Schurig static struct dentry *lbs_dir;
16876c9d3aSMarcelo Tosatti static char *szStates[] = {
17876c9d3aSMarcelo Tosatti 	"Connected",
18876c9d3aSMarcelo Tosatti 	"Disconnected"
19876c9d3aSMarcelo Tosatti };
20876c9d3aSMarcelo Tosatti 
2146868202SHolger Schurig #ifdef PROC_DEBUG
22e98a88ddSHolger Schurig static void lbs_debug_init(struct lbs_private *priv);
2346868202SHolger Schurig #endif
24876c9d3aSMarcelo Tosatti 
write_file_dummy(struct file * file,const char __user * buf,size_t count,loff_t * ppos)25876c9d3aSMarcelo Tosatti static ssize_t write_file_dummy(struct file *file, const char __user *buf,
26876c9d3aSMarcelo Tosatti                                 size_t count, loff_t *ppos)
27876c9d3aSMarcelo Tosatti {
28876c9d3aSMarcelo Tosatti         return -EINVAL;
29876c9d3aSMarcelo Tosatti }
30876c9d3aSMarcelo Tosatti 
31876c9d3aSMarcelo Tosatti static const size_t len = PAGE_SIZE;
32876c9d3aSMarcelo Tosatti 
lbs_dev_info(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)3310078321SHolger Schurig static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
34876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
35876c9d3aSMarcelo Tosatti {
3669f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
37876c9d3aSMarcelo Tosatti 	size_t pos = 0;
38876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
39876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
40876c9d3aSMarcelo Tosatti 	ssize_t res;
41ad43f8bfSKiran Divekar 	if (!buf)
42ad43f8bfSKiran Divekar 		return -ENOMEM;
43876c9d3aSMarcelo Tosatti 
44876c9d3aSMarcelo Tosatti 	pos += snprintf(buf+pos, len-pos, "state = %s\n",
45aa21c004SDavid Woodhouse 				szStates[priv->connect_status]);
46876c9d3aSMarcelo Tosatti 	pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
47aa21c004SDavid Woodhouse 				(u32) priv->regioncode);
48876c9d3aSMarcelo Tosatti 
49876c9d3aSMarcelo Tosatti 	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
50876c9d3aSMarcelo Tosatti 
51876c9d3aSMarcelo Tosatti 	free_page(addr);
52876c9d3aSMarcelo Tosatti 	return res;
53876c9d3aSMarcelo Tosatti }
54876c9d3aSMarcelo Tosatti 
lbs_sleepparams_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)5510078321SHolger Schurig static ssize_t lbs_sleepparams_write(struct file *file,
56876c9d3aSMarcelo Tosatti 				const char __user *user_buf, size_t count,
57876c9d3aSMarcelo Tosatti 				loff_t *ppos)
58876c9d3aSMarcelo Tosatti {
5969f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
60f0fc8696SAl Viro 	ssize_t ret;
613fbe104cSDavid Woodhouse 	struct sleep_params sp;
62876c9d3aSMarcelo Tosatti 	int p1, p2, p3, p4, p5, p6;
63f0fc8696SAl Viro 	char *buf;
64876c9d3aSMarcelo Tosatti 
65f0fc8696SAl Viro 	buf = memdup_user_nul(user_buf, min(count, len - 1));
66f0fc8696SAl Viro 	if (IS_ERR(buf))
67f0fc8696SAl Viro 		return PTR_ERR(buf);
68f0fc8696SAl Viro 
693fbe104cSDavid Woodhouse 	ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
703fbe104cSDavid Woodhouse 	if (ret != 6) {
713fbe104cSDavid Woodhouse 		ret = -EINVAL;
72876c9d3aSMarcelo Tosatti 		goto out_unlock;
73876c9d3aSMarcelo Tosatti 	}
743fbe104cSDavid Woodhouse 	sp.sp_error = p1;
753fbe104cSDavid Woodhouse 	sp.sp_offset = p2;
763fbe104cSDavid Woodhouse 	sp.sp_stabletime = p3;
773fbe104cSDavid Woodhouse 	sp.sp_calcontrol = p4;
783fbe104cSDavid Woodhouse 	sp.sp_extsleepclk = p5;
793fbe104cSDavid Woodhouse 	sp.sp_reserved = p6;
80876c9d3aSMarcelo Tosatti 
813fbe104cSDavid Woodhouse 	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp);
823fbe104cSDavid Woodhouse 	if (!ret)
833fbe104cSDavid Woodhouse 		ret = count;
843fbe104cSDavid Woodhouse 	else if (ret > 0)
853fbe104cSDavid Woodhouse 		ret = -EINVAL;
86876c9d3aSMarcelo Tosatti 
87876c9d3aSMarcelo Tosatti out_unlock:
88f0fc8696SAl Viro 	kfree(buf);
893fbe104cSDavid Woodhouse 	return ret;
90876c9d3aSMarcelo Tosatti }
91876c9d3aSMarcelo Tosatti 
lbs_sleepparams_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)9210078321SHolger Schurig static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
93876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
94876c9d3aSMarcelo Tosatti {
9569f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
963fbe104cSDavid Woodhouse 	ssize_t ret;
97876c9d3aSMarcelo Tosatti 	size_t pos = 0;
983fbe104cSDavid Woodhouse 	struct sleep_params sp;
99876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
100876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
101ad43f8bfSKiran Divekar 	if (!buf)
102ad43f8bfSKiran Divekar 		return -ENOMEM;
103876c9d3aSMarcelo Tosatti 
1043fbe104cSDavid Woodhouse 	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
1053fbe104cSDavid Woodhouse 	if (ret)
106876c9d3aSMarcelo Tosatti 		goto out_unlock;
107876c9d3aSMarcelo Tosatti 
1083fbe104cSDavid Woodhouse 	pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error,
1093fbe104cSDavid Woodhouse 			sp.sp_offset, sp.sp_stabletime,
1103fbe104cSDavid Woodhouse 			sp.sp_calcontrol, sp.sp_extsleepclk,
1113fbe104cSDavid Woodhouse 			sp.sp_reserved);
112876c9d3aSMarcelo Tosatti 
1133fbe104cSDavid Woodhouse 	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
114876c9d3aSMarcelo Tosatti 
115876c9d3aSMarcelo Tosatti out_unlock:
116876c9d3aSMarcelo Tosatti 	free_page(addr);
1173fbe104cSDavid Woodhouse 	return ret;
118876c9d3aSMarcelo Tosatti }
119876c9d3aSMarcelo Tosatti 
lbs_host_sleep_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1201311843cSAmitkumar Karwar static ssize_t lbs_host_sleep_write(struct file *file,
1211311843cSAmitkumar Karwar 				const char __user *user_buf, size_t count,
1221311843cSAmitkumar Karwar 				loff_t *ppos)
1231311843cSAmitkumar Karwar {
1241311843cSAmitkumar Karwar 	struct lbs_private *priv = file->private_data;
125f0fc8696SAl Viro 	ssize_t ret;
1261311843cSAmitkumar Karwar 	int host_sleep;
127f0fc8696SAl Viro 	char *buf;
1281311843cSAmitkumar Karwar 
129f0fc8696SAl Viro 	buf = memdup_user_nul(user_buf, min(count, len - 1));
130f0fc8696SAl Viro 	if (IS_ERR(buf))
131f0fc8696SAl Viro 		return PTR_ERR(buf);
132f0fc8696SAl Viro 
1331311843cSAmitkumar Karwar 	ret = sscanf(buf, "%d", &host_sleep);
1341311843cSAmitkumar Karwar 	if (ret != 1) {
1351311843cSAmitkumar Karwar 		ret = -EINVAL;
1361311843cSAmitkumar Karwar 		goto out_unlock;
1371311843cSAmitkumar Karwar 	}
1381311843cSAmitkumar Karwar 
1391311843cSAmitkumar Karwar 	if (host_sleep == 0)
1401311843cSAmitkumar Karwar 		ret = lbs_set_host_sleep(priv, 0);
1411311843cSAmitkumar Karwar 	else if (host_sleep == 1) {
1421311843cSAmitkumar Karwar 		if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
143f3a57fd1SJoe Perches 			netdev_info(priv->dev,
144f3a57fd1SJoe Perches 				    "wake parameters not configured\n");
1451311843cSAmitkumar Karwar 			ret = -EINVAL;
1461311843cSAmitkumar Karwar 			goto out_unlock;
1471311843cSAmitkumar Karwar 		}
1481311843cSAmitkumar Karwar 		ret = lbs_set_host_sleep(priv, 1);
1491311843cSAmitkumar Karwar 	} else {
150f3a57fd1SJoe Perches 		netdev_err(priv->dev, "invalid option\n");
1511311843cSAmitkumar Karwar 		ret = -EINVAL;
1521311843cSAmitkumar Karwar 	}
1531311843cSAmitkumar Karwar 
1541311843cSAmitkumar Karwar 	if (!ret)
1551311843cSAmitkumar Karwar 		ret = count;
1561311843cSAmitkumar Karwar 
1571311843cSAmitkumar Karwar out_unlock:
158f0fc8696SAl Viro 	kfree(buf);
1591311843cSAmitkumar Karwar 	return ret;
1601311843cSAmitkumar Karwar }
1611311843cSAmitkumar Karwar 
lbs_host_sleep_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)1621311843cSAmitkumar Karwar static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
1631311843cSAmitkumar Karwar 				  size_t count, loff_t *ppos)
1641311843cSAmitkumar Karwar {
1651311843cSAmitkumar Karwar 	struct lbs_private *priv = file->private_data;
1661311843cSAmitkumar Karwar 	ssize_t ret;
1671311843cSAmitkumar Karwar 	size_t pos = 0;
1681311843cSAmitkumar Karwar 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
1691311843cSAmitkumar Karwar 	char *buf = (char *)addr;
1701311843cSAmitkumar Karwar 	if (!buf)
1711311843cSAmitkumar Karwar 		return -ENOMEM;
1721311843cSAmitkumar Karwar 
1731311843cSAmitkumar Karwar 	pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);
1741311843cSAmitkumar Karwar 
1751311843cSAmitkumar Karwar 	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
1761311843cSAmitkumar Karwar 
1771311843cSAmitkumar Karwar 	free_page(addr);
1781311843cSAmitkumar Karwar 	return ret;
1791311843cSAmitkumar Karwar }
1801311843cSAmitkumar Karwar 
1813a188649SHolger Schurig /*
1823a188649SHolger Schurig  * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
1833a188649SHolger Schurig  * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
1843a188649SHolger Schurig  * firmware. Here's an example:
1853a188649SHolger Schurig  *	04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00
1863a188649SHolger Schurig  *	00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03
1873a188649SHolger Schurig  *	00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1883a188649SHolger Schurig  *
1893a188649SHolger Schurig  * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length,
1903a188649SHolger Schurig  * 00 00 are the data bytes of this TLV. For this TLV, their meaning is
1913a188649SHolger Schurig  * defined in mrvlietypes_thresholds
1923a188649SHolger Schurig  *
1933a188649SHolger Schurig  * This function searches in this TLV data chunk for a given TLV type
1943a188649SHolger Schurig  * and returns a pointer to the first data byte of the TLV, or to NULL
1953a188649SHolger Schurig  * if the TLV hasn't been found.
1963a188649SHolger Schurig  */
lbs_tlv_find(uint16_t tlv_type,const uint8_t * tlv,uint16_t size)1975844d12eSDavid Woodhouse static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size)
198876c9d3aSMarcelo Tosatti {
19975b6a61aSDan Williams 	struct mrvl_ie_header *tlv_h;
2005844d12eSDavid Woodhouse 	uint16_t length;
2015844d12eSDavid Woodhouse 	ssize_t pos = 0;
2025844d12eSDavid Woodhouse 
2033a188649SHolger Schurig 	while (pos < size) {
20475b6a61aSDan Williams 		tlv_h = (struct mrvl_ie_header *) tlv;
2055844d12eSDavid Woodhouse 		if (!tlv_h->len)
2063a188649SHolger Schurig 			return NULL;
2075844d12eSDavid Woodhouse 		if (tlv_h->type == cpu_to_le16(tlv_type))
2085844d12eSDavid Woodhouse 			return tlv_h;
2095844d12eSDavid Woodhouse 		length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h);
2103a188649SHolger Schurig 		pos += length;
2113a188649SHolger Schurig 		tlv += length;
2123a188649SHolger Schurig 	}
2133a188649SHolger Schurig 	return NULL;
214876c9d3aSMarcelo Tosatti }
215876c9d3aSMarcelo Tosatti 
216876c9d3aSMarcelo Tosatti 
lbs_threshold_read(uint16_t tlv_type,uint16_t event_mask,struct file * file,char __user * userbuf,size_t count,loff_t * ppos)2175844d12eSDavid Woodhouse static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
2183a188649SHolger Schurig 				  struct file *file, char __user *userbuf,
219876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
220876c9d3aSMarcelo Tosatti {
2215844d12eSDavid Woodhouse 	struct cmd_ds_802_11_subscribe_event *subscribed;
22275b6a61aSDan Williams 	struct mrvl_ie_thresholds *got;
22369f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
2245844d12eSDavid Woodhouse 	ssize_t ret = 0;
2253a188649SHolger Schurig 	size_t pos = 0;
2265844d12eSDavid Woodhouse 	char *buf;
2273a188649SHolger Schurig 	u8 value;
2283a188649SHolger Schurig 	u8 freq;
229b6b8abe4SHolger Schurig 	int events = 0;
230876c9d3aSMarcelo Tosatti 
2315844d12eSDavid Woodhouse 	buf = (char *)get_zeroed_page(GFP_KERNEL);
2325844d12eSDavid Woodhouse 	if (!buf)
2335844d12eSDavid Woodhouse 		return -ENOMEM;
234876c9d3aSMarcelo Tosatti 
2355844d12eSDavid Woodhouse 	subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL);
2365844d12eSDavid Woodhouse 	if (!subscribed) {
2375844d12eSDavid Woodhouse 		ret = -ENOMEM;
2385844d12eSDavid Woodhouse 		goto out_page;
239876c9d3aSMarcelo Tosatti 	}
240876c9d3aSMarcelo Tosatti 
2415844d12eSDavid Woodhouse 	subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed));
2425844d12eSDavid Woodhouse 	subscribed->action = cpu_to_le16(CMD_ACT_GET);
2435844d12eSDavid Woodhouse 
2445844d12eSDavid Woodhouse 	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed);
2455844d12eSDavid Woodhouse 	if (ret)
2465844d12eSDavid Woodhouse 		goto out_cmd;
2475844d12eSDavid Woodhouse 
248b6b8abe4SHolger Schurig 	got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv));
2493a188649SHolger Schurig 	if (got) {
2503a188649SHolger Schurig 		value = got->value;
2513a188649SHolger Schurig 		freq  = got->freq;
252b6b8abe4SHolger Schurig 		events = le16_to_cpu(subscribed->events);
253876c9d3aSMarcelo Tosatti 
2543a188649SHolger Schurig 		pos += snprintf(buf, len, "%d %d %d\n", value, freq,
255b6b8abe4SHolger Schurig 				!!(events & event_mask));
2565844d12eSDavid Woodhouse 	}
257876c9d3aSMarcelo Tosatti 
2585844d12eSDavid Woodhouse 	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
2593a188649SHolger Schurig 
2605844d12eSDavid Woodhouse  out_cmd:
2615844d12eSDavid Woodhouse 	kfree(subscribed);
2625844d12eSDavid Woodhouse 
2635844d12eSDavid Woodhouse  out_page:
2645844d12eSDavid Woodhouse 	free_page((unsigned long)buf);
2655844d12eSDavid Woodhouse 	return ret;
266876c9d3aSMarcelo Tosatti }
267876c9d3aSMarcelo Tosatti 
2683a188649SHolger Schurig 
lbs_threshold_write(uint16_t tlv_type,uint16_t event_mask,struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)2695844d12eSDavid Woodhouse static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
2703a188649SHolger Schurig 				   struct file *file,
2715844d12eSDavid Woodhouse 				   const char __user *userbuf, size_t count,
2725844d12eSDavid Woodhouse 				   loff_t *ppos)
273876c9d3aSMarcelo Tosatti {
2743a188649SHolger Schurig 	struct cmd_ds_802_11_subscribe_event *events;
27575b6a61aSDan Williams 	struct mrvl_ie_thresholds *tlv;
2765844d12eSDavid Woodhouse 	struct lbs_private *priv = file->private_data;
2775844d12eSDavid Woodhouse 	int value, freq, new_mask;
2785844d12eSDavid Woodhouse 	uint16_t curr_mask;
2795844d12eSDavid Woodhouse 	char *buf;
2805844d12eSDavid Woodhouse 	int ret;
2815844d12eSDavid Woodhouse 
282f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
283f0fc8696SAl Viro 	if (IS_ERR(buf))
284f0fc8696SAl Viro 		return PTR_ERR(buf);
285876c9d3aSMarcelo Tosatti 
2865844d12eSDavid Woodhouse 	ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask);
2875844d12eSDavid Woodhouse 	if (ret != 3) {
2885844d12eSDavid Woodhouse 		ret = -EINVAL;
2895844d12eSDavid Woodhouse 		goto out_page;
290876c9d3aSMarcelo Tosatti 	}
2915844d12eSDavid Woodhouse 	events = kzalloc(sizeof(*events), GFP_KERNEL);
2925844d12eSDavid Woodhouse 	if (!events) {
2935844d12eSDavid Woodhouse 		ret = -ENOMEM;
2945844d12eSDavid Woodhouse 		goto out_page;
2955844d12eSDavid Woodhouse 	}
2965844d12eSDavid Woodhouse 
2975844d12eSDavid Woodhouse 	events->hdr.size = cpu_to_le16(sizeof(*events));
2985844d12eSDavid Woodhouse 	events->action = cpu_to_le16(CMD_ACT_GET);
2995844d12eSDavid Woodhouse 
3005844d12eSDavid Woodhouse 	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
3015844d12eSDavid Woodhouse 	if (ret)
3025844d12eSDavid Woodhouse 		goto out_events;
3035844d12eSDavid Woodhouse 
3045844d12eSDavid Woodhouse 	curr_mask = le16_to_cpu(events->events);
305876c9d3aSMarcelo Tosatti 
3063a188649SHolger Schurig 	if (new_mask)
3073a188649SHolger Schurig 		new_mask = curr_mask | event_mask;
3083a188649SHolger Schurig 	else
3093a188649SHolger Schurig 		new_mask = curr_mask & ~event_mask;
310876c9d3aSMarcelo Tosatti 
3113a188649SHolger Schurig 	/* Now everything is set and we can send stuff down to the firmware */
3125844d12eSDavid Woodhouse 
3135844d12eSDavid Woodhouse 	tlv = (void *)events->tlv;
3145844d12eSDavid Woodhouse 
3153a188649SHolger Schurig 	events->action = cpu_to_le16(CMD_ACT_SET);
3163a188649SHolger Schurig 	events->events = cpu_to_le16(new_mask);
3173a188649SHolger Schurig 	tlv->header.type = cpu_to_le16(tlv_type);
3185844d12eSDavid Woodhouse 	tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header));
3193a188649SHolger Schurig 	tlv->value = value;
3203a188649SHolger Schurig 	if (tlv_type != TLV_TYPE_BCNMISS)
3213a188649SHolger Schurig 		tlv->freq = freq;
3225844d12eSDavid Woodhouse 
323a75eda43SHolger Schurig 	/* The command header, the action, the event mask, and one TLV */
324a75eda43SHolger Schurig 	events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv));
3255844d12eSDavid Woodhouse 
3265844d12eSDavid Woodhouse 	ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
3275844d12eSDavid Woodhouse 
3285844d12eSDavid Woodhouse 	if (!ret)
3295844d12eSDavid Woodhouse 		ret = count;
3305844d12eSDavid Woodhouse  out_events:
3313a188649SHolger Schurig 	kfree(events);
3325844d12eSDavid Woodhouse  out_page:
333f0fc8696SAl Viro 	kfree(buf);
3345844d12eSDavid Woodhouse 	return ret;
335876c9d3aSMarcelo Tosatti }
336876c9d3aSMarcelo Tosatti 
3373a188649SHolger Schurig 
lbs_lowrssi_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)3385844d12eSDavid Woodhouse static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf,
339876c9d3aSMarcelo Tosatti 				size_t count, loff_t *ppos)
340876c9d3aSMarcelo Tosatti {
3413a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
3423a188649SHolger Schurig 				  file, userbuf, count, ppos);
343876c9d3aSMarcelo Tosatti }
344876c9d3aSMarcelo Tosatti 
345876c9d3aSMarcelo Tosatti 
lbs_lowrssi_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)3465844d12eSDavid Woodhouse static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf,
347876c9d3aSMarcelo Tosatti 				 size_t count, loff_t *ppos)
348876c9d3aSMarcelo Tosatti {
3493a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
3503a188649SHolger Schurig 				   file, userbuf, count, ppos);
351876c9d3aSMarcelo Tosatti }
352876c9d3aSMarcelo Tosatti 
353876c9d3aSMarcelo Tosatti 
lbs_lowsnr_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)3545844d12eSDavid Woodhouse static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf,
355876c9d3aSMarcelo Tosatti 			       size_t count, loff_t *ppos)
356876c9d3aSMarcelo Tosatti {
3573a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
3583a188649SHolger Schurig 				  file, userbuf, count, ppos);
359876c9d3aSMarcelo Tosatti }
360876c9d3aSMarcelo Tosatti 
361876c9d3aSMarcelo Tosatti 
lbs_lowsnr_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)3625844d12eSDavid Woodhouse static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf,
363876c9d3aSMarcelo Tosatti 				size_t count, loff_t *ppos)
364876c9d3aSMarcelo Tosatti {
3653a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
3663a188649SHolger Schurig 				   file, userbuf, count, ppos);
367876c9d3aSMarcelo Tosatti }
368876c9d3aSMarcelo Tosatti 
369876c9d3aSMarcelo Tosatti 
lbs_failcount_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)3705844d12eSDavid Woodhouse static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf,
371876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
372876c9d3aSMarcelo Tosatti {
3733a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
3743a188649SHolger Schurig 				  file, userbuf, count, ppos);
375876c9d3aSMarcelo Tosatti }
376876c9d3aSMarcelo Tosatti 
377876c9d3aSMarcelo Tosatti 
lbs_failcount_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)3785844d12eSDavid Woodhouse static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf,
379876c9d3aSMarcelo Tosatti 				   size_t count, loff_t *ppos)
380876c9d3aSMarcelo Tosatti {
3813a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
3823a188649SHolger Schurig 				   file, userbuf, count, ppos);
383876c9d3aSMarcelo Tosatti }
384876c9d3aSMarcelo Tosatti 
385876c9d3aSMarcelo Tosatti 
lbs_highrssi_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)3865844d12eSDavid Woodhouse static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf,
3873a188649SHolger Schurig 				 size_t count, loff_t *ppos)
3883a188649SHolger Schurig {
3893a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
3903a188649SHolger Schurig 				  file, userbuf, count, ppos);
391876c9d3aSMarcelo Tosatti }
392876c9d3aSMarcelo Tosatti 
3933a188649SHolger Schurig 
lbs_highrssi_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)3945844d12eSDavid Woodhouse static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf,
3953a188649SHolger Schurig 				  size_t count, loff_t *ppos)
3963a188649SHolger Schurig {
3973a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
3983a188649SHolger Schurig 				   file, userbuf, count, ppos);
399876c9d3aSMarcelo Tosatti }
400876c9d3aSMarcelo Tosatti 
4013a188649SHolger Schurig 
lbs_highsnr_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)4025844d12eSDavid Woodhouse static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf,
4033a188649SHolger Schurig 				size_t count, loff_t *ppos)
4043a188649SHolger Schurig {
4053a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
4063a188649SHolger Schurig 				  file, userbuf, count, ppos);
407876c9d3aSMarcelo Tosatti }
408876c9d3aSMarcelo Tosatti 
4093a188649SHolger Schurig 
lbs_highsnr_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)4105844d12eSDavid Woodhouse static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf,
4113a188649SHolger Schurig 				 size_t count, loff_t *ppos)
4123a188649SHolger Schurig {
4133a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
4143a188649SHolger Schurig 				   file, userbuf, count, ppos);
4153a188649SHolger Schurig }
4163a188649SHolger Schurig 
lbs_bcnmiss_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)4175844d12eSDavid Woodhouse static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf,
4183a188649SHolger Schurig 				size_t count, loff_t *ppos)
4193a188649SHolger Schurig {
4203a188649SHolger Schurig 	return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
4213a188649SHolger Schurig 				  file, userbuf, count, ppos);
4223a188649SHolger Schurig }
4233a188649SHolger Schurig 
4243a188649SHolger Schurig 
lbs_bcnmiss_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)4255844d12eSDavid Woodhouse static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf,
4263a188649SHolger Schurig 				 size_t count, loff_t *ppos)
4273a188649SHolger Schurig {
4283a188649SHolger Schurig 	return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
4293a188649SHolger Schurig 				   file, userbuf, count, ppos);
4303a188649SHolger Schurig }
4313a188649SHolger Schurig 
4323a188649SHolger Schurig 
lbs_rdmac_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)43310078321SHolger Schurig static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
434876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
435876c9d3aSMarcelo Tosatti {
43669f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
437876c9d3aSMarcelo Tosatti 	ssize_t pos = 0;
438876c9d3aSMarcelo Tosatti 	int ret;
439876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
440876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
4414c7c6e00SDan Williams 	u32 val = 0;
4424c7c6e00SDan Williams 
443ad43f8bfSKiran Divekar 	if (!buf)
444ad43f8bfSKiran Divekar 		return -ENOMEM;
445876c9d3aSMarcelo Tosatti 
4464c7c6e00SDan Williams 	ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val);
447876c9d3aSMarcelo Tosatti 	mdelay(10);
448c0bbd576SAmitkumar Karwar 	if (!ret) {
4494c7c6e00SDan Williams 		pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n",
4504c7c6e00SDan Williams 				priv->mac_offset, val);
451876c9d3aSMarcelo Tosatti 		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
452c0bbd576SAmitkumar Karwar 	}
453876c9d3aSMarcelo Tosatti 	free_page(addr);
454876c9d3aSMarcelo Tosatti 	return ret;
455876c9d3aSMarcelo Tosatti }
456876c9d3aSMarcelo Tosatti 
lbs_rdmac_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)45710078321SHolger Schurig static ssize_t lbs_rdmac_write(struct file *file,
458876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
459876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
460876c9d3aSMarcelo Tosatti {
46169f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
462f0fc8696SAl Viro 	char *buf;
463876c9d3aSMarcelo Tosatti 
464f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
465f0fc8696SAl Viro 	if (IS_ERR(buf))
466f0fc8696SAl Viro 		return PTR_ERR(buf);
467f0fc8696SAl Viro 
4682c208890SJoe Perches 	priv->mac_offset = simple_strtoul(buf, NULL, 16);
469f0fc8696SAl Viro 	kfree(buf);
470f0fc8696SAl Viro 	return count;
471876c9d3aSMarcelo Tosatti }
472876c9d3aSMarcelo Tosatti 
lbs_wrmac_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)47310078321SHolger Schurig static ssize_t lbs_wrmac_write(struct file *file,
474876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
475876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
476876c9d3aSMarcelo Tosatti {
477876c9d3aSMarcelo Tosatti 
47869f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
479f0fc8696SAl Viro 	ssize_t res;
480876c9d3aSMarcelo Tosatti 	u32 offset, value;
481f0fc8696SAl Viro 	char *buf;
482876c9d3aSMarcelo Tosatti 
483f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
484f0fc8696SAl Viro 	if (IS_ERR(buf))
485f0fc8696SAl Viro 		return PTR_ERR(buf);
486f0fc8696SAl Viro 
487876c9d3aSMarcelo Tosatti 	res = sscanf(buf, "%x %x", &offset, &value);
488876c9d3aSMarcelo Tosatti 	if (res != 2) {
489876c9d3aSMarcelo Tosatti 		res = -EFAULT;
490876c9d3aSMarcelo Tosatti 		goto out_unlock;
491876c9d3aSMarcelo Tosatti 	}
492876c9d3aSMarcelo Tosatti 
4934c7c6e00SDan Williams 	res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value);
494876c9d3aSMarcelo Tosatti 	mdelay(10);
495876c9d3aSMarcelo Tosatti 
496c0bbd576SAmitkumar Karwar 	if (!res)
497876c9d3aSMarcelo Tosatti 		res = count;
498876c9d3aSMarcelo Tosatti out_unlock:
499f0fc8696SAl Viro 	kfree(buf);
500876c9d3aSMarcelo Tosatti 	return res;
501876c9d3aSMarcelo Tosatti }
502876c9d3aSMarcelo Tosatti 
lbs_rdbbp_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)50310078321SHolger Schurig static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
504876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
505876c9d3aSMarcelo Tosatti {
50669f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
507876c9d3aSMarcelo Tosatti 	ssize_t pos = 0;
508876c9d3aSMarcelo Tosatti 	int ret;
509876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
510876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
5114c7c6e00SDan Williams 	u32 val;
5124c7c6e00SDan Williams 
513ad43f8bfSKiran Divekar 	if (!buf)
514ad43f8bfSKiran Divekar 		return -ENOMEM;
515876c9d3aSMarcelo Tosatti 
5164c7c6e00SDan Williams 	ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val);
517876c9d3aSMarcelo Tosatti 	mdelay(10);
518c0bbd576SAmitkumar Karwar 	if (!ret) {
5194c7c6e00SDan Williams 		pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n",
5204c7c6e00SDan Williams 				priv->bbp_offset, val);
521876c9d3aSMarcelo Tosatti 		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
522c0bbd576SAmitkumar Karwar 	}
523876c9d3aSMarcelo Tosatti 	free_page(addr);
524876c9d3aSMarcelo Tosatti 
525876c9d3aSMarcelo Tosatti 	return ret;
526876c9d3aSMarcelo Tosatti }
527876c9d3aSMarcelo Tosatti 
lbs_rdbbp_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)52810078321SHolger Schurig static ssize_t lbs_rdbbp_write(struct file *file,
529876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
530876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
531876c9d3aSMarcelo Tosatti {
53269f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
533f0fc8696SAl Viro 	char *buf;
534876c9d3aSMarcelo Tosatti 
535f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
536f0fc8696SAl Viro 	if (IS_ERR(buf))
537f0fc8696SAl Viro 		return PTR_ERR(buf);
538f0fc8696SAl Viro 
5392c208890SJoe Perches 	priv->bbp_offset = simple_strtoul(buf, NULL, 16);
540f0fc8696SAl Viro 	kfree(buf);
541f0fc8696SAl Viro 
542f0fc8696SAl Viro 	return count;
543876c9d3aSMarcelo Tosatti }
544876c9d3aSMarcelo Tosatti 
lbs_wrbbp_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)54510078321SHolger Schurig static ssize_t lbs_wrbbp_write(struct file *file,
546876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
547876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
548876c9d3aSMarcelo Tosatti {
549876c9d3aSMarcelo Tosatti 
55069f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
551f0fc8696SAl Viro 	ssize_t res;
552876c9d3aSMarcelo Tosatti 	u32 offset, value;
553f0fc8696SAl Viro 	char *buf;
554876c9d3aSMarcelo Tosatti 
555f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
556f0fc8696SAl Viro 	if (IS_ERR(buf))
557f0fc8696SAl Viro 		return PTR_ERR(buf);
558f0fc8696SAl Viro 
559876c9d3aSMarcelo Tosatti 	res = sscanf(buf, "%x %x", &offset, &value);
560876c9d3aSMarcelo Tosatti 	if (res != 2) {
561876c9d3aSMarcelo Tosatti 		res = -EFAULT;
562876c9d3aSMarcelo Tosatti 		goto out_unlock;
563876c9d3aSMarcelo Tosatti 	}
564876c9d3aSMarcelo Tosatti 
5654c7c6e00SDan Williams 	res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value);
566876c9d3aSMarcelo Tosatti 	mdelay(10);
567876c9d3aSMarcelo Tosatti 
568c0bbd576SAmitkumar Karwar 	if (!res)
569876c9d3aSMarcelo Tosatti 		res = count;
570876c9d3aSMarcelo Tosatti out_unlock:
571f0fc8696SAl Viro 	kfree(buf);
572876c9d3aSMarcelo Tosatti 	return res;
573876c9d3aSMarcelo Tosatti }
574876c9d3aSMarcelo Tosatti 
lbs_rdrf_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)57510078321SHolger Schurig static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
576876c9d3aSMarcelo Tosatti 				  size_t count, loff_t *ppos)
577876c9d3aSMarcelo Tosatti {
57869f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
579876c9d3aSMarcelo Tosatti 	ssize_t pos = 0;
580876c9d3aSMarcelo Tosatti 	int ret;
581876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
582876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
5834c7c6e00SDan Williams 	u32 val;
5844c7c6e00SDan Williams 
585ad43f8bfSKiran Divekar 	if (!buf)
586ad43f8bfSKiran Divekar 		return -ENOMEM;
587876c9d3aSMarcelo Tosatti 
5884c7c6e00SDan Williams 	ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val);
589876c9d3aSMarcelo Tosatti 	mdelay(10);
590c0bbd576SAmitkumar Karwar 	if (!ret) {
5914c7c6e00SDan Williams 		pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n",
5924c7c6e00SDan Williams 				priv->rf_offset, val);
593876c9d3aSMarcelo Tosatti 		ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
594c0bbd576SAmitkumar Karwar 	}
595876c9d3aSMarcelo Tosatti 	free_page(addr);
596876c9d3aSMarcelo Tosatti 
597876c9d3aSMarcelo Tosatti 	return ret;
598876c9d3aSMarcelo Tosatti }
599876c9d3aSMarcelo Tosatti 
lbs_rdrf_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)60010078321SHolger Schurig static ssize_t lbs_rdrf_write(struct file *file,
601876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
602876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
603876c9d3aSMarcelo Tosatti {
60469f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
605f0fc8696SAl Viro 	char *buf;
606876c9d3aSMarcelo Tosatti 
607f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
608f0fc8696SAl Viro 	if (IS_ERR(buf))
609f0fc8696SAl Viro 		return PTR_ERR(buf);
610f0fc8696SAl Viro 
6114101dec9SJan Engelhardt 	priv->rf_offset = simple_strtoul(buf, NULL, 16);
612f0fc8696SAl Viro 	kfree(buf);
613f0fc8696SAl Viro 	return count;
614876c9d3aSMarcelo Tosatti }
615876c9d3aSMarcelo Tosatti 
lbs_wrrf_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)61610078321SHolger Schurig static ssize_t lbs_wrrf_write(struct file *file,
617876c9d3aSMarcelo Tosatti 				    const char __user *userbuf,
618876c9d3aSMarcelo Tosatti 				    size_t count, loff_t *ppos)
619876c9d3aSMarcelo Tosatti {
620876c9d3aSMarcelo Tosatti 
62169f9032dSHolger Schurig 	struct lbs_private *priv = file->private_data;
622f0fc8696SAl Viro 	ssize_t res;
623876c9d3aSMarcelo Tosatti 	u32 offset, value;
624f0fc8696SAl Viro 	char *buf;
625876c9d3aSMarcelo Tosatti 
626f0fc8696SAl Viro 	buf = memdup_user_nul(userbuf, min(count, len - 1));
627f0fc8696SAl Viro 	if (IS_ERR(buf))
628f0fc8696SAl Viro 		return PTR_ERR(buf);
629f0fc8696SAl Viro 
630876c9d3aSMarcelo Tosatti 	res = sscanf(buf, "%x %x", &offset, &value);
631876c9d3aSMarcelo Tosatti 	if (res != 2) {
632876c9d3aSMarcelo Tosatti 		res = -EFAULT;
633876c9d3aSMarcelo Tosatti 		goto out_unlock;
634876c9d3aSMarcelo Tosatti 	}
635876c9d3aSMarcelo Tosatti 
6364c7c6e00SDan Williams 	res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value);
637876c9d3aSMarcelo Tosatti 	mdelay(10);
638876c9d3aSMarcelo Tosatti 
639c0bbd576SAmitkumar Karwar 	if (!res)
640876c9d3aSMarcelo Tosatti 		res = count;
641876c9d3aSMarcelo Tosatti out_unlock:
642f0fc8696SAl Viro 	kfree(buf);
643876c9d3aSMarcelo Tosatti 	return res;
644876c9d3aSMarcelo Tosatti }
645876c9d3aSMarcelo Tosatti 
646876c9d3aSMarcelo Tosatti #define FOPS(fread, fwrite) { \
647876c9d3aSMarcelo Tosatti 	.owner = THIS_MODULE, \
648234e3405SStephen Boyd 	.open = simple_open, \
649876c9d3aSMarcelo Tosatti 	.read = (fread), \
650876c9d3aSMarcelo Tosatti 	.write = (fwrite), \
6512b18ab36SArnd Bergmann 	.llseek = generic_file_llseek, \
652876c9d3aSMarcelo Tosatti }
653876c9d3aSMarcelo Tosatti 
65410078321SHolger Schurig struct lbs_debugfs_files {
6554101dec9SJan Engelhardt 	const char *name;
656f4ae40a6SAl Viro 	umode_t perm;
657876c9d3aSMarcelo Tosatti 	struct file_operations fops;
658876c9d3aSMarcelo Tosatti };
659876c9d3aSMarcelo Tosatti 
6604101dec9SJan Engelhardt static const struct lbs_debugfs_files debugfs_files[] = {
66110078321SHolger Schurig 	{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
66210078321SHolger Schurig 	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
66310078321SHolger Schurig 				lbs_sleepparams_write), },
6641311843cSAmitkumar Karwar 	{ "hostsleep", 0644, FOPS(lbs_host_sleep_read,
6651311843cSAmitkumar Karwar 				lbs_host_sleep_write), },
666876c9d3aSMarcelo Tosatti };
667876c9d3aSMarcelo Tosatti 
6684101dec9SJan Engelhardt static const struct lbs_debugfs_files debugfs_events_files[] = {
66910078321SHolger Schurig 	{"low_rssi", 0644, FOPS(lbs_lowrssi_read,
67010078321SHolger Schurig 				lbs_lowrssi_write), },
67110078321SHolger Schurig 	{"low_snr", 0644, FOPS(lbs_lowsnr_read,
67210078321SHolger Schurig 				lbs_lowsnr_write), },
67310078321SHolger Schurig 	{"failure_count", 0644, FOPS(lbs_failcount_read,
67410078321SHolger Schurig 				lbs_failcount_write), },
67510078321SHolger Schurig 	{"beacon_missed", 0644, FOPS(lbs_bcnmiss_read,
67610078321SHolger Schurig 				lbs_bcnmiss_write), },
67710078321SHolger Schurig 	{"high_rssi", 0644, FOPS(lbs_highrssi_read,
67810078321SHolger Schurig 				lbs_highrssi_write), },
67910078321SHolger Schurig 	{"high_snr", 0644, FOPS(lbs_highsnr_read,
68010078321SHolger Schurig 				lbs_highsnr_write), },
681876c9d3aSMarcelo Tosatti };
682876c9d3aSMarcelo Tosatti 
6834101dec9SJan Engelhardt static const struct lbs_debugfs_files debugfs_regs_files[] = {
68410078321SHolger Schurig 	{"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), },
68510078321SHolger Schurig 	{"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), },
68610078321SHolger Schurig 	{"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), },
68710078321SHolger Schurig 	{"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), },
68810078321SHolger Schurig 	{"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), },
68910078321SHolger Schurig 	{"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), },
690876c9d3aSMarcelo Tosatti };
691876c9d3aSMarcelo Tosatti 
lbs_debugfs_init(void)69210078321SHolger Schurig void lbs_debugfs_init(void)
693876c9d3aSMarcelo Tosatti {
69410078321SHolger Schurig 	if (!lbs_dir)
69510078321SHolger Schurig 		lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
696876c9d3aSMarcelo Tosatti }
697876c9d3aSMarcelo Tosatti 
lbs_debugfs_remove(void)69810078321SHolger Schurig void lbs_debugfs_remove(void)
699876c9d3aSMarcelo Tosatti {
70010078321SHolger Schurig 	debugfs_remove(lbs_dir);
701876c9d3aSMarcelo Tosatti }
702876c9d3aSMarcelo Tosatti 
lbs_debugfs_init_one(struct lbs_private * priv,struct net_device * dev)70369f9032dSHolger Schurig void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
704876c9d3aSMarcelo Tosatti {
705876c9d3aSMarcelo Tosatti 	int i;
7064101dec9SJan Engelhardt 	const struct lbs_debugfs_files *files;
70710078321SHolger Schurig 	if (!lbs_dir)
708876c9d3aSMarcelo Tosatti 		goto exit;
709876c9d3aSMarcelo Tosatti 
71010078321SHolger Schurig 	priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir);
711876c9d3aSMarcelo Tosatti 
712876c9d3aSMarcelo Tosatti 	for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
713876c9d3aSMarcelo Tosatti 		files = &debugfs_files[i];
714876c9d3aSMarcelo Tosatti 		priv->debugfs_files[i] = debugfs_create_file(files->name,
715876c9d3aSMarcelo Tosatti 							     files->perm,
716876c9d3aSMarcelo Tosatti 							     priv->debugfs_dir,
717876c9d3aSMarcelo Tosatti 							     priv,
718876c9d3aSMarcelo Tosatti 							     &files->fops);
719876c9d3aSMarcelo Tosatti 	}
720876c9d3aSMarcelo Tosatti 
721876c9d3aSMarcelo Tosatti 	priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
722876c9d3aSMarcelo Tosatti 
723876c9d3aSMarcelo Tosatti 	for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
724876c9d3aSMarcelo Tosatti 		files = &debugfs_events_files[i];
725876c9d3aSMarcelo Tosatti 		priv->debugfs_events_files[i] = debugfs_create_file(files->name,
726876c9d3aSMarcelo Tosatti 							     files->perm,
727876c9d3aSMarcelo Tosatti 							     priv->events_dir,
728876c9d3aSMarcelo Tosatti 							     priv,
729876c9d3aSMarcelo Tosatti 							     &files->fops);
730876c9d3aSMarcelo Tosatti 	}
731876c9d3aSMarcelo Tosatti 
732876c9d3aSMarcelo Tosatti 	priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
733876c9d3aSMarcelo Tosatti 
734876c9d3aSMarcelo Tosatti 	for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
735876c9d3aSMarcelo Tosatti 		files = &debugfs_regs_files[i];
736876c9d3aSMarcelo Tosatti 		priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
737876c9d3aSMarcelo Tosatti 							     files->perm,
738876c9d3aSMarcelo Tosatti 							     priv->regs_dir,
739876c9d3aSMarcelo Tosatti 							     priv,
740876c9d3aSMarcelo Tosatti 							     &files->fops);
741876c9d3aSMarcelo Tosatti 	}
742876c9d3aSMarcelo Tosatti 
743876c9d3aSMarcelo Tosatti #ifdef PROC_DEBUG
744e98a88ddSHolger Schurig 	lbs_debug_init(priv);
745876c9d3aSMarcelo Tosatti #endif
746876c9d3aSMarcelo Tosatti exit:
747876c9d3aSMarcelo Tosatti 	return;
748876c9d3aSMarcelo Tosatti }
749876c9d3aSMarcelo Tosatti 
lbs_debugfs_remove_one(struct lbs_private * priv)75069f9032dSHolger Schurig void lbs_debugfs_remove_one(struct lbs_private *priv)
751876c9d3aSMarcelo Tosatti {
752876c9d3aSMarcelo Tosatti 	int i;
753876c9d3aSMarcelo Tosatti 
754876c9d3aSMarcelo Tosatti 	for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
755876c9d3aSMarcelo Tosatti 		debugfs_remove(priv->debugfs_regs_files[i]);
756876c9d3aSMarcelo Tosatti 
757876c9d3aSMarcelo Tosatti 	debugfs_remove(priv->regs_dir);
758876c9d3aSMarcelo Tosatti 
7590b7db956SHolger Schurig 	for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++)
760876c9d3aSMarcelo Tosatti 		debugfs_remove(priv->debugfs_events_files[i]);
761876c9d3aSMarcelo Tosatti 
762876c9d3aSMarcelo Tosatti 	debugfs_remove(priv->events_dir);
763876c9d3aSMarcelo Tosatti #ifdef PROC_DEBUG
764876c9d3aSMarcelo Tosatti 	debugfs_remove(priv->debugfs_debug);
765876c9d3aSMarcelo Tosatti #endif
766876c9d3aSMarcelo Tosatti 	for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
767876c9d3aSMarcelo Tosatti 		debugfs_remove(priv->debugfs_files[i]);
7680b7db956SHolger Schurig 	debugfs_remove(priv->debugfs_dir);
769876c9d3aSMarcelo Tosatti }
770876c9d3aSMarcelo Tosatti 
77146868202SHolger Schurig 
77246868202SHolger Schurig 
773876c9d3aSMarcelo Tosatti /* debug entry */
774876c9d3aSMarcelo Tosatti 
77546868202SHolger Schurig #ifdef PROC_DEBUG
77646868202SHolger Schurig 
777*c593642cSPankaj Bharadiya #define item_size(n)	(sizeof_field(struct lbs_private, n))
778aa21c004SDavid Woodhouse #define item_addr(n)	(offsetof(struct lbs_private, n))
779876c9d3aSMarcelo Tosatti 
78046868202SHolger Schurig 
781876c9d3aSMarcelo Tosatti struct debug_data {
782876c9d3aSMarcelo Tosatti 	char name[32];
783876c9d3aSMarcelo Tosatti 	u32 size;
7844269e2adSDan Williams 	size_t addr;
785876c9d3aSMarcelo Tosatti };
786876c9d3aSMarcelo Tosatti 
787aa21c004SDavid Woodhouse /* To debug any member of struct lbs_private, simply add one line here.
788876c9d3aSMarcelo Tosatti  */
789876c9d3aSMarcelo Tosatti static struct debug_data items[] = {
790876c9d3aSMarcelo Tosatti 	{"psmode", item_size(psmode), item_addr(psmode)},
791876c9d3aSMarcelo Tosatti 	{"psstate", item_size(psstate), item_addr(psstate)},
792876c9d3aSMarcelo Tosatti };
793876c9d3aSMarcelo Tosatti 
794d2f11e09STony Breeds static int num_of_items = ARRAY_SIZE(items);
795876c9d3aSMarcelo Tosatti 
796876c9d3aSMarcelo Tosatti /**
7978973a6e7SRandy Dunlap  * lbs_debugfs_read - proc read function
798876c9d3aSMarcelo Tosatti  *
7998973a6e7SRandy Dunlap  * @file:	file to read
8008973a6e7SRandy Dunlap  * @userbuf:	pointer to buffer
8018973a6e7SRandy Dunlap  * @count:	number of bytes to read
8028973a6e7SRandy Dunlap  * @ppos:	read data starting position
8038973a6e7SRandy Dunlap  *
8048973a6e7SRandy Dunlap  * returns:	amount of data read or negative error code
805876c9d3aSMarcelo Tosatti  */
lbs_debugfs_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)80610078321SHolger Schurig static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
807876c9d3aSMarcelo Tosatti 			size_t count, loff_t *ppos)
808876c9d3aSMarcelo Tosatti {
809876c9d3aSMarcelo Tosatti 	int val = 0;
810876c9d3aSMarcelo Tosatti 	size_t pos = 0;
811876c9d3aSMarcelo Tosatti 	ssize_t res;
812876c9d3aSMarcelo Tosatti 	char *p;
813876c9d3aSMarcelo Tosatti 	int i;
814876c9d3aSMarcelo Tosatti 	struct debug_data *d;
815876c9d3aSMarcelo Tosatti 	unsigned long addr = get_zeroed_page(GFP_KERNEL);
816876c9d3aSMarcelo Tosatti 	char *buf = (char *)addr;
817ad43f8bfSKiran Divekar 	if (!buf)
818ad43f8bfSKiran Divekar 		return -ENOMEM;
819876c9d3aSMarcelo Tosatti 
820876c9d3aSMarcelo Tosatti 	p = buf;
821876c9d3aSMarcelo Tosatti 
82257674308SJoe Perches 	d = file->private_data;
823876c9d3aSMarcelo Tosatti 
824876c9d3aSMarcelo Tosatti 	for (i = 0; i < num_of_items; i++) {
825876c9d3aSMarcelo Tosatti 		if (d[i].size == 1)
826876c9d3aSMarcelo Tosatti 			val = *((u8 *) d[i].addr);
827876c9d3aSMarcelo Tosatti 		else if (d[i].size == 2)
828876c9d3aSMarcelo Tosatti 			val = *((u16 *) d[i].addr);
829876c9d3aSMarcelo Tosatti 		else if (d[i].size == 4)
830876c9d3aSMarcelo Tosatti 			val = *((u32 *) d[i].addr);
8314269e2adSDan Williams 		else if (d[i].size == 8)
8324269e2adSDan Williams 			val = *((u64 *) d[i].addr);
833876c9d3aSMarcelo Tosatti 
834876c9d3aSMarcelo Tosatti 		pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
835876c9d3aSMarcelo Tosatti 	}
836876c9d3aSMarcelo Tosatti 
837876c9d3aSMarcelo Tosatti 	res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
838876c9d3aSMarcelo Tosatti 
839876c9d3aSMarcelo Tosatti 	free_page(addr);
840876c9d3aSMarcelo Tosatti 	return res;
841876c9d3aSMarcelo Tosatti }
842876c9d3aSMarcelo Tosatti 
843876c9d3aSMarcelo Tosatti /**
8448973a6e7SRandy Dunlap  * lbs_debugfs_write - proc write function
845876c9d3aSMarcelo Tosatti  *
8468973a6e7SRandy Dunlap  * @f:		file pointer
8478973a6e7SRandy Dunlap  * @buf:	pointer to data buffer
8488973a6e7SRandy Dunlap  * @cnt:	data number to write
8498973a6e7SRandy Dunlap  * @ppos:	file position
8508973a6e7SRandy Dunlap  *
8518973a6e7SRandy Dunlap  * returns:	amount of data written
852876c9d3aSMarcelo Tosatti  */
lbs_debugfs_write(struct file * f,const char __user * buf,size_t cnt,loff_t * ppos)85310078321SHolger Schurig static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
854876c9d3aSMarcelo Tosatti 			    size_t cnt, loff_t *ppos)
855876c9d3aSMarcelo Tosatti {
856876c9d3aSMarcelo Tosatti 	int r, i;
857876c9d3aSMarcelo Tosatti 	char *pdata;
858876c9d3aSMarcelo Tosatti 	char *p;
859876c9d3aSMarcelo Tosatti 	char *p0;
860876c9d3aSMarcelo Tosatti 	char *p1;
861876c9d3aSMarcelo Tosatti 	char *p2;
86257674308SJoe Perches 	struct debug_data *d = f->private_data;
863876c9d3aSMarcelo Tosatti 
864a497e47dSDan Carpenter 	if (cnt == 0)
865a497e47dSDan Carpenter 		return 0;
866a497e47dSDan Carpenter 
867f0fc8696SAl Viro 	pdata = memdup_user_nul(buf, cnt);
868f0fc8696SAl Viro 	if (IS_ERR(pdata))
869f0fc8696SAl Viro 		return PTR_ERR(pdata);
870876c9d3aSMarcelo Tosatti 
871876c9d3aSMarcelo Tosatti 	p0 = pdata;
872876c9d3aSMarcelo Tosatti 	for (i = 0; i < num_of_items; i++) {
873876c9d3aSMarcelo Tosatti 		do {
874876c9d3aSMarcelo Tosatti 			p = strstr(p0, d[i].name);
875876c9d3aSMarcelo Tosatti 			if (p == NULL)
876876c9d3aSMarcelo Tosatti 				break;
877876c9d3aSMarcelo Tosatti 			p1 = strchr(p, '\n');
878876c9d3aSMarcelo Tosatti 			if (p1 == NULL)
879876c9d3aSMarcelo Tosatti 				break;
880876c9d3aSMarcelo Tosatti 			p0 = p1++;
881876c9d3aSMarcelo Tosatti 			p2 = strchr(p, '=');
882876c9d3aSMarcelo Tosatti 			if (!p2)
883876c9d3aSMarcelo Tosatti 				break;
884876c9d3aSMarcelo Tosatti 			p2++;
885d2f11e09STony Breeds 			r = simple_strtoul(p2, NULL, 0);
886876c9d3aSMarcelo Tosatti 			if (d[i].size == 1)
887876c9d3aSMarcelo Tosatti 				*((u8 *) d[i].addr) = (u8) r;
888876c9d3aSMarcelo Tosatti 			else if (d[i].size == 2)
889876c9d3aSMarcelo Tosatti 				*((u16 *) d[i].addr) = (u16) r;
890876c9d3aSMarcelo Tosatti 			else if (d[i].size == 4)
891876c9d3aSMarcelo Tosatti 				*((u32 *) d[i].addr) = (u32) r;
8924269e2adSDan Williams 			else if (d[i].size == 8)
8934269e2adSDan Williams 				*((u64 *) d[i].addr) = (u64) r;
894876c9d3aSMarcelo Tosatti 			break;
895876c9d3aSMarcelo Tosatti 		} while (1);
896876c9d3aSMarcelo Tosatti 	}
897876c9d3aSMarcelo Tosatti 	kfree(pdata);
898876c9d3aSMarcelo Tosatti 
8994269e2adSDan Williams 	return (ssize_t)cnt;
900876c9d3aSMarcelo Tosatti }
901876c9d3aSMarcelo Tosatti 
9024101dec9SJan Engelhardt static const struct file_operations lbs_debug_fops = {
903876c9d3aSMarcelo Tosatti 	.owner = THIS_MODULE,
904234e3405SStephen Boyd 	.open = simple_open,
90510078321SHolger Schurig 	.write = lbs_debugfs_write,
90610078321SHolger Schurig 	.read = lbs_debugfs_read,
9076038f373SArnd Bergmann 	.llseek = default_llseek,
908876c9d3aSMarcelo Tosatti };
909876c9d3aSMarcelo Tosatti 
910876c9d3aSMarcelo Tosatti /**
9118973a6e7SRandy Dunlap  * lbs_debug_init - create debug proc file
912876c9d3aSMarcelo Tosatti  *
9138973a6e7SRandy Dunlap  * @priv:	pointer to &struct lbs_private
9148973a6e7SRandy Dunlap  *
9158973a6e7SRandy Dunlap  * returns:	N/A
916876c9d3aSMarcelo Tosatti  */
lbs_debug_init(struct lbs_private * priv)917e98a88ddSHolger Schurig static void lbs_debug_init(struct lbs_private *priv)
918876c9d3aSMarcelo Tosatti {
919876c9d3aSMarcelo Tosatti 	int i;
920876c9d3aSMarcelo Tosatti 
921876c9d3aSMarcelo Tosatti 	if (!priv->debugfs_dir)
922876c9d3aSMarcelo Tosatti 		return;
923876c9d3aSMarcelo Tosatti 
924876c9d3aSMarcelo Tosatti 	for (i = 0; i < num_of_items; i++)
925aa21c004SDavid Woodhouse 		items[i].addr += (size_t) priv;
926876c9d3aSMarcelo Tosatti 
927876c9d3aSMarcelo Tosatti 	priv->debugfs_debug = debugfs_create_file("debug", 0644,
928876c9d3aSMarcelo Tosatti 						  priv->debugfs_dir, &items[0],
92910078321SHolger Schurig 						  &lbs_debug_fops);
930876c9d3aSMarcelo Tosatti }
93146868202SHolger Schurig #endif
932