]>
Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /** |
2 | * This file contains ioctl functions | |
3 | */ | |
4 | ||
5 | #include <linux/ctype.h> | |
6 | #include <linux/delay.h> | |
7 | #include <linux/if.h> | |
8 | #include <linux/if_arp.h> | |
9 | #include <linux/wireless.h> | |
10 | ||
11 | #include <net/iw_handler.h> | |
12 | #include <net/ieee80211.h> | |
13 | ||
14 | #include "host.h" | |
15 | #include "radiotap.h" | |
16 | #include "decl.h" | |
17 | #include "defs.h" | |
18 | #include "dev.h" | |
19 | #include "join.h" | |
20 | #include "wext.h" | |
21 | ||
22 | #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ | |
23 | IW_ESSID_MAX_SIZE + \ | |
24 | IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ | |
25 | IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ | |
26 | IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ | |
27 | ||
28 | #define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) | |
29 | ||
4b19fcc3 | 30 | static int wlan_set_region(wlan_private * priv, u16 region_code) |
876c9d3a | 31 | { |
4b19fcc3 | 32 | int i; |
9012b28a | 33 | int ret = 0; |
876c9d3a | 34 | |
4b19fcc3 DW |
35 | for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { |
36 | // use the region code to search for the index | |
37 | if (region_code == libertas_region_code_to_index[i]) { | |
38 | priv->adapter->regiontableindex = (u16) i; | |
39 | priv->adapter->regioncode = region_code; | |
876c9d3a | 40 | break; |
876c9d3a | 41 | } |
876c9d3a MT |
42 | } |
43 | ||
4b19fcc3 DW |
44 | // if it's unidentified region code |
45 | if (i >= MRVDRV_MAX_REGION_CODE) { | |
9012b28a HS |
46 | lbs_deb_ioctl("region Code not identified\n"); |
47 | ret = -1; | |
48 | goto done; | |
4b19fcc3 | 49 | } |
876c9d3a | 50 | |
4b19fcc3 | 51 | if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { |
9012b28a | 52 | ret = -EINVAL; |
876c9d3a | 53 | } |
4b19fcc3 | 54 | |
9012b28a HS |
55 | done: |
56 | lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret); | |
57 | return ret; | |
876c9d3a MT |
58 | } |
59 | ||
60 | static inline int hex2int(char c) | |
61 | { | |
62 | if (c >= '0' && c <= '9') | |
63 | return (c - '0'); | |
64 | if (c >= 'a' && c <= 'f') | |
65 | return (c - 'a' + 10); | |
66 | if (c >= 'A' && c <= 'F') | |
67 | return (c - 'A' + 10); | |
68 | return -1; | |
69 | } | |
70 | ||
71 | /* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") | |
72 | into binary format (6 bytes). | |
73 | ||
74 | This function expects that each byte is represented with 2 characters | |
75 | (e.g., 11:2:11:11:11:11 is invalid) | |
76 | ||
77 | */ | |
78 | static char *eth_str2addr(char *ethstr, u8 * addr) | |
79 | { | |
80 | int i, val, val2; | |
81 | char *pos = ethstr; | |
82 | ||
83 | /* get rid of initial blanks */ | |
84 | while (*pos == ' ' || *pos == '\t') | |
85 | ++pos; | |
86 | ||
87 | for (i = 0; i < 6; i++) { | |
88 | val = hex2int(*pos++); | |
89 | if (val < 0) | |
90 | return NULL; | |
91 | val2 = hex2int(*pos++); | |
92 | if (val2 < 0) | |
93 | return NULL; | |
94 | addr[i] = (val * 16 + val2) & 0xff; | |
95 | ||
96 | if (i < 5 && *pos++ != ':') | |
97 | return NULL; | |
98 | } | |
99 | return pos; | |
100 | } | |
101 | ||
102 | /* this writes xx:xx:xx:xx:xx:xx into ethstr | |
103 | (ethstr must have space for 18 chars) */ | |
104 | static int eth_addr2str(u8 * addr, char *ethstr) | |
105 | { | |
106 | int i; | |
107 | char *pos = ethstr; | |
108 | ||
109 | for (i = 0; i < 6; i++) { | |
110 | sprintf(pos, "%02x", addr[i] & 0xff); | |
111 | pos += 2; | |
112 | if (i < 5) | |
113 | *pos++ = ':'; | |
114 | } | |
115 | return 17; | |
116 | } | |
117 | ||
118 | /** | |
119 | * @brief Add an entry to the BT table | |
120 | * @param priv A pointer to wlan_private structure | |
121 | * @param req A pointer to ifreq structure | |
122 | * @return 0 --success, otherwise fail | |
123 | */ | |
124 | static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) | |
125 | { | |
126 | struct iwreq *wrq = (struct iwreq *)req; | |
127 | char ethaddrs_str[18]; | |
128 | char *pos; | |
129 | u8 ethaddr[ETH_ALEN]; | |
9012b28a HS |
130 | int ret; |
131 | ||
132 | lbs_deb_enter(LBS_DEB_IOCTL); | |
876c9d3a | 133 | |
876c9d3a MT |
134 | if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, |
135 | sizeof(ethaddrs_str))) | |
136 | return -EFAULT; | |
137 | ||
138 | if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { | |
139 | lbs_pr_info("BT_ADD: Invalid MAC address\n"); | |
140 | return -EINVAL; | |
141 | } | |
142 | ||
9012b28a HS |
143 | lbs_deb_ioctl("BT: adding %s\n", ethaddrs_str); |
144 | ret = libertas_prepare_and_send_command(priv, cmd_bt_access, | |
876c9d3a | 145 | cmd_act_bt_access_add, |
9012b28a HS |
146 | cmd_option_waitforrsp, 0, ethaddr); |
147 | lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret); | |
148 | return ret; | |
876c9d3a MT |
149 | } |
150 | ||
151 | /** | |
152 | * @brief Delete an entry from the BT table | |
153 | * @param priv A pointer to wlan_private structure | |
154 | * @param req A pointer to ifreq structure | |
155 | * @return 0 --success, otherwise fail | |
156 | */ | |
157 | static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) | |
158 | { | |
159 | struct iwreq *wrq = (struct iwreq *)req; | |
160 | char ethaddrs_str[18]; | |
161 | u8 ethaddr[ETH_ALEN]; | |
162 | char *pos; | |
163 | ||
9012b28a HS |
164 | lbs_deb_enter(LBS_DEB_IOCTL); |
165 | ||
876c9d3a MT |
166 | if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, |
167 | sizeof(ethaddrs_str))) | |
168 | return -EFAULT; | |
169 | ||
170 | if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { | |
171 | lbs_pr_info("Invalid MAC address\n"); | |
172 | return -EINVAL; | |
173 | } | |
174 | ||
9012b28a | 175 | lbs_deb_ioctl("BT: deleting %s\n", ethaddrs_str); |
876c9d3a MT |
176 | |
177 | return (libertas_prepare_and_send_command(priv, | |
178 | cmd_bt_access, | |
179 | cmd_act_bt_access_del, | |
180 | cmd_option_waitforrsp, 0, ethaddr)); | |
9012b28a HS |
181 | |
182 | lbs_deb_leave(LBS_DEB_IOCTL); | |
876c9d3a MT |
183 | return 0; |
184 | } | |
185 | ||
186 | /** | |
187 | * @brief Reset all entries from the BT table | |
188 | * @param priv A pointer to wlan_private structure | |
189 | * @return 0 --success, otherwise fail | |
190 | */ | |
191 | static int wlan_bt_reset_ioctl(wlan_private * priv) | |
192 | { | |
9012b28a | 193 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a MT |
194 | |
195 | lbs_pr_alert( "BT: resetting\n"); | |
196 | ||
197 | return (libertas_prepare_and_send_command(priv, | |
198 | cmd_bt_access, | |
199 | cmd_act_bt_access_reset, | |
200 | cmd_option_waitforrsp, 0, NULL)); | |
201 | ||
9012b28a | 202 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
203 | return 0; |
204 | } | |
205 | ||
206 | /** | |
207 | * @brief List an entry from the BT table | |
208 | * @param priv A pointer to wlan_private structure | |
209 | * @param req A pointer to ifreq structure | |
210 | * @return 0 --success, otherwise fail | |
211 | */ | |
212 | static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) | |
213 | { | |
214 | int pos; | |
215 | char *addr1; | |
216 | struct iwreq *wrq = (struct iwreq *)req; | |
217 | /* used to pass id and store the bt entry returned by the FW */ | |
218 | union { | |
219 | int id; | |
220 | char addr1addr2[2 * ETH_ALEN]; | |
221 | } param; | |
222 | static char outstr[64]; | |
223 | char *pbuf = outstr; | |
224 | int ret; | |
225 | ||
9012b28a | 226 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a MT |
227 | |
228 | if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { | |
9012b28a | 229 | lbs_deb_ioctl("Copy from user failed\n"); |
876c9d3a MT |
230 | return -1; |
231 | } | |
232 | param.id = simple_strtoul(outstr, NULL, 10); | |
233 | pos = sprintf(pbuf, "%d: ", param.id); | |
234 | pbuf += pos; | |
235 | ||
236 | ret = libertas_prepare_and_send_command(priv, cmd_bt_access, | |
237 | cmd_act_bt_access_list, | |
238 | cmd_option_waitforrsp, 0, | |
239 | (char *)¶m); | |
240 | ||
241 | if (ret == 0) { | |
242 | addr1 = param.addr1addr2; | |
243 | ||
244 | pos = sprintf(pbuf, "ignoring traffic from "); | |
245 | pbuf += pos; | |
246 | pos = eth_addr2str(addr1, pbuf); | |
247 | pbuf += pos; | |
248 | } else { | |
249 | sprintf(pbuf, "(null)"); | |
250 | pbuf += pos; | |
251 | } | |
252 | ||
253 | wrq->u.data.length = strlen(outstr); | |
254 | if (copy_to_user(wrq->u.data.pointer, (char *)outstr, | |
255 | wrq->u.data.length)) { | |
9012b28a | 256 | lbs_deb_ioctl("BT_LIST: Copy to user failed!\n"); |
876c9d3a MT |
257 | return -EFAULT; |
258 | } | |
259 | ||
9012b28a | 260 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
261 | return 0; |
262 | } | |
263 | ||
264 | /** | |
265 | * @brief Find the next parameter in an input string | |
266 | * @param ptr A pointer to the input parameter string | |
267 | * @return A pointer to the next parameter, or 0 if no parameters left. | |
268 | */ | |
269 | static char * next_param(char * ptr) | |
270 | { | |
271 | if (!ptr) return NULL; | |
272 | while (*ptr == ' ' || *ptr == '\t') ++ptr; | |
273 | return (*ptr == '\0') ? NULL : ptr; | |
274 | } | |
275 | ||
276 | /** | |
277 | * @brief Add an entry to the FWT table | |
278 | * @param priv A pointer to wlan_private structure | |
279 | * @param req A pointer to ifreq structure | |
280 | * @return 0 --success, otherwise fail | |
281 | */ | |
282 | static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) | |
283 | { | |
284 | struct iwreq *wrq = (struct iwreq *)req; | |
285 | char in_str[128]; | |
286 | static struct cmd_ds_fwt_access fwt_access; | |
287 | char *ptr; | |
9012b28a HS |
288 | int ret; |
289 | ||
290 | lbs_deb_enter(LBS_DEB_IOCTL); | |
876c9d3a | 291 | |
876c9d3a MT |
292 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
293 | return -EFAULT; | |
294 | ||
295 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | |
296 | lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); | |
297 | return -EINVAL; | |
298 | } | |
299 | ||
300 | if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { | |
301 | lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); | |
302 | return -EINVAL; | |
303 | } | |
304 | ||
305 | if ((ptr = next_param(ptr))) | |
306 | fwt_access.metric = | |
307 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
308 | else | |
309 | fwt_access.metric = FWT_DEFAULT_METRIC; | |
310 | ||
311 | if ((ptr = next_param(ptr))) | |
312 | fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); | |
313 | else | |
314 | fwt_access.dir = FWT_DEFAULT_DIR; | |
315 | ||
316 | if ((ptr = next_param(ptr))) | |
317 | fwt_access.ssn = | |
318 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
319 | else | |
320 | fwt_access.ssn = FWT_DEFAULT_SSN; | |
321 | ||
322 | if ((ptr = next_param(ptr))) | |
323 | fwt_access.dsn = | |
324 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
325 | else | |
326 | fwt_access.dsn = FWT_DEFAULT_DSN; | |
327 | ||
328 | if ((ptr = next_param(ptr))) | |
329 | fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); | |
330 | else | |
331 | fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; | |
332 | ||
333 | if ((ptr = next_param(ptr))) | |
334 | fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); | |
335 | else | |
336 | fwt_access.ttl = FWT_DEFAULT_TTL; | |
337 | ||
338 | if ((ptr = next_param(ptr))) | |
339 | fwt_access.expiration = | |
340 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
341 | else | |
342 | fwt_access.expiration = FWT_DEFAULT_EXPIRATION; | |
343 | ||
344 | if ((ptr = next_param(ptr))) | |
345 | fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); | |
346 | else | |
347 | fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; | |
348 | ||
349 | if ((ptr = next_param(ptr))) | |
350 | fwt_access.snr = | |
351 | cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
352 | else | |
353 | fwt_access.snr = FWT_DEFAULT_SNR; | |
354 | ||
355 | #ifdef DEBUG | |
356 | { | |
357 | char ethaddr1_str[18], ethaddr2_str[18]; | |
358 | eth_addr2str(fwt_access.da, ethaddr1_str); | |
359 | eth_addr2str(fwt_access.ra, ethaddr2_str); | |
9012b28a | 360 | lbs_deb_ioctl("FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, |
876c9d3a | 361 | fwt_access.dir, ethaddr2_str); |
9012b28a | 362 | lbs_deb_ioctl("FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", |
876c9d3a MT |
363 | fwt_access.ssn, fwt_access.dsn, fwt_access.metric, |
364 | fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, | |
365 | fwt_access.sleepmode, fwt_access.snr); | |
366 | } | |
367 | #endif | |
368 | ||
9012b28a HS |
369 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, |
370 | cmd_act_fwt_access_add, | |
371 | cmd_option_waitforrsp, 0, | |
372 | (void *)&fwt_access); | |
373 | ||
374 | lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret); | |
375 | return ret; | |
876c9d3a MT |
376 | } |
377 | ||
378 | /** | |
379 | * @brief Delete an entry from the FWT table | |
380 | * @param priv A pointer to wlan_private structure | |
381 | * @param req A pointer to ifreq structure | |
382 | * @return 0 --success, otherwise fail | |
383 | */ | |
384 | static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) | |
385 | { | |
386 | struct iwreq *wrq = (struct iwreq *)req; | |
387 | char in_str[64]; | |
388 | static struct cmd_ds_fwt_access fwt_access; | |
389 | char *ptr; | |
9012b28a HS |
390 | int ret; |
391 | ||
392 | lbs_deb_enter(LBS_DEB_IOCTL); | |
876c9d3a | 393 | |
876c9d3a MT |
394 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
395 | return -EFAULT; | |
396 | ||
397 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | |
398 | lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); | |
399 | return -EINVAL; | |
400 | } | |
401 | ||
402 | if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { | |
403 | lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); | |
404 | return -EINVAL; | |
405 | } | |
406 | ||
407 | if ((ptr = next_param(ptr))) | |
408 | fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); | |
409 | else | |
410 | fwt_access.dir = FWT_DEFAULT_DIR; | |
411 | ||
412 | #ifdef DEBUG | |
413 | { | |
414 | char ethaddr1_str[18], ethaddr2_str[18]; | |
9012b28a | 415 | lbs_deb_ioctl("FWT_DEL: line is %s\n", in_str); |
876c9d3a MT |
416 | eth_addr2str(fwt_access.da, ethaddr1_str); |
417 | eth_addr2str(fwt_access.ra, ethaddr2_str); | |
9012b28a | 418 | lbs_deb_ioctl("FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, |
876c9d3a MT |
419 | ethaddr2_str, fwt_access.dir); |
420 | } | |
421 | #endif | |
422 | ||
9012b28a HS |
423 | ret = libertas_prepare_and_send_command(priv, |
424 | cmd_fwt_access, | |
425 | cmd_act_fwt_access_del, | |
426 | cmd_option_waitforrsp, 0, | |
427 | (void *)&fwt_access); | |
428 | lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret); | |
429 | return ret; | |
876c9d3a MT |
430 | } |
431 | ||
432 | ||
433 | /** | |
434 | * @brief Print route parameters | |
435 | * @param fwt_access struct cmd_ds_fwt_access with route info | |
436 | * @param buf destination buffer for route info | |
437 | */ | |
438 | static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) | |
439 | { | |
440 | buf += sprintf(buf, " "); | |
441 | buf += eth_addr2str(fwt_access.da, buf); | |
442 | buf += sprintf(buf, " "); | |
443 | buf += eth_addr2str(fwt_access.ra, buf); | |
444 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); | |
445 | buf += sprintf(buf, " %u", fwt_access.dir); | |
446 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); | |
447 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); | |
448 | buf += sprintf(buf, " %u", fwt_access.hopcount); | |
449 | buf += sprintf(buf, " %u", fwt_access.ttl); | |
450 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); | |
451 | buf += sprintf(buf, " %u", fwt_access.sleepmode); | |
452 | buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); | |
453 | } | |
454 | ||
455 | /** | |
456 | * @brief Lookup an entry in the FWT table | |
457 | * @param priv A pointer to wlan_private structure | |
458 | * @param req A pointer to ifreq structure | |
459 | * @return 0 --success, otherwise fail | |
460 | */ | |
461 | static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) | |
462 | { | |
463 | struct iwreq *wrq = (struct iwreq *)req; | |
464 | char in_str[64]; | |
465 | char *ptr; | |
466 | static struct cmd_ds_fwt_access fwt_access; | |
467 | static char out_str[128]; | |
468 | int ret; | |
469 | ||
9012b28a HS |
470 | lbs_deb_enter(LBS_DEB_IOCTL); |
471 | ||
876c9d3a MT |
472 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
473 | return -EFAULT; | |
474 | ||
475 | if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { | |
476 | lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); | |
477 | return -EINVAL; | |
478 | } | |
479 | ||
480 | #ifdef DEBUG | |
481 | { | |
482 | char ethaddr1_str[18]; | |
9012b28a | 483 | lbs_deb_ioctl("FWT_LOOKUP: line is %s\n", in_str); |
876c9d3a | 484 | eth_addr2str(fwt_access.da, ethaddr1_str); |
9012b28a | 485 | lbs_deb_ioctl("FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); |
876c9d3a MT |
486 | } |
487 | #endif | |
488 | ||
489 | ret = libertas_prepare_and_send_command(priv, | |
490 | cmd_fwt_access, | |
491 | cmd_act_fwt_access_lookup, | |
492 | cmd_option_waitforrsp, 0, | |
493 | (void *)&fwt_access); | |
494 | ||
495 | if (ret == 0) | |
496 | print_route(fwt_access, out_str); | |
497 | else | |
498 | sprintf(out_str, "(null)"); | |
499 | ||
500 | wrq->u.data.length = strlen(out_str); | |
501 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | |
502 | wrq->u.data.length)) { | |
9012b28a | 503 | lbs_deb_ioctl("FWT_LOOKUP: Copy to user failed!\n"); |
876c9d3a MT |
504 | return -EFAULT; |
505 | } | |
506 | ||
9012b28a | 507 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
508 | return 0; |
509 | } | |
510 | ||
511 | /** | |
512 | * @brief Reset all entries from the FWT table | |
513 | * @param priv A pointer to wlan_private structure | |
514 | * @return 0 --success, otherwise fail | |
515 | */ | |
516 | static int wlan_fwt_reset_ioctl(wlan_private * priv) | |
517 | { | |
9012b28a | 518 | lbs_deb_ioctl("FWT: resetting\n"); |
876c9d3a MT |
519 | |
520 | return (libertas_prepare_and_send_command(priv, | |
521 | cmd_fwt_access, | |
522 | cmd_act_fwt_access_reset, | |
523 | cmd_option_waitforrsp, 0, NULL)); | |
524 | } | |
525 | ||
526 | /** | |
527 | * @brief List an entry from the FWT table | |
528 | * @param priv A pointer to wlan_private structure | |
529 | * @param req A pointer to ifreq structure | |
530 | * @return 0 --success, otherwise fail | |
531 | */ | |
532 | static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) | |
533 | { | |
534 | struct iwreq *wrq = (struct iwreq *)req; | |
535 | char in_str[8]; | |
536 | static struct cmd_ds_fwt_access fwt_access; | |
537 | char *ptr = in_str; | |
538 | static char out_str[128]; | |
539 | char *pbuf = out_str; | |
540 | int ret; | |
541 | ||
9012b28a HS |
542 | lbs_deb_enter(LBS_DEB_IOCTL); |
543 | ||
876c9d3a MT |
544 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
545 | return -EFAULT; | |
546 | ||
547 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
548 | ||
549 | #ifdef DEBUG | |
550 | { | |
9012b28a HS |
551 | lbs_deb_ioctl("FWT_LIST: line is %s\n", in_str); |
552 | lbs_deb_ioctl("FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); | |
876c9d3a MT |
553 | } |
554 | #endif | |
555 | ||
556 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | |
557 | cmd_act_fwt_access_list, | |
558 | cmd_option_waitforrsp, 0, (void *)&fwt_access); | |
559 | ||
560 | if (ret == 0) | |
561 | print_route(fwt_access, pbuf); | |
562 | else | |
563 | pbuf += sprintf(pbuf, " (null)"); | |
564 | ||
565 | wrq->u.data.length = strlen(out_str); | |
566 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | |
567 | wrq->u.data.length)) { | |
9012b28a | 568 | lbs_deb_ioctl("FWT_LIST: Copy to user failed!\n"); |
876c9d3a MT |
569 | return -EFAULT; |
570 | } | |
571 | ||
9012b28a | 572 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
573 | return 0; |
574 | } | |
575 | ||
576 | /** | |
577 | * @brief List an entry from the FRT table | |
578 | * @param priv A pointer to wlan_private structure | |
579 | * @param req A pointer to ifreq structure | |
580 | * @return 0 --success, otherwise fail | |
581 | */ | |
582 | static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) | |
583 | { | |
584 | struct iwreq *wrq = (struct iwreq *)req; | |
585 | char in_str[64]; | |
586 | static struct cmd_ds_fwt_access fwt_access; | |
587 | char *ptr = in_str; | |
588 | static char out_str[128]; | |
589 | char *pbuf = out_str; | |
590 | int ret; | |
591 | ||
9012b28a HS |
592 | lbs_deb_enter(LBS_DEB_IOCTL); |
593 | ||
876c9d3a MT |
594 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
595 | return -EFAULT; | |
596 | ||
597 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
598 | ||
599 | #ifdef DEBUG | |
600 | { | |
9012b28a HS |
601 | lbs_deb_ioctl("FWT_LIST_ROUTE: line is %s\n", in_str); |
602 | lbs_deb_ioctl("FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); | |
876c9d3a MT |
603 | } |
604 | #endif | |
605 | ||
606 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | |
607 | cmd_act_fwt_access_list_route, | |
608 | cmd_option_waitforrsp, 0, (void *)&fwt_access); | |
609 | ||
610 | if (ret == 0) { | |
611 | pbuf += sprintf(pbuf, " "); | |
612 | pbuf += eth_addr2str(fwt_access.da, pbuf); | |
613 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); | |
614 | pbuf += sprintf(pbuf, " %u", fwt_access.dir); | |
615 | /* note that the firmware returns the nid in the id field */ | |
616 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); | |
617 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); | |
618 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); | |
619 | pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); | |
620 | pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); | |
621 | pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); | |
622 | } else | |
623 | pbuf += sprintf(pbuf, " (null)"); | |
624 | ||
625 | wrq->u.data.length = strlen(out_str); | |
626 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | |
627 | wrq->u.data.length)) { | |
9012b28a | 628 | lbs_deb_ioctl("FWT_LIST_ROUTE: Copy to user failed!\n"); |
876c9d3a MT |
629 | return -EFAULT; |
630 | } | |
631 | ||
9012b28a | 632 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
633 | return 0; |
634 | } | |
635 | ||
636 | /** | |
637 | * @brief List an entry from the FNT table | |
638 | * @param priv A pointer to wlan_private structure | |
639 | * @param req A pointer to ifreq structure | |
640 | * @return 0 --success, otherwise fail | |
641 | */ | |
642 | static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) | |
643 | { | |
644 | struct iwreq *wrq = (struct iwreq *)req; | |
645 | char in_str[8]; | |
646 | static struct cmd_ds_fwt_access fwt_access; | |
647 | char *ptr = in_str; | |
648 | static char out_str[128]; | |
649 | char *pbuf = out_str; | |
650 | int ret; | |
651 | ||
9012b28a HS |
652 | lbs_deb_enter(LBS_DEB_IOCTL); |
653 | ||
876c9d3a MT |
654 | if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) |
655 | return -EFAULT; | |
656 | ||
657 | memset(&fwt_access, 0, sizeof(fwt_access)); | |
658 | fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); | |
659 | ||
660 | #ifdef DEBUG | |
661 | { | |
9012b28a HS |
662 | lbs_deb_ioctl("FWT_LIST_NEIGHBOR: line is %s\n", in_str); |
663 | lbs_deb_ioctl("FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); | |
876c9d3a MT |
664 | } |
665 | #endif | |
666 | ||
667 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | |
668 | cmd_act_fwt_access_list_neighbor, | |
669 | cmd_option_waitforrsp, 0, | |
670 | (void *)&fwt_access); | |
671 | ||
672 | if (ret == 0) { | |
673 | pbuf += sprintf(pbuf, " ra "); | |
674 | pbuf += eth_addr2str(fwt_access.ra, pbuf); | |
675 | pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); | |
676 | pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); | |
677 | pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); | |
678 | } else | |
679 | pbuf += sprintf(pbuf, " (null)"); | |
680 | ||
681 | wrq->u.data.length = strlen(out_str); | |
682 | if (copy_to_user(wrq->u.data.pointer, (char *)out_str, | |
683 | wrq->u.data.length)) { | |
9012b28a | 684 | lbs_deb_ioctl("FWT_LIST_NEIGHBOR: Copy to user failed!\n"); |
876c9d3a MT |
685 | return -EFAULT; |
686 | } | |
687 | ||
9012b28a | 688 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
689 | return 0; |
690 | } | |
691 | ||
692 | /** | |
693 | * @brief Cleans up the route (FRT) and neighbor (FNT) tables | |
694 | * (Garbage Collection) | |
695 | * @param priv A pointer to wlan_private structure | |
696 | * @param req A pointer to ifreq structure | |
697 | * @return 0 --success, otherwise fail | |
698 | */ | |
699 | static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) | |
700 | { | |
c4aa7051 | 701 | struct iwreq *wrq = (struct iwreq *)req; |
876c9d3a MT |
702 | static struct cmd_ds_fwt_access fwt_access; |
703 | int ret; | |
704 | ||
9012b28a | 705 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a | 706 | |
9012b28a | 707 | lbs_deb_ioctl("FWT: cleaning up\n"); |
876c9d3a MT |
708 | |
709 | memset(&fwt_access, 0, sizeof(fwt_access)); | |
710 | ||
711 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | |
712 | cmd_act_fwt_access_cleanup, | |
713 | cmd_option_waitforrsp, 0, | |
714 | (void *)&fwt_access); | |
715 | ||
716 | if (ret == 0) | |
c4aa7051 | 717 | wrq->u.param.value = le32_to_cpu(fwt_access.references); |
876c9d3a MT |
718 | else |
719 | return -EFAULT; | |
720 | ||
9012b28a | 721 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
722 | return 0; |
723 | } | |
724 | ||
725 | /** | |
726 | * @brief Gets firmware internal time (debug purposes) | |
727 | * @param priv A pointer to wlan_private structure | |
728 | * @param req A pointer to ifreq structure | |
729 | * @return 0 --success, otherwise fail | |
730 | */ | |
731 | static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) | |
732 | { | |
c4aa7051 | 733 | struct iwreq *wrq = (struct iwreq *)req; |
876c9d3a MT |
734 | static struct cmd_ds_fwt_access fwt_access; |
735 | int ret; | |
736 | ||
9012b28a | 737 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a | 738 | |
9012b28a | 739 | lbs_deb_ioctl("FWT: getting time\n"); |
876c9d3a MT |
740 | |
741 | memset(&fwt_access, 0, sizeof(fwt_access)); | |
742 | ||
743 | ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, | |
744 | cmd_act_fwt_access_time, | |
745 | cmd_option_waitforrsp, 0, | |
746 | (void *)&fwt_access); | |
747 | ||
748 | if (ret == 0) | |
c4aa7051 | 749 | wrq->u.param.value = le32_to_cpu(fwt_access.references); |
876c9d3a MT |
750 | else |
751 | return -EFAULT; | |
752 | ||
9012b28a | 753 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
754 | return 0; |
755 | } | |
756 | ||
757 | /** | |
758 | * @brief Gets mesh ttl from firmware | |
759 | * @param priv A pointer to wlan_private structure | |
760 | * @param req A pointer to ifreq structure | |
761 | * @return 0 --success, otherwise fail | |
762 | */ | |
763 | static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) | |
764 | { | |
c4aa7051 | 765 | struct iwreq *wrq = (struct iwreq *)req; |
876c9d3a MT |
766 | struct cmd_ds_mesh_access mesh_access; |
767 | int ret; | |
768 | ||
9012b28a | 769 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a MT |
770 | |
771 | memset(&mesh_access, 0, sizeof(mesh_access)); | |
772 | ||
773 | ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, | |
774 | cmd_act_mesh_get_ttl, | |
775 | cmd_option_waitforrsp, 0, | |
776 | (void *)&mesh_access); | |
777 | ||
c4aa7051 DW |
778 | if (ret == 0) |
779 | wrq->u.param.value = le32_to_cpu(mesh_access.data[0]); | |
876c9d3a MT |
780 | else |
781 | return -EFAULT; | |
782 | ||
9012b28a | 783 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
784 | return 0; |
785 | } | |
786 | ||
787 | /** | |
788 | * @brief Gets mesh ttl from firmware | |
789 | * @param priv A pointer to wlan_private structure | |
790 | * @param ttl New ttl value | |
791 | * @return 0 --success, otherwise fail | |
792 | */ | |
793 | static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) | |
794 | { | |
795 | struct cmd_ds_mesh_access mesh_access; | |
796 | int ret; | |
797 | ||
9012b28a | 798 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a MT |
799 | |
800 | if( (ttl > 0xff) || (ttl < 0) ) | |
801 | return -EINVAL; | |
802 | ||
803 | memset(&mesh_access, 0, sizeof(mesh_access)); | |
804 | mesh_access.data[0] = ttl; | |
805 | ||
806 | ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, | |
807 | cmd_act_mesh_set_ttl, | |
808 | cmd_option_waitforrsp, 0, | |
809 | (void *)&mesh_access); | |
810 | ||
811 | if (ret != 0) | |
812 | ret = -EFAULT; | |
813 | ||
9012b28a | 814 | lbs_deb_leave(LBS_DEB_IOCTL); |
876c9d3a MT |
815 | return ret; |
816 | } | |
817 | ||
818 | /** | |
819 | * @brief ioctl function - entry point | |
820 | * | |
821 | * @param dev A pointer to net_device structure | |
822 | * @param req A pointer to ifreq structure | |
823 | * @param cmd command | |
824 | * @return 0--success, otherwise fail | |
825 | */ | |
826 | int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) | |
827 | { | |
828 | int subcmd = 0; | |
829 | int idata = 0; | |
830 | int *pdata; | |
831 | int ret = 0; | |
832 | wlan_private *priv = dev->priv; | |
833 | wlan_adapter *adapter = priv->adapter; | |
834 | struct iwreq *wrq = (struct iwreq *)req; | |
835 | ||
9012b28a | 836 | lbs_deb_enter(LBS_DEB_IOCTL); |
876c9d3a | 837 | |
9012b28a | 838 | lbs_deb_ioctl("libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); |
876c9d3a | 839 | switch (cmd) { |
876c9d3a MT |
840 | case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ |
841 | switch (wrq->u.data.flags) { | |
876c9d3a MT |
842 | case WLAN_SUBCMD_BT_RESET: /* bt_reset */ |
843 | wlan_bt_reset_ioctl(priv); | |
844 | break; | |
845 | case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ | |
846 | wlan_fwt_reset_ioctl(priv); | |
847 | break; | |
848 | } /* End of switch */ | |
849 | break; | |
850 | ||
876c9d3a MT |
851 | case WLAN_SETONEINT_GETNONE: |
852 | /* The first 4 bytes of req->ifr_data is sub-ioctl number | |
853 | * after 4 bytes sits the payload. | |
854 | */ | |
c4aa7051 | 855 | subcmd = wrq->u.data.flags; |
876c9d3a | 856 | if (!subcmd) |
c4aa7051 | 857 | subcmd = (int)wrq->u.param.value; |
876c9d3a MT |
858 | |
859 | switch (subcmd) { | |
876c9d3a MT |
860 | case WLANSETREGION: |
861 | idata = SUBCMD_DATA(wrq); | |
862 | ret = wlan_set_region(priv, (u16) idata); | |
863 | break; | |
876c9d3a MT |
864 | case WLAN_SUBCMD_MESH_SET_TTL: |
865 | idata = SUBCMD_DATA(wrq); | |
866 | ret = wlan_mesh_set_ttl_ioctl(priv, idata); | |
867 | break; | |
868 | ||
869 | default: | |
870 | ret = -EOPNOTSUPP; | |
871 | break; | |
872 | } | |
873 | ||
874 | break; | |
875 | ||
876c9d3a MT |
876 | case WLAN_SET128CHAR_GET128CHAR: |
877 | switch ((int)wrq->u.data.flags) { | |
876c9d3a MT |
878 | case WLAN_SUBCMD_BT_ADD: |
879 | ret = wlan_bt_add_ioctl(priv, req); | |
880 | break; | |
881 | case WLAN_SUBCMD_BT_DEL: | |
882 | ret = wlan_bt_del_ioctl(priv, req); | |
883 | break; | |
884 | case WLAN_SUBCMD_BT_LIST: | |
885 | ret = wlan_bt_list_ioctl(priv, req); | |
886 | break; | |
887 | case WLAN_SUBCMD_FWT_ADD: | |
888 | ret = wlan_fwt_add_ioctl(priv, req); | |
889 | break; | |
890 | case WLAN_SUBCMD_FWT_DEL: | |
891 | ret = wlan_fwt_del_ioctl(priv, req); | |
892 | break; | |
893 | case WLAN_SUBCMD_FWT_LOOKUP: | |
894 | ret = wlan_fwt_lookup_ioctl(priv, req); | |
895 | break; | |
896 | case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: | |
897 | ret = wlan_fwt_list_neighbor_ioctl(priv, req); | |
898 | break; | |
899 | case WLAN_SUBCMD_FWT_LIST: | |
900 | ret = wlan_fwt_list_ioctl(priv, req); | |
901 | break; | |
902 | case WLAN_SUBCMD_FWT_LIST_ROUTE: | |
903 | ret = wlan_fwt_list_route_ioctl(priv, req); | |
904 | break; | |
905 | } | |
906 | break; | |
907 | ||
908 | case WLAN_SETNONE_GETONEINT: | |
c4aa7051 | 909 | switch (wrq->u.param.value) { |
876c9d3a MT |
910 | case WLANGETREGION: |
911 | pdata = (int *)wrq->u.name; | |
912 | *pdata = (int)adapter->regioncode; | |
913 | break; | |
876c9d3a MT |
914 | case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ |
915 | ret = wlan_fwt_cleanup_ioctl(priv, req); | |
916 | break; | |
917 | ||
918 | case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ | |
919 | ret = wlan_fwt_time_ioctl(priv, req); | |
920 | break; | |
921 | ||
922 | case WLAN_SUBCMD_MESH_GET_TTL: | |
923 | ret = wlan_mesh_get_ttl_ioctl(priv, req); | |
924 | break; | |
925 | ||
926 | default: | |
927 | ret = -EOPNOTSUPP; | |
928 | ||
929 | } | |
930 | ||
931 | break; | |
932 | ||
876c9d3a MT |
933 | case WLAN_SET_GET_SIXTEEN_INT: |
934 | switch ((int)wrq->u.data.flags) { | |
876c9d3a MT |
935 | case WLAN_LED_GPIO_CTRL: |
936 | { | |
937 | int i; | |
938 | int data[16]; | |
939 | ||
940 | struct cmd_ds_802_11_led_ctrl ctrl; | |
941 | struct mrvlietypes_ledgpio *gpio = | |
942 | (struct mrvlietypes_ledgpio *) ctrl.data; | |
943 | ||
944 | memset(&ctrl, 0, sizeof(ctrl)); | |
945 | if (wrq->u.data.length > MAX_LEDS * 2) | |
946 | return -ENOTSUPP; | |
947 | if ((wrq->u.data.length % 2) != 0) | |
948 | return -ENOTSUPP; | |
949 | if (wrq->u.data.length == 0) { | |
950 | ctrl.action = | |
951 | cpu_to_le16 | |
952 | (cmd_act_get); | |
953 | } else { | |
954 | if (copy_from_user | |
955 | (data, wrq->u.data.pointer, | |
956 | sizeof(int) * | |
957 | wrq->u.data.length)) { | |
9012b28a | 958 | lbs_deb_ioctl( |
876c9d3a MT |
959 | "Copy from user failed\n"); |
960 | return -EFAULT; | |
961 | } | |
962 | ||
963 | ctrl.action = | |
964 | cpu_to_le16 | |
965 | (cmd_act_set); | |
966 | ctrl.numled = cpu_to_le16(0); | |
967 | gpio->header.type = | |
968 | cpu_to_le16(TLV_TYPE_LED_GPIO); | |
969 | gpio->header.len = wrq->u.data.length; | |
970 | for (i = 0; i < wrq->u.data.length; | |
971 | i += 2) { | |
972 | gpio->ledpin[i / 2].led = | |
973 | data[i]; | |
974 | gpio->ledpin[i / 2].pin = | |
975 | data[i + 1]; | |
976 | } | |
977 | } | |
978 | ret = | |
979 | libertas_prepare_and_send_command(priv, | |
980 | cmd_802_11_led_gpio_ctrl, | |
981 | 0, | |
982 | cmd_option_waitforrsp, | |
983 | 0, (void *)&ctrl); | |
984 | for (i = 0; i < gpio->header.len; i += 2) { | |
985 | data[i] = gpio->ledpin[i / 2].led; | |
986 | data[i + 1] = gpio->ledpin[i / 2].pin; | |
987 | } | |
988 | if (copy_to_user(wrq->u.data.pointer, data, | |
989 | sizeof(int) * | |
990 | gpio->header.len)) { | |
9012b28a | 991 | lbs_deb_ioctl("Copy to user failed\n"); |
876c9d3a MT |
992 | return -EFAULT; |
993 | } | |
994 | ||
995 | wrq->u.data.length = gpio->header.len; | |
996 | } | |
997 | break; | |
876c9d3a MT |
998 | } |
999 | break; | |
1000 | ||
1001 | default: | |
1002 | ret = -EINVAL; | |
1003 | break; | |
1004 | } | |
9012b28a HS |
1005 | |
1006 | lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret); | |
876c9d3a MT |
1007 | return ret; |
1008 | } | |
1009 | ||
1010 |