Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | #include <linux/module.h> |
2 | #include <linux/dcache.h> | |
3 | #include <linux/debugfs.h> | |
4 | #include <linux/delay.h> | |
5 | #include <linux/mm.h> | |
d8b0fb51 | 6 | #include <linux/string.h> |
876c9d3a | 7 | #include <net/iw_handler.h> |
46868202 | 8 | |
876c9d3a MT |
9 | #include "dev.h" |
10 | #include "decl.h" | |
11 | #include "host.h" | |
5bdb3efe | 12 | #include "debugfs.h" |
876c9d3a | 13 | |
10078321 | 14 | static struct dentry *lbs_dir; |
876c9d3a MT |
15 | static char *szStates[] = { |
16 | "Connected", | |
17 | "Disconnected" | |
18 | }; | |
19 | ||
46868202 | 20 | #ifdef PROC_DEBUG |
69f9032d | 21 | static void lbs_debug_init(struct lbs_private *priv, struct net_device *dev); |
46868202 | 22 | #endif |
876c9d3a MT |
23 | |
24 | static int open_file_generic(struct inode *inode, struct file *file) | |
25 | { | |
26 | file->private_data = inode->i_private; | |
27 | return 0; | |
28 | } | |
29 | ||
30 | static ssize_t write_file_dummy(struct file *file, const char __user *buf, | |
31 | size_t count, loff_t *ppos) | |
32 | { | |
33 | return -EINVAL; | |
34 | } | |
35 | ||
36 | static const size_t len = PAGE_SIZE; | |
37 | ||
10078321 | 38 | static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, |
876c9d3a MT |
39 | size_t count, loff_t *ppos) |
40 | { | |
69f9032d | 41 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
42 | size_t pos = 0; |
43 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
44 | char *buf = (char *)addr; | |
45 | ssize_t res; | |
46 | ||
47 | pos += snprintf(buf+pos, len-pos, "state = %s\n", | |
aa21c004 | 48 | szStates[priv->connect_status]); |
876c9d3a | 49 | pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", |
aa21c004 | 50 | (u32) priv->regioncode); |
876c9d3a MT |
51 | |
52 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
53 | ||
54 | free_page(addr); | |
55 | return res; | |
56 | } | |
57 | ||
58 | ||
10078321 | 59 | static ssize_t lbs_getscantable(struct file *file, char __user *userbuf, |
876c9d3a MT |
60 | size_t count, loff_t *ppos) |
61 | { | |
69f9032d | 62 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
63 | size_t pos = 0; |
64 | int numscansdone = 0, res; | |
65 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
66 | char *buf = (char *)addr; | |
0795af57 | 67 | DECLARE_MAC_BUF(mac); |
fcdb53db | 68 | struct bss_descriptor * iter_bss; |
876c9d3a | 69 | |
876c9d3a | 70 | pos += snprintf(buf+pos, len-pos, |
a2235ed4 | 71 | "# | ch | rssi | bssid | cap | Qual | SSID \n"); |
876c9d3a | 72 | |
aa21c004 DW |
73 | mutex_lock(&priv->lock); |
74 | list_for_each_entry (iter_bss, &priv->network_list, list) { | |
0c9ca690 DW |
75 | u16 ibss = (iter_bss->capability & WLAN_CAPABILITY_IBSS); |
76 | u16 privacy = (iter_bss->capability & WLAN_CAPABILITY_PRIVACY); | |
77 | u16 spectrum_mgmt = (iter_bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT); | |
876c9d3a | 78 | |
876c9d3a | 79 | pos += snprintf(buf+pos, len-pos, |
0795af57 | 80 | "%02u| %03d | %04ld | %s |", |
fcdb53db | 81 | numscansdone, iter_bss->channel, iter_bss->rssi, |
0795af57 | 82 | print_mac(mac, iter_bss->bssid)); |
0c9ca690 | 83 | pos += snprintf(buf+pos, len-pos, " %04x-", iter_bss->capability); |
876c9d3a | 84 | pos += snprintf(buf+pos, len-pos, "%c%c%c |", |
0c9ca690 DW |
85 | ibss ? 'A' : 'I', privacy ? 'P' : ' ', |
86 | spectrum_mgmt ? 'S' : ' '); | |
a2235ed4 | 87 | pos += snprintf(buf+pos, len-pos, " %04d |", SCAN_RSSI(iter_bss->rssi)); |
d8efea25 DW |
88 | pos += snprintf(buf+pos, len-pos, " %s\n", |
89 | escape_essid(iter_bss->ssid, iter_bss->ssid_len)); | |
876c9d3a MT |
90 | |
91 | numscansdone++; | |
92 | } | |
aa21c004 | 93 | mutex_unlock(&priv->lock); |
876c9d3a MT |
94 | |
95 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
96 | ||
97 | free_page(addr); | |
98 | return res; | |
99 | } | |
100 | ||
10078321 | 101 | static ssize_t lbs_sleepparams_write(struct file *file, |
876c9d3a MT |
102 | const char __user *user_buf, size_t count, |
103 | loff_t *ppos) | |
104 | { | |
69f9032d | 105 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
106 | ssize_t buf_size, res; |
107 | int p1, p2, p3, p4, p5, p6; | |
876c9d3a MT |
108 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
109 | char *buf = (char *)addr; | |
110 | ||
111 | buf_size = min(count, len - 1); | |
112 | if (copy_from_user(buf, user_buf, buf_size)) { | |
113 | res = -EFAULT; | |
114 | goto out_unlock; | |
115 | } | |
116 | res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); | |
117 | if (res != 6) { | |
118 | res = -EFAULT; | |
119 | goto out_unlock; | |
120 | } | |
aa21c004 DW |
121 | priv->sp.sp_error = p1; |
122 | priv->sp.sp_offset = p2; | |
123 | priv->sp.sp_stabletime = p3; | |
124 | priv->sp.sp_calcontrol = p4; | |
125 | priv->sp.sp_extsleepclk = p5; | |
126 | priv->sp.sp_reserved = p6; | |
876c9d3a | 127 | |
10078321 | 128 | res = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
129 | CMD_802_11_SLEEP_PARAMS, |
130 | CMD_ACT_SET, | |
131 | CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a MT |
132 | |
133 | if (!res) | |
134 | res = count; | |
135 | else | |
136 | res = -EINVAL; | |
137 | ||
138 | out_unlock: | |
139 | free_page(addr); | |
140 | return res; | |
141 | } | |
142 | ||
10078321 | 143 | static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, |
876c9d3a MT |
144 | size_t count, loff_t *ppos) |
145 | { | |
69f9032d | 146 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
147 | ssize_t res; |
148 | size_t pos = 0; | |
149 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
150 | char *buf = (char *)addr; | |
151 | ||
10078321 | 152 | res = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
153 | CMD_802_11_SLEEP_PARAMS, |
154 | CMD_ACT_GET, | |
155 | CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a MT |
156 | if (res) { |
157 | res = -EFAULT; | |
158 | goto out_unlock; | |
159 | } | |
160 | ||
aa21c004 DW |
161 | pos += snprintf(buf, len, "%d %d %d %d %d %d\n", priv->sp.sp_error, |
162 | priv->sp.sp_offset, priv->sp.sp_stabletime, | |
163 | priv->sp.sp_calcontrol, priv->sp.sp_extsleepclk, | |
164 | priv->sp.sp_reserved); | |
876c9d3a MT |
165 | |
166 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
167 | ||
168 | out_unlock: | |
169 | free_page(addr); | |
170 | return res; | |
171 | } | |
172 | ||
10078321 | 173 | static ssize_t lbs_extscan(struct file *file, const char __user *userbuf, |
876c9d3a MT |
174 | size_t count, loff_t *ppos) |
175 | { | |
69f9032d | 176 | struct lbs_private *priv = file->private_data; |
876c9d3a | 177 | ssize_t res, buf_size; |
876c9d3a MT |
178 | union iwreq_data wrqu; |
179 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
180 | char *buf = (char *)addr; | |
181 | ||
182 | buf_size = min(count, len - 1); | |
183 | if (copy_from_user(buf, userbuf, buf_size)) { | |
184 | res = -EFAULT; | |
185 | goto out_unlock; | |
186 | } | |
187 | ||
10078321 | 188 | lbs_send_specific_ssid_scan(priv, buf, strlen(buf)-1, 0); |
876c9d3a MT |
189 | |
190 | memset(&wrqu, 0, sizeof(union iwreq_data)); | |
634b8f49 | 191 | wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); |
876c9d3a MT |
192 | |
193 | out_unlock: | |
194 | free_page(addr); | |
195 | return count; | |
196 | } | |
197 | ||
10078321 HS |
198 | static void lbs_parse_bssid(char *buf, size_t count, |
199 | struct lbs_ioctl_user_scan_cfg *scan_cfg) | |
876c9d3a MT |
200 | { |
201 | char *hold; | |
202 | unsigned int mac[ETH_ALEN]; | |
876c9d3a MT |
203 | |
204 | hold = strstr(buf, "bssid="); | |
205 | if (!hold) | |
206 | return; | |
207 | hold += 6; | |
15ca36fb DM |
208 | sscanf(hold, "%02x:%02x:%02x:%02x:%02x:%02x", |
209 | mac, mac+1, mac+2, mac+3, mac+4, mac+5); | |
eb8f7330 | 210 | memcpy(scan_cfg->bssid, mac, ETH_ALEN); |
876c9d3a MT |
211 | } |
212 | ||
10078321 HS |
213 | static void lbs_parse_ssid(char *buf, size_t count, |
214 | struct lbs_ioctl_user_scan_cfg *scan_cfg) | |
876c9d3a MT |
215 | { |
216 | char *hold, *end; | |
217 | ssize_t size; | |
218 | ||
219 | hold = strstr(buf, "ssid="); | |
220 | if (!hold) | |
221 | return; | |
222 | hold += 5; | |
d8b0fb51 | 223 | end = strchr(hold, ' '); |
876c9d3a MT |
224 | if (!end) |
225 | end = buf + count - 1; | |
226 | ||
4269e2ad | 227 | size = min((size_t)IW_ESSID_MAX_SIZE, (size_t) (end - hold)); |
eb8f7330 | 228 | strncpy(scan_cfg->ssid, hold, size); |
876c9d3a MT |
229 | |
230 | return; | |
231 | } | |
232 | ||
10078321 | 233 | static int lbs_parse_clear(char *buf, size_t count, const char *tag) |
876c9d3a MT |
234 | { |
235 | char *hold; | |
236 | int val; | |
237 | ||
eb8f7330 | 238 | hold = strstr(buf, tag); |
876c9d3a | 239 | if (!hold) |
eb8f7330 DW |
240 | return 0; |
241 | hold += strlen(tag); | |
876c9d3a MT |
242 | sscanf(hold, "%d", &val); |
243 | ||
244 | if (val != 0) | |
245 | val = 1; | |
246 | ||
eb8f7330 | 247 | return val; |
876c9d3a MT |
248 | } |
249 | ||
10078321 HS |
250 | static int lbs_parse_dur(char *buf, size_t count, |
251 | struct lbs_ioctl_user_scan_cfg *scan_cfg) | |
876c9d3a MT |
252 | { |
253 | char *hold; | |
254 | int val; | |
255 | ||
256 | hold = strstr(buf, "dur="); | |
257 | if (!hold) | |
258 | return 0; | |
259 | hold += 4; | |
260 | sscanf(hold, "%d", &val); | |
261 | ||
262 | return val; | |
263 | } | |
264 | ||
10078321 HS |
265 | static void lbs_parse_type(char *buf, size_t count, |
266 | struct lbs_ioctl_user_scan_cfg *scan_cfg) | |
876c9d3a MT |
267 | { |
268 | char *hold; | |
269 | int val; | |
270 | ||
271 | hold = strstr(buf, "type="); | |
272 | if (!hold) | |
273 | return; | |
274 | hold += 5; | |
275 | sscanf(hold, "%d", &val); | |
276 | ||
277 | /* type=1,2 or 3 */ | |
278 | if (val < 1 || val > 3) | |
279 | return; | |
280 | ||
281 | scan_cfg->bsstype = val; | |
282 | ||
283 | return; | |
284 | } | |
285 | ||
10078321 | 286 | static ssize_t lbs_setuserscan(struct file *file, |
876c9d3a MT |
287 | const char __user *userbuf, |
288 | size_t count, loff_t *ppos) | |
289 | { | |
69f9032d | 290 | struct lbs_private *priv = file->private_data; |
876c9d3a | 291 | ssize_t res, buf_size; |
10078321 | 292 | struct lbs_ioctl_user_scan_cfg *scan_cfg; |
876c9d3a MT |
293 | union iwreq_data wrqu; |
294 | int dur; | |
99c893f3 | 295 | char *buf = (char *)get_zeroed_page(GFP_KERNEL); |
876c9d3a | 296 | |
99c893f3 | 297 | if (!buf) |
876c9d3a | 298 | return -ENOMEM; |
99c893f3 | 299 | |
876c9d3a MT |
300 | buf_size = min(count, len - 1); |
301 | if (copy_from_user(buf, userbuf, buf_size)) { | |
302 | res = -EFAULT; | |
99c893f3 | 303 | goto out_buf; |
876c9d3a MT |
304 | } |
305 | ||
99c893f3 DW |
306 | scan_cfg = kzalloc(sizeof(struct lbs_ioctl_user_scan_cfg), GFP_KERNEL); |
307 | if (!scan_cfg) { | |
308 | res = -ENOMEM; | |
309 | goto out_buf; | |
310 | } | |
311 | res = count; | |
312 | ||
10078321 | 313 | scan_cfg->bsstype = LBS_SCAN_BSS_TYPE_ANY; |
876c9d3a | 314 | |
10078321 | 315 | dur = lbs_parse_dur(buf, count, scan_cfg); |
10078321 HS |
316 | lbs_parse_bssid(buf, count, scan_cfg); |
317 | scan_cfg->clear_bssid = lbs_parse_clear(buf, count, "clear_bssid="); | |
318 | lbs_parse_ssid(buf, count, scan_cfg); | |
319 | scan_cfg->clear_ssid = lbs_parse_clear(buf, count, "clear_ssid="); | |
10078321 | 320 | lbs_parse_type(buf, count, scan_cfg); |
876c9d3a | 321 | |
10078321 | 322 | lbs_scan_networks(priv, scan_cfg, 1); |
aa21c004 | 323 | wait_event_interruptible(priv->cmd_pending, |
020bb19e | 324 | priv->surpriseremoved || !priv->last_scanned_channel); |
99c893f3 | 325 | |
aa21c004 | 326 | if (priv->surpriseremoved) |
99c893f3 | 327 | goto out_scan_cfg; |
876c9d3a MT |
328 | |
329 | memset(&wrqu, 0x00, sizeof(union iwreq_data)); | |
634b8f49 | 330 | wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); |
876c9d3a | 331 | |
99c893f3 | 332 | out_scan_cfg: |
876c9d3a | 333 | kfree(scan_cfg); |
99c893f3 DW |
334 | out_buf: |
335 | free_page((unsigned long)buf); | |
336 | return res; | |
876c9d3a MT |
337 | } |
338 | ||
876c9d3a | 339 | |
3a188649 HS |
340 | /* |
341 | * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might | |
342 | * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the | |
343 | * firmware. Here's an example: | |
344 | * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 | |
345 | * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 | |
346 | * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
347 | * | |
348 | * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, | |
349 | * 00 00 are the data bytes of this TLV. For this TLV, their meaning is | |
350 | * defined in mrvlietypes_thresholds | |
351 | * | |
352 | * This function searches in this TLV data chunk for a given TLV type | |
353 | * and returns a pointer to the first data byte of the TLV, or to NULL | |
354 | * if the TLV hasn't been found. | |
355 | */ | |
356 | static void *lbs_tlv_find(u16 tlv_type, const u8 *tlv, u16 size) | |
876c9d3a | 357 | { |
3a188649 | 358 | __le16 le_type = cpu_to_le16(tlv_type); |
876c9d3a | 359 | ssize_t pos = 0; |
3a188649 HS |
360 | struct mrvlietypesheader *tlv_h; |
361 | while (pos < size) { | |
362 | u16 length; | |
363 | tlv_h = (struct mrvlietypesheader *) tlv; | |
364 | if (tlv_h->type == le_type) | |
365 | return tlv_h; | |
366 | if (tlv_h->len == 0) | |
367 | return NULL; | |
368 | length = le16_to_cpu(tlv_h->len) + | |
369 | sizeof(struct mrvlietypesheader); | |
370 | pos += length; | |
371 | tlv += length; | |
372 | } | |
373 | return NULL; | |
876c9d3a MT |
374 | } |
375 | ||
3a188649 HS |
376 | |
377 | /* | |
378 | * This just gets the bitmap of currently subscribed events. Used when | |
379 | * adding an additonal event subscription. | |
380 | */ | |
69f9032d | 381 | static u16 lbs_get_events_bitmap(struct lbs_private *priv) |
876c9d3a | 382 | { |
3a188649 | 383 | ssize_t res; |
876c9d3a | 384 | |
3a188649 HS |
385 | struct cmd_ds_802_11_subscribe_event *events = kzalloc( |
386 | sizeof(struct cmd_ds_802_11_subscribe_event), | |
387 | GFP_KERNEL); | |
876c9d3a | 388 | |
3a188649 HS |
389 | res = lbs_prepare_and_send_command(priv, |
390 | CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_GET, | |
391 | CMD_OPTION_WAITFORRSP, 0, events); | |
876c9d3a | 392 | |
3a188649 HS |
393 | if (res) { |
394 | kfree(events); | |
876c9d3a MT |
395 | return 0; |
396 | } | |
3a188649 | 397 | return le16_to_cpu(events->events); |
876c9d3a MT |
398 | } |
399 | ||
876c9d3a | 400 | |
3a188649 HS |
401 | static ssize_t lbs_threshold_read( |
402 | u16 tlv_type, u16 event_mask, | |
403 | struct file *file, char __user *userbuf, | |
404 | size_t count, loff_t *ppos) | |
876c9d3a | 405 | { |
69f9032d | 406 | struct lbs_private *priv = file->private_data; |
3a188649 HS |
407 | ssize_t res = 0; |
408 | size_t pos = 0; | |
876c9d3a MT |
409 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
410 | char *buf = (char *)addr; | |
3a188649 HS |
411 | u8 value; |
412 | u8 freq; | |
876c9d3a | 413 | |
3a188649 HS |
414 | struct cmd_ds_802_11_subscribe_event *events = kzalloc( |
415 | sizeof(struct cmd_ds_802_11_subscribe_event), | |
416 | GFP_KERNEL); | |
417 | struct mrvlietypes_thresholds *got; | |
876c9d3a | 418 | |
3a188649 HS |
419 | res = lbs_prepare_and_send_command(priv, |
420 | CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_GET, | |
421 | CMD_OPTION_WAITFORRSP, 0, events); | |
422 | if (res) { | |
423 | kfree(events); | |
424 | return res; | |
876c9d3a MT |
425 | } |
426 | ||
3a188649 HS |
427 | got = lbs_tlv_find(tlv_type, events->tlv, sizeof(events->tlv)); |
428 | if (got) { | |
429 | value = got->value; | |
430 | freq = got->freq; | |
876c9d3a | 431 | } |
3a188649 | 432 | kfree(events); |
876c9d3a | 433 | |
3a188649 HS |
434 | if (got) |
435 | pos += snprintf(buf, len, "%d %d %d\n", value, freq, | |
436 | !!(le16_to_cpu(events->events) & event_mask)); | |
876c9d3a MT |
437 | |
438 | res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
876c9d3a | 439 | |
876c9d3a MT |
440 | free_page(addr); |
441 | return res; | |
442 | } | |
443 | ||
876c9d3a | 444 | |
3a188649 HS |
445 | static ssize_t lbs_threshold_write( |
446 | u16 tlv_type, u16 event_mask, | |
447 | struct file *file, | |
448 | const char __user *userbuf, | |
449 | size_t count, loff_t *ppos) | |
876c9d3a | 450 | { |
69f9032d | 451 | struct lbs_private *priv = file->private_data; |
876c9d3a | 452 | ssize_t res, buf_size; |
3a188649 | 453 | int value, freq, curr_mask, new_mask; |
876c9d3a MT |
454 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
455 | char *buf = (char *)addr; | |
3a188649 | 456 | struct cmd_ds_802_11_subscribe_event *events; |
876c9d3a MT |
457 | |
458 | buf_size = min(count, len - 1); | |
459 | if (copy_from_user(buf, userbuf, buf_size)) { | |
460 | res = -EFAULT; | |
461 | goto out_unlock; | |
462 | } | |
3a188649 | 463 | res = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); |
876c9d3a MT |
464 | if (res != 3) { |
465 | res = -EFAULT; | |
466 | goto out_unlock; | |
467 | } | |
3a188649 | 468 | curr_mask = lbs_get_events_bitmap(priv); |
876c9d3a | 469 | |
3a188649 HS |
470 | if (new_mask) |
471 | new_mask = curr_mask | event_mask; | |
472 | else | |
473 | new_mask = curr_mask & ~event_mask; | |
474 | ||
475 | /* Now everything is set and we can send stuff down to the firmware */ | |
476 | events = kzalloc( | |
477 | sizeof(struct cmd_ds_802_11_subscribe_event), | |
478 | GFP_KERNEL); | |
479 | if (events) { | |
480 | struct mrvlietypes_thresholds *tlv = | |
481 | (struct mrvlietypes_thresholds *) events->tlv; | |
482 | events->action = cpu_to_le16(CMD_ACT_SET); | |
483 | events->events = cpu_to_le16(new_mask); | |
484 | tlv->header.type = cpu_to_le16(tlv_type); | |
485 | tlv->header.len = cpu_to_le16( | |
486 | sizeof(struct mrvlietypes_thresholds) - | |
487 | sizeof(struct mrvlietypesheader)); | |
488 | tlv->value = value; | |
489 | if (tlv_type != TLV_TYPE_BCNMISS) | |
490 | tlv->freq = freq; | |
491 | lbs_prepare_and_send_command(priv, | |
492 | CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_SET, | |
493 | CMD_OPTION_WAITFORRSP, 0, events); | |
494 | kfree(events); | |
876c9d3a MT |
495 | } |
496 | ||
497 | res = count; | |
498 | out_unlock: | |
499 | free_page(addr); | |
500 | return res; | |
501 | } | |
502 | ||
876c9d3a | 503 | |
3a188649 HS |
504 | static ssize_t lbs_lowrssi_read( |
505 | struct file *file, char __user *userbuf, | |
506 | size_t count, loff_t *ppos) | |
876c9d3a | 507 | { |
3a188649 HS |
508 | return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, |
509 | file, userbuf, count, ppos); | |
876c9d3a MT |
510 | } |
511 | ||
876c9d3a | 512 | |
3a188649 HS |
513 | static ssize_t lbs_lowrssi_write( |
514 | struct file *file, const char __user *userbuf, | |
515 | size_t count, loff_t *ppos) | |
516 | { | |
517 | return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, | |
518 | file, userbuf, count, ppos); | |
519 | } | |
876c9d3a | 520 | |
876c9d3a | 521 | |
3a188649 HS |
522 | static ssize_t lbs_lowsnr_read( |
523 | struct file *file, char __user *userbuf, | |
524 | size_t count, loff_t *ppos) | |
525 | { | |
526 | return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, | |
527 | file, userbuf, count, ppos); | |
528 | } | |
876c9d3a | 529 | |
876c9d3a | 530 | |
3a188649 HS |
531 | static ssize_t lbs_lowsnr_write( |
532 | struct file *file, const char __user *userbuf, | |
533 | size_t count, loff_t *ppos) | |
534 | { | |
535 | return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, | |
536 | file, userbuf, count, ppos); | |
537 | } | |
876c9d3a | 538 | |
876c9d3a | 539 | |
3a188649 HS |
540 | static ssize_t lbs_failcount_read( |
541 | struct file *file, char __user *userbuf, | |
542 | size_t count, loff_t *ppos) | |
543 | { | |
544 | return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, | |
545 | file, userbuf, count, ppos); | |
546 | } | |
876c9d3a | 547 | |
876c9d3a | 548 | |
3a188649 HS |
549 | static ssize_t lbs_failcount_write( |
550 | struct file *file, const char __user *userbuf, | |
551 | size_t count, loff_t *ppos) | |
552 | { | |
553 | return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, | |
554 | file, userbuf, count, ppos); | |
876c9d3a MT |
555 | } |
556 | ||
3a188649 HS |
557 | |
558 | static ssize_t lbs_highrssi_read( | |
559 | struct file *file, char __user *userbuf, | |
560 | size_t count, loff_t *ppos) | |
876c9d3a | 561 | { |
3a188649 HS |
562 | return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, |
563 | file, userbuf, count, ppos); | |
564 | } | |
876c9d3a | 565 | |
876c9d3a | 566 | |
3a188649 HS |
567 | static ssize_t lbs_highrssi_write( |
568 | struct file *file, const char __user *userbuf, | |
569 | size_t count, loff_t *ppos) | |
570 | { | |
571 | return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, | |
572 | file, userbuf, count, ppos); | |
573 | } | |
876c9d3a | 574 | |
876c9d3a | 575 | |
3a188649 HS |
576 | static ssize_t lbs_highsnr_read( |
577 | struct file *file, char __user *userbuf, | |
578 | size_t count, loff_t *ppos) | |
579 | { | |
580 | return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, | |
581 | file, userbuf, count, ppos); | |
582 | } | |
876c9d3a | 583 | |
876c9d3a | 584 | |
3a188649 HS |
585 | static ssize_t lbs_highsnr_write( |
586 | struct file *file, const char __user *userbuf, | |
587 | size_t count, loff_t *ppos) | |
588 | { | |
589 | return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, | |
590 | file, userbuf, count, ppos); | |
876c9d3a MT |
591 | } |
592 | ||
3a188649 HS |
593 | static ssize_t lbs_bcnmiss_read( |
594 | struct file *file, char __user *userbuf, | |
595 | size_t count, loff_t *ppos) | |
876c9d3a | 596 | { |
3a188649 HS |
597 | return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, |
598 | file, userbuf, count, ppos); | |
599 | } | |
876c9d3a | 600 | |
876c9d3a | 601 | |
3a188649 HS |
602 | static ssize_t lbs_bcnmiss_write( |
603 | struct file *file, const char __user *userbuf, | |
604 | size_t count, loff_t *ppos) | |
605 | { | |
606 | return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, | |
607 | file, userbuf, count, ppos); | |
876c9d3a MT |
608 | } |
609 | ||
876c9d3a | 610 | |
876c9d3a | 611 | |
876c9d3a | 612 | |
876c9d3a | 613 | |
876c9d3a | 614 | |
876c9d3a | 615 | |
876c9d3a | 616 | |
10078321 | 617 | static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, |
876c9d3a MT |
618 | size_t count, loff_t *ppos) |
619 | { | |
69f9032d | 620 | struct lbs_private *priv = file->private_data; |
10078321 | 621 | struct lbs_offset_value offval; |
876c9d3a MT |
622 | ssize_t pos = 0; |
623 | int ret; | |
624 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
625 | char *buf = (char *)addr; | |
626 | ||
627 | offval.offset = priv->mac_offset; | |
628 | offval.value = 0; | |
629 | ||
10078321 | 630 | ret = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
631 | CMD_MAC_REG_ACCESS, 0, |
632 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
633 | mdelay(10); |
634 | pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", | |
aa21c004 | 635 | priv->mac_offset, priv->offsetvalue.value); |
876c9d3a MT |
636 | |
637 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
638 | free_page(addr); | |
639 | return ret; | |
640 | } | |
641 | ||
10078321 | 642 | static ssize_t lbs_rdmac_write(struct file *file, |
876c9d3a MT |
643 | const char __user *userbuf, |
644 | size_t count, loff_t *ppos) | |
645 | { | |
69f9032d | 646 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
647 | ssize_t res, buf_size; |
648 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
649 | char *buf = (char *)addr; | |
650 | ||
651 | buf_size = min(count, len - 1); | |
652 | if (copy_from_user(buf, userbuf, buf_size)) { | |
653 | res = -EFAULT; | |
654 | goto out_unlock; | |
655 | } | |
656 | priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); | |
657 | res = count; | |
658 | out_unlock: | |
659 | free_page(addr); | |
660 | return res; | |
661 | } | |
662 | ||
10078321 | 663 | static ssize_t lbs_wrmac_write(struct file *file, |
876c9d3a MT |
664 | const char __user *userbuf, |
665 | size_t count, loff_t *ppos) | |
666 | { | |
667 | ||
69f9032d | 668 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
669 | ssize_t res, buf_size; |
670 | u32 offset, value; | |
10078321 | 671 | struct lbs_offset_value offval; |
876c9d3a MT |
672 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
673 | char *buf = (char *)addr; | |
674 | ||
675 | buf_size = min(count, len - 1); | |
676 | if (copy_from_user(buf, userbuf, buf_size)) { | |
677 | res = -EFAULT; | |
678 | goto out_unlock; | |
679 | } | |
680 | res = sscanf(buf, "%x %x", &offset, &value); | |
681 | if (res != 2) { | |
682 | res = -EFAULT; | |
683 | goto out_unlock; | |
684 | } | |
685 | ||
686 | offval.offset = offset; | |
687 | offval.value = value; | |
10078321 | 688 | res = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
689 | CMD_MAC_REG_ACCESS, 1, |
690 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
691 | mdelay(10); |
692 | ||
693 | res = count; | |
694 | out_unlock: | |
695 | free_page(addr); | |
696 | return res; | |
697 | } | |
698 | ||
10078321 | 699 | static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, |
876c9d3a MT |
700 | size_t count, loff_t *ppos) |
701 | { | |
69f9032d | 702 | struct lbs_private *priv = file->private_data; |
10078321 | 703 | struct lbs_offset_value offval; |
876c9d3a MT |
704 | ssize_t pos = 0; |
705 | int ret; | |
706 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
707 | char *buf = (char *)addr; | |
708 | ||
709 | offval.offset = priv->bbp_offset; | |
710 | offval.value = 0; | |
711 | ||
10078321 | 712 | ret = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
713 | CMD_BBP_REG_ACCESS, 0, |
714 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
715 | mdelay(10); |
716 | pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", | |
aa21c004 | 717 | priv->bbp_offset, priv->offsetvalue.value); |
876c9d3a MT |
718 | |
719 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
720 | free_page(addr); | |
721 | ||
722 | return ret; | |
723 | } | |
724 | ||
10078321 | 725 | static ssize_t lbs_rdbbp_write(struct file *file, |
876c9d3a MT |
726 | const char __user *userbuf, |
727 | size_t count, loff_t *ppos) | |
728 | { | |
69f9032d | 729 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
730 | ssize_t res, buf_size; |
731 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
732 | char *buf = (char *)addr; | |
733 | ||
734 | buf_size = min(count, len - 1); | |
735 | if (copy_from_user(buf, userbuf, buf_size)) { | |
736 | res = -EFAULT; | |
737 | goto out_unlock; | |
738 | } | |
739 | priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); | |
740 | res = count; | |
741 | out_unlock: | |
742 | free_page(addr); | |
743 | return res; | |
744 | } | |
745 | ||
10078321 | 746 | static ssize_t lbs_wrbbp_write(struct file *file, |
876c9d3a MT |
747 | const char __user *userbuf, |
748 | size_t count, loff_t *ppos) | |
749 | { | |
750 | ||
69f9032d | 751 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
752 | ssize_t res, buf_size; |
753 | u32 offset, value; | |
10078321 | 754 | struct lbs_offset_value offval; |
876c9d3a MT |
755 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
756 | char *buf = (char *)addr; | |
757 | ||
758 | buf_size = min(count, len - 1); | |
759 | if (copy_from_user(buf, userbuf, buf_size)) { | |
760 | res = -EFAULT; | |
761 | goto out_unlock; | |
762 | } | |
763 | res = sscanf(buf, "%x %x", &offset, &value); | |
764 | if (res != 2) { | |
765 | res = -EFAULT; | |
766 | goto out_unlock; | |
767 | } | |
768 | ||
769 | offval.offset = offset; | |
770 | offval.value = value; | |
10078321 | 771 | res = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
772 | CMD_BBP_REG_ACCESS, 1, |
773 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
774 | mdelay(10); |
775 | ||
776 | res = count; | |
777 | out_unlock: | |
778 | free_page(addr); | |
779 | return res; | |
780 | } | |
781 | ||
10078321 | 782 | static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, |
876c9d3a MT |
783 | size_t count, loff_t *ppos) |
784 | { | |
69f9032d | 785 | struct lbs_private *priv = file->private_data; |
10078321 | 786 | struct lbs_offset_value offval; |
876c9d3a MT |
787 | ssize_t pos = 0; |
788 | int ret; | |
789 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
790 | char *buf = (char *)addr; | |
791 | ||
792 | offval.offset = priv->rf_offset; | |
793 | offval.value = 0; | |
794 | ||
10078321 | 795 | ret = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
796 | CMD_RF_REG_ACCESS, 0, |
797 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
798 | mdelay(10); |
799 | pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", | |
aa21c004 | 800 | priv->rf_offset, priv->offsetvalue.value); |
876c9d3a MT |
801 | |
802 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | |
803 | free_page(addr); | |
804 | ||
805 | return ret; | |
806 | } | |
807 | ||
10078321 | 808 | static ssize_t lbs_rdrf_write(struct file *file, |
876c9d3a MT |
809 | const char __user *userbuf, |
810 | size_t count, loff_t *ppos) | |
811 | { | |
69f9032d | 812 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
813 | ssize_t res, buf_size; |
814 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
815 | char *buf = (char *)addr; | |
816 | ||
817 | buf_size = min(count, len - 1); | |
818 | if (copy_from_user(buf, userbuf, buf_size)) { | |
819 | res = -EFAULT; | |
820 | goto out_unlock; | |
821 | } | |
822 | priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); | |
823 | res = count; | |
824 | out_unlock: | |
825 | free_page(addr); | |
826 | return res; | |
827 | } | |
828 | ||
10078321 | 829 | static ssize_t lbs_wrrf_write(struct file *file, |
876c9d3a MT |
830 | const char __user *userbuf, |
831 | size_t count, loff_t *ppos) | |
832 | { | |
833 | ||
69f9032d | 834 | struct lbs_private *priv = file->private_data; |
876c9d3a MT |
835 | ssize_t res, buf_size; |
836 | u32 offset, value; | |
10078321 | 837 | struct lbs_offset_value offval; |
876c9d3a MT |
838 | unsigned long addr = get_zeroed_page(GFP_KERNEL); |
839 | char *buf = (char *)addr; | |
840 | ||
841 | buf_size = min(count, len - 1); | |
842 | if (copy_from_user(buf, userbuf, buf_size)) { | |
843 | res = -EFAULT; | |
844 | goto out_unlock; | |
845 | } | |
846 | res = sscanf(buf, "%x %x", &offset, &value); | |
847 | if (res != 2) { | |
848 | res = -EFAULT; | |
849 | goto out_unlock; | |
850 | } | |
851 | ||
852 | offval.offset = offset; | |
853 | offval.value = value; | |
10078321 | 854 | res = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
855 | CMD_RF_REG_ACCESS, 1, |
856 | CMD_OPTION_WAITFORRSP, 0, &offval); | |
876c9d3a MT |
857 | mdelay(10); |
858 | ||
859 | res = count; | |
860 | out_unlock: | |
861 | free_page(addr); | |
862 | return res; | |
863 | } | |
864 | ||
865 | #define FOPS(fread, fwrite) { \ | |
866 | .owner = THIS_MODULE, \ | |
867 | .open = open_file_generic, \ | |
868 | .read = (fread), \ | |
869 | .write = (fwrite), \ | |
870 | } | |
871 | ||
10078321 | 872 | struct lbs_debugfs_files { |
876c9d3a MT |
873 | char *name; |
874 | int perm; | |
875 | struct file_operations fops; | |
876 | }; | |
877 | ||
10078321 HS |
878 | static struct lbs_debugfs_files debugfs_files[] = { |
879 | { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, | |
880 | { "getscantable", 0444, FOPS(lbs_getscantable, | |
876c9d3a | 881 | write_file_dummy), }, |
10078321 HS |
882 | { "sleepparams", 0644, FOPS(lbs_sleepparams_read, |
883 | lbs_sleepparams_write), }, | |
884 | { "extscan", 0600, FOPS(NULL, lbs_extscan), }, | |
885 | { "setuserscan", 0600, FOPS(NULL, lbs_setuserscan), }, | |
876c9d3a MT |
886 | }; |
887 | ||
10078321 HS |
888 | static struct lbs_debugfs_files debugfs_events_files[] = { |
889 | {"low_rssi", 0644, FOPS(lbs_lowrssi_read, | |
890 | lbs_lowrssi_write), }, | |
891 | {"low_snr", 0644, FOPS(lbs_lowsnr_read, | |
892 | lbs_lowsnr_write), }, | |
893 | {"failure_count", 0644, FOPS(lbs_failcount_read, | |
894 | lbs_failcount_write), }, | |
895 | {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, | |
896 | lbs_bcnmiss_write), }, | |
897 | {"high_rssi", 0644, FOPS(lbs_highrssi_read, | |
898 | lbs_highrssi_write), }, | |
899 | {"high_snr", 0644, FOPS(lbs_highsnr_read, | |
900 | lbs_highsnr_write), }, | |
876c9d3a MT |
901 | }; |
902 | ||
10078321 HS |
903 | static struct lbs_debugfs_files debugfs_regs_files[] = { |
904 | {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, | |
905 | {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, | |
906 | {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, | |
907 | {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, | |
908 | {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, | |
909 | {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, | |
876c9d3a MT |
910 | }; |
911 | ||
10078321 | 912 | void lbs_debugfs_init(void) |
876c9d3a | 913 | { |
10078321 HS |
914 | if (!lbs_dir) |
915 | lbs_dir = debugfs_create_dir("lbs_wireless", NULL); | |
876c9d3a MT |
916 | |
917 | return; | |
918 | } | |
919 | ||
10078321 | 920 | void lbs_debugfs_remove(void) |
876c9d3a | 921 | { |
10078321 HS |
922 | if (lbs_dir) |
923 | debugfs_remove(lbs_dir); | |
876c9d3a MT |
924 | return; |
925 | } | |
926 | ||
69f9032d | 927 | void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) |
876c9d3a MT |
928 | { |
929 | int i; | |
10078321 HS |
930 | struct lbs_debugfs_files *files; |
931 | if (!lbs_dir) | |
876c9d3a MT |
932 | goto exit; |
933 | ||
10078321 | 934 | priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); |
876c9d3a MT |
935 | if (!priv->debugfs_dir) |
936 | goto exit; | |
937 | ||
938 | for (i=0; i<ARRAY_SIZE(debugfs_files); i++) { | |
939 | files = &debugfs_files[i]; | |
940 | priv->debugfs_files[i] = debugfs_create_file(files->name, | |
941 | files->perm, | |
942 | priv->debugfs_dir, | |
943 | priv, | |
944 | &files->fops); | |
945 | } | |
946 | ||
947 | priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); | |
948 | if (!priv->events_dir) | |
949 | goto exit; | |
950 | ||
951 | for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) { | |
952 | files = &debugfs_events_files[i]; | |
953 | priv->debugfs_events_files[i] = debugfs_create_file(files->name, | |
954 | files->perm, | |
955 | priv->events_dir, | |
956 | priv, | |
957 | &files->fops); | |
958 | } | |
959 | ||
960 | priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); | |
961 | if (!priv->regs_dir) | |
962 | goto exit; | |
963 | ||
964 | for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) { | |
965 | files = &debugfs_regs_files[i]; | |
966 | priv->debugfs_regs_files[i] = debugfs_create_file(files->name, | |
967 | files->perm, | |
968 | priv->regs_dir, | |
969 | priv, | |
970 | &files->fops); | |
971 | } | |
972 | ||
973 | #ifdef PROC_DEBUG | |
10078321 | 974 | lbs_debug_init(priv, dev); |
876c9d3a MT |
975 | #endif |
976 | exit: | |
977 | return; | |
978 | } | |
979 | ||
69f9032d | 980 | void lbs_debugfs_remove_one(struct lbs_private *priv) |
876c9d3a MT |
981 | { |
982 | int i; | |
983 | ||
984 | for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) | |
985 | debugfs_remove(priv->debugfs_regs_files[i]); | |
986 | ||
987 | debugfs_remove(priv->regs_dir); | |
988 | ||
0b7db956 | 989 | for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++) |
876c9d3a MT |
990 | debugfs_remove(priv->debugfs_events_files[i]); |
991 | ||
992 | debugfs_remove(priv->events_dir); | |
993 | #ifdef PROC_DEBUG | |
994 | debugfs_remove(priv->debugfs_debug); | |
995 | #endif | |
996 | for(i=0; i<ARRAY_SIZE(debugfs_files); i++) | |
997 | debugfs_remove(priv->debugfs_files[i]); | |
0b7db956 | 998 | debugfs_remove(priv->debugfs_dir); |
876c9d3a MT |
999 | } |
1000 | ||
46868202 HS |
1001 | |
1002 | ||
876c9d3a MT |
1003 | /* debug entry */ |
1004 | ||
46868202 HS |
1005 | #ifdef PROC_DEBUG |
1006 | ||
aa21c004 DW |
1007 | #define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) |
1008 | #define item_addr(n) (offsetof(struct lbs_private, n)) | |
876c9d3a | 1009 | |
46868202 | 1010 | |
876c9d3a MT |
1011 | struct debug_data { |
1012 | char name[32]; | |
1013 | u32 size; | |
4269e2ad | 1014 | size_t addr; |
876c9d3a MT |
1015 | }; |
1016 | ||
aa21c004 | 1017 | /* To debug any member of struct lbs_private, simply add one line here. |
876c9d3a MT |
1018 | */ |
1019 | static struct debug_data items[] = { | |
1020 | {"intcounter", item_size(intcounter), item_addr(intcounter)}, | |
1021 | {"psmode", item_size(psmode), item_addr(psmode)}, | |
1022 | {"psstate", item_size(psstate), item_addr(psstate)}, | |
1023 | }; | |
1024 | ||
d2f11e09 | 1025 | static int num_of_items = ARRAY_SIZE(items); |
876c9d3a MT |
1026 | |
1027 | /** | |
1028 | * @brief proc read function | |
1029 | * | |
1030 | * @param page pointer to buffer | |
1031 | * @param s read data starting position | |
1032 | * @param off offset | |
1033 | * @param cnt counter | |
1034 | * @param eof end of file flag | |
1035 | * @param data data to output | |
1036 | * @return number of output data | |
1037 | */ | |
10078321 | 1038 | static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, |
876c9d3a MT |
1039 | size_t count, loff_t *ppos) |
1040 | { | |
1041 | int val = 0; | |
1042 | size_t pos = 0; | |
1043 | ssize_t res; | |
1044 | char *p; | |
1045 | int i; | |
1046 | struct debug_data *d; | |
1047 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | |
1048 | char *buf = (char *)addr; | |
1049 | ||
1050 | p = buf; | |
1051 | ||
1052 | d = (struct debug_data *)file->private_data; | |
1053 | ||
1054 | for (i = 0; i < num_of_items; i++) { | |
1055 | if (d[i].size == 1) | |
1056 | val = *((u8 *) d[i].addr); | |
1057 | else if (d[i].size == 2) | |
1058 | val = *((u16 *) d[i].addr); | |
1059 | else if (d[i].size == 4) | |
1060 | val = *((u32 *) d[i].addr); | |
4269e2ad DW |
1061 | else if (d[i].size == 8) |
1062 | val = *((u64 *) d[i].addr); | |
876c9d3a MT |
1063 | |
1064 | pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); | |
1065 | } | |
1066 | ||
1067 | res = simple_read_from_buffer(userbuf, count, ppos, p, pos); | |
1068 | ||
1069 | free_page(addr); | |
1070 | return res; | |
1071 | } | |
1072 | ||
1073 | /** | |
1074 | * @brief proc write function | |
1075 | * | |
1076 | * @param f file pointer | |
1077 | * @param buf pointer to data buffer | |
1078 | * @param cnt data number to write | |
1079 | * @param data data to write | |
1080 | * @return number of data | |
1081 | */ | |
10078321 | 1082 | static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, |
876c9d3a MT |
1083 | size_t cnt, loff_t *ppos) |
1084 | { | |
1085 | int r, i; | |
1086 | char *pdata; | |
1087 | char *p; | |
1088 | char *p0; | |
1089 | char *p1; | |
1090 | char *p2; | |
1091 | struct debug_data *d = (struct debug_data *)f->private_data; | |
1092 | ||
655b4d16 | 1093 | pdata = kmalloc(cnt, GFP_KERNEL); |
876c9d3a MT |
1094 | if (pdata == NULL) |
1095 | return 0; | |
1096 | ||
1097 | if (copy_from_user(pdata, buf, cnt)) { | |
9012b28a | 1098 | lbs_deb_debugfs("Copy from user failed\n"); |
876c9d3a MT |
1099 | kfree(pdata); |
1100 | return 0; | |
1101 | } | |
1102 | ||
1103 | p0 = pdata; | |
1104 | for (i = 0; i < num_of_items; i++) { | |
1105 | do { | |
1106 | p = strstr(p0, d[i].name); | |
1107 | if (p == NULL) | |
1108 | break; | |
1109 | p1 = strchr(p, '\n'); | |
1110 | if (p1 == NULL) | |
1111 | break; | |
1112 | p0 = p1++; | |
1113 | p2 = strchr(p, '='); | |
1114 | if (!p2) | |
1115 | break; | |
1116 | p2++; | |
d2f11e09 | 1117 | r = simple_strtoul(p2, NULL, 0); |
876c9d3a MT |
1118 | if (d[i].size == 1) |
1119 | *((u8 *) d[i].addr) = (u8) r; | |
1120 | else if (d[i].size == 2) | |
1121 | *((u16 *) d[i].addr) = (u16) r; | |
1122 | else if (d[i].size == 4) | |
1123 | *((u32 *) d[i].addr) = (u32) r; | |
4269e2ad DW |
1124 | else if (d[i].size == 8) |
1125 | *((u64 *) d[i].addr) = (u64) r; | |
876c9d3a MT |
1126 | break; |
1127 | } while (1); | |
1128 | } | |
1129 | kfree(pdata); | |
1130 | ||
4269e2ad | 1131 | return (ssize_t)cnt; |
876c9d3a MT |
1132 | } |
1133 | ||
10078321 | 1134 | static struct file_operations lbs_debug_fops = { |
876c9d3a MT |
1135 | .owner = THIS_MODULE, |
1136 | .open = open_file_generic, | |
10078321 HS |
1137 | .write = lbs_debugfs_write, |
1138 | .read = lbs_debugfs_read, | |
876c9d3a MT |
1139 | }; |
1140 | ||
1141 | /** | |
1142 | * @brief create debug proc file | |
1143 | * | |
69f9032d | 1144 | * @param priv pointer struct lbs_private |
876c9d3a MT |
1145 | * @param dev pointer net_device |
1146 | * @return N/A | |
1147 | */ | |
69f9032d | 1148 | static void lbs_debug_init(struct lbs_private *priv, struct net_device *dev) |
876c9d3a MT |
1149 | { |
1150 | int i; | |
1151 | ||
1152 | if (!priv->debugfs_dir) | |
1153 | return; | |
1154 | ||
1155 | for (i = 0; i < num_of_items; i++) | |
aa21c004 | 1156 | items[i].addr += (size_t) priv; |
876c9d3a MT |
1157 | |
1158 | priv->debugfs_debug = debugfs_create_file("debug", 0644, | |
1159 | priv->debugfs_dir, &items[0], | |
10078321 | 1160 | &lbs_debug_fops); |
876c9d3a | 1161 | } |
46868202 | 1162 | #endif |