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