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