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