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