invert the mirror orders seems the first one seems to be offline right now
[openwrt/openwrt.git] / package / libertas / src / ioctl.c
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 "wext.h"
20 #include "cmd.h"
21 #include "ioctl.h"
22
23 #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \
24 IW_ESSID_MAX_SIZE + \
25 IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \
26 IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \
27 IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */
28
29 #define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ)
30
31 static int lbs_set_region(struct lbs_private * priv, u16 region_code)
32 {
33 int i;
34 int ret = 0;
35
36 for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
37 // use the region code to search for the index
38 if (region_code == lbs_region_code_to_index[i]) {
39 priv->regioncode = region_code;
40 break;
41 }
42 }
43
44 // if it's unidentified region code
45 if (i >= MRVDRV_MAX_REGION_CODE) {
46 lbs_deb_ioctl("region Code not identified\n");
47 ret = -1;
48 goto done;
49 }
50
51 if (lbs_set_regiontable(priv, priv->regioncode, 0)) {
52 ret = -EINVAL;
53 }
54
55 done:
56 lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret);
57 return ret;
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 struct lbs_private structure
121 * @param req A pointer to ifreq structure
122 * @return 0 --success, otherwise fail
123 */
124 static int lbs_bt_add_ioctl(struct lbs_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];
130 int ret;
131
132 lbs_deb_enter(LBS_DEB_IOCTL);
133
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
143 lbs_deb_ioctl("BT: adding %s\n", ethaddrs_str);
144 ret = lbs_prepare_and_send_command(priv, CMD_BT_ACCESS,
145 CMD_ACT_BT_ACCESS_ADD,
146 CMD_OPTION_WAITFORRSP, 0, ethaddr);
147 lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret);
148 return ret;
149 }
150
151 /**
152 * @brief Delete an entry from the BT table
153 * @param priv A pointer to struct lbs_private structure
154 * @param req A pointer to ifreq structure
155 * @return 0 --success, otherwise fail
156 */
157 static int lbs_bt_del_ioctl(struct lbs_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
164 lbs_deb_enter(LBS_DEB_IOCTL);
165
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
175 lbs_deb_ioctl("BT: deleting %s\n", ethaddrs_str);
176
177 return (lbs_prepare_and_send_command(priv,
178 CMD_BT_ACCESS,
179 CMD_ACT_BT_ACCESS_DEL,
180 CMD_OPTION_WAITFORRSP, 0, ethaddr));
181
182 lbs_deb_leave(LBS_DEB_IOCTL);
183 return 0;
184 }
185
186 /**
187 * @brief Reset all entries from the BT table
188 * @param priv A pointer to struct lbs_private structure
189 * @return 0 --success, otherwise fail
190 */
191 static int lbs_bt_reset_ioctl(struct lbs_private * priv)
192 {
193 lbs_deb_enter(LBS_DEB_IOCTL);
194
195 lbs_pr_alert( "BT: resetting\n");
196
197 return (lbs_prepare_and_send_command(priv,
198 CMD_BT_ACCESS,
199 CMD_ACT_BT_ACCESS_RESET,
200 CMD_OPTION_WAITFORRSP, 0, NULL));
201
202 lbs_deb_leave(LBS_DEB_IOCTL);
203 return 0;
204 }
205
206 /**
207 * @brief List an entry from the BT table
208 * @param priv A pointer to struct lbs_private structure
209 * @param req A pointer to ifreq structure
210 * @return 0 --success, otherwise fail
211 */
212 static int lbs_bt_list_ioctl(struct lbs_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 u32 id;
220 char addr1addr2[2 * ETH_ALEN];
221 } param;
222 static char outstr[64];
223 char *pbuf = outstr;
224 int ret;
225
226 lbs_deb_enter(LBS_DEB_IOCTL);
227
228 if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) {
229 lbs_deb_ioctl("Copy from user failed\n");
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 = lbs_prepare_and_send_command(priv, CMD_BT_ACCESS,
237 CMD_ACT_BT_ACCESS_LIST,
238 CMD_OPTION_WAITFORRSP, 0,
239 (char *)&param);
240
241 if (ret == 0) {
242 addr1 = param.addr1addr2;
243
244 pos = sprintf(pbuf, "BT includes node ");
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)) {
256 lbs_deb_ioctl("BT_LIST: Copy to user failed!\n");
257 return -EFAULT;
258 }
259
260 lbs_deb_leave(LBS_DEB_IOCTL);
261 return 0 ;
262 }
263
264 /**
265 * @brief Sets inverted state of blacklist (non-zero if inverted)
266 * @param priv A pointer to struct lbs_private structure
267 * @param req A pointer to ifreq structure
268 * @return 0 --success, otherwise fail
269 */
270 static int lbs_bt_set_invert_ioctl(struct lbs_private * priv, struct ifreq *req)
271 {
272 int ret;
273 struct iwreq *wrq = (struct iwreq *)req;
274 union {
275 u32 id;
276 char addr1addr2[2 * ETH_ALEN];
277 } param;
278
279 lbs_deb_enter(LBS_DEB_IOCTL);
280
281 param.id = SUBCMD_DATA(wrq) ;
282 ret = lbs_prepare_and_send_command(priv, CMD_BT_ACCESS,
283 CMD_ACT_BT_ACCESS_SET_INVERT,
284 CMD_OPTION_WAITFORRSP, 0,
285 (char *)&param);
286 if (ret != 0)
287 return -EFAULT;
288 lbs_deb_leave(LBS_DEB_IOCTL);
289 return 0;
290 }
291
292 /**
293 * @brief Gets inverted state of blacklist (non-zero if inverted)
294 * @param priv A pointer to struct lbs_private structure
295 * @param req A pointer to ifreq structure
296 * @return 0 --success, otherwise fail
297 */
298 static int lbs_bt_get_invert_ioctl(struct lbs_private * priv, struct ifreq *req)
299 {
300 struct iwreq *wrq = (struct iwreq *)req;
301 int ret;
302 union {
303 __le32 id;
304 char addr1addr2[2 * ETH_ALEN];
305 } param;
306
307 lbs_deb_enter(LBS_DEB_IOCTL);
308
309 ret = lbs_prepare_and_send_command(priv, CMD_BT_ACCESS,
310 CMD_ACT_BT_ACCESS_GET_INVERT,
311 CMD_OPTION_WAITFORRSP, 0,
312 (char *)&param);
313
314 if (ret == 0)
315 wrq->u.param.value = le32_to_cpu(param.id);
316 else
317 return -EFAULT;
318
319 lbs_deb_leave(LBS_DEB_IOCTL);
320 return 0;
321 }
322
323 /**
324 * @brief Find the next parameter in an input string
325 * @param ptr A pointer to the input parameter string
326 * @return A pointer to the next parameter, or 0 if no parameters left.
327 */
328 static char * next_param(char * ptr)
329 {
330 if (!ptr) return NULL;
331 while (*ptr == ' ' || *ptr == '\t') ++ptr;
332 return (*ptr == '\0') ? NULL : ptr;
333 }
334
335 /**
336 * @brief Add an entry to the FWT table
337 * @param priv A pointer to struct lbs_private structure
338 * @param req A pointer to ifreq structure
339 * @return 0 --success, otherwise fail
340 */
341 static int lbs_fwt_add_ioctl(struct lbs_private * priv, struct ifreq *req)
342 {
343 struct iwreq *wrq = (struct iwreq *)req;
344 char in_str[128];
345 static struct cmd_ds_fwt_access fwt_access;
346 char *ptr;
347 int ret;
348
349 lbs_deb_enter(LBS_DEB_IOCTL);
350
351 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
352 return -EFAULT;
353
354 if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
355 lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n");
356 return -EINVAL;
357 }
358
359 if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
360 lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n");
361 return -EINVAL;
362 }
363
364 if ((ptr = next_param(ptr)))
365 fwt_access.metric =
366 cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
367 else
368 fwt_access.metric = cpu_to_le32(FWT_DEFAULT_METRIC);
369
370 if ((ptr = next_param(ptr)))
371 fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
372 else
373 fwt_access.dir = FWT_DEFAULT_DIR;
374
375 if ((ptr = next_param(ptr)))
376 fwt_access.rate = (u8) simple_strtoul(ptr, &ptr, 10);
377 else
378 fwt_access.rate = FWT_DEFAULT_RATE;
379
380 if ((ptr = next_param(ptr)))
381 fwt_access.ssn =
382 cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
383 else
384 fwt_access.ssn = cpu_to_le32(FWT_DEFAULT_SSN);
385
386 if ((ptr = next_param(ptr)))
387 fwt_access.dsn =
388 cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
389 else
390 fwt_access.dsn = cpu_to_le32(FWT_DEFAULT_DSN);
391
392 if ((ptr = next_param(ptr)))
393 fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10);
394 else
395 fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT;
396
397 if ((ptr = next_param(ptr)))
398 fwt_access.ttl = simple_strtoul(ptr, &ptr, 10);
399 else
400 fwt_access.ttl = FWT_DEFAULT_TTL;
401
402 if ((ptr = next_param(ptr)))
403 fwt_access.expiration =
404 cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
405 else
406 fwt_access.expiration = cpu_to_le32(FWT_DEFAULT_EXPIRATION);
407
408 if ((ptr = next_param(ptr)))
409 fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10);
410 else
411 fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE;
412
413 if ((ptr = next_param(ptr)))
414 fwt_access.snr =
415 cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
416 else
417 fwt_access.snr = cpu_to_le32(FWT_DEFAULT_SNR);
418
419 #ifdef DEBUG
420 {
421 char ethaddr1_str[18], ethaddr2_str[18];
422 eth_addr2str(fwt_access.da, ethaddr1_str);
423 eth_addr2str(fwt_access.ra, ethaddr2_str);
424 lbs_deb_ioctl("FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str,
425 fwt_access.dir, ethaddr2_str);
426 lbs_deb_ioctl("FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n",
427 fwt_access.ssn, fwt_access.dsn, fwt_access.metric,
428 fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration,
429 fwt_access.sleepmode, fwt_access.snr);
430 }
431 #endif
432
433 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
434 CMD_ACT_FWT_ACCESS_ADD,
435 CMD_OPTION_WAITFORRSP, 0,
436 (void *)&fwt_access);
437
438 lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret);
439 return ret;
440 }
441
442 /**
443 * @brief Delete an entry from the FWT table
444 * @param priv A pointer to struct lbs_private structure
445 * @param req A pointer to ifreq structure
446 * @return 0 --success, otherwise fail
447 */
448 static int lbs_fwt_del_ioctl(struct lbs_private * priv, struct ifreq *req)
449 {
450 struct iwreq *wrq = (struct iwreq *)req;
451 char in_str[64];
452 static struct cmd_ds_fwt_access fwt_access;
453 char *ptr;
454 int ret;
455
456 lbs_deb_enter(LBS_DEB_IOCTL);
457
458 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
459 return -EFAULT;
460
461 if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
462 lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n");
463 return -EINVAL;
464 }
465
466 if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
467 lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n");
468 return -EINVAL;
469 }
470
471 if ((ptr = next_param(ptr)))
472 fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
473 else
474 fwt_access.dir = FWT_DEFAULT_DIR;
475
476 #ifdef DEBUG
477 {
478 char ethaddr1_str[18], ethaddr2_str[18];
479 lbs_deb_ioctl("FWT_DEL: line is %s\n", in_str);
480 eth_addr2str(fwt_access.da, ethaddr1_str);
481 eth_addr2str(fwt_access.ra, ethaddr2_str);
482 lbs_deb_ioctl("FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str,
483 ethaddr2_str, fwt_access.dir);
484 }
485 #endif
486
487 ret = lbs_prepare_and_send_command(priv,
488 CMD_FWT_ACCESS,
489 CMD_ACT_FWT_ACCESS_DEL,
490 CMD_OPTION_WAITFORRSP, 0,
491 (void *)&fwt_access);
492 lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret);
493 return ret;
494 }
495
496
497 /**
498 * @brief Print route parameters
499 * @param fwt_access struct cmd_ds_fwt_access with route info
500 * @param buf destination buffer for route info
501 */
502 static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf)
503 {
504 buf += sprintf(buf, " ");
505 buf += eth_addr2str(fwt_access.da, buf);
506 buf += sprintf(buf, " ");
507 buf += eth_addr2str(fwt_access.ra, buf);
508 buf += sprintf(buf, " %u", fwt_access.valid);
509 buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric));
510 buf += sprintf(buf, " %u", fwt_access.dir);
511 buf += sprintf(buf, " %u", fwt_access.rate);
512 buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn));
513 buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn));
514 buf += sprintf(buf, " %u", fwt_access.hopcount);
515 buf += sprintf(buf, " %u", fwt_access.ttl);
516 buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration));
517 buf += sprintf(buf, " %u", fwt_access.sleepmode);
518 buf += sprintf(buf, " %u ", le32_to_cpu(fwt_access.snr));
519 buf += eth_addr2str(fwt_access.prec, buf);
520 }
521
522 /**
523 * @brief Lookup an entry in the FWT table
524 * @param priv A pointer to struct lbs_private structure
525 * @param req A pointer to ifreq structure
526 * @return 0 --success, otherwise fail
527 */
528 static int lbs_fwt_lookup_ioctl(struct lbs_private * priv, struct ifreq *req)
529 {
530 struct iwreq *wrq = (struct iwreq *)req;
531 char in_str[64];
532 char *ptr;
533 static struct cmd_ds_fwt_access fwt_access;
534 static char out_str[128];
535 int ret;
536
537 lbs_deb_enter(LBS_DEB_IOCTL);
538
539 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
540 return -EFAULT;
541
542 if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
543 lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n");
544 return -EINVAL;
545 }
546
547 #ifdef DEBUG
548 {
549 char ethaddr1_str[18];
550 lbs_deb_ioctl("FWT_LOOKUP: line is %s\n", in_str);
551 eth_addr2str(fwt_access.da, ethaddr1_str);
552 lbs_deb_ioctl("FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str);
553 }
554 #endif
555
556 ret = lbs_prepare_and_send_command(priv,
557 CMD_FWT_ACCESS,
558 CMD_ACT_FWT_ACCESS_LOOKUP,
559 CMD_OPTION_WAITFORRSP, 0,
560 (void *)&fwt_access);
561
562 if (ret == 0)
563 print_route(fwt_access, out_str);
564 else
565 sprintf(out_str, "(null)");
566
567 wrq->u.data.length = strlen(out_str);
568 if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
569 wrq->u.data.length)) {
570 lbs_deb_ioctl("FWT_LOOKUP: Copy to user failed!\n");
571 return -EFAULT;
572 }
573
574 lbs_deb_leave(LBS_DEB_IOCTL);
575 return 0;
576 }
577
578 /**
579 * @brief Reset all entries from the FWT table
580 * @param priv A pointer to struct lbs_private structure
581 * @return 0 --success, otherwise fail
582 */
583 static int lbs_fwt_reset_ioctl(struct lbs_private * priv)
584 {
585 lbs_deb_ioctl("FWT: resetting\n");
586
587 return (lbs_prepare_and_send_command(priv,
588 CMD_FWT_ACCESS,
589 CMD_ACT_FWT_ACCESS_RESET,
590 CMD_OPTION_WAITFORRSP, 0, NULL));
591 }
592
593 /**
594 * @brief List an entry from the FWT table
595 * @param priv A pointer to struct lbs_private structure
596 * @param req A pointer to ifreq structure
597 * @return 0 --success, otherwise fail
598 */
599 static int lbs_fwt_list_ioctl(struct lbs_private * priv, struct ifreq *req)
600 {
601 struct iwreq *wrq = (struct iwreq *)req;
602 char in_str[8];
603 static struct cmd_ds_fwt_access fwt_access;
604 char *ptr = in_str;
605 static char out_str[128];
606 char *pbuf = out_str;
607 int ret = 0;
608
609 lbs_deb_enter(LBS_DEB_IOCTL);
610
611 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) {
612 ret = -EFAULT;
613 goto out;
614 }
615
616 fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
617
618 #ifdef DEBUG
619 {
620 lbs_deb_ioctl("FWT_LIST: line is %s\n", in_str);
621 lbs_deb_ioctl("FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id));
622 }
623 #endif
624
625 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
626 CMD_ACT_FWT_ACCESS_LIST,
627 CMD_OPTION_WAITFORRSP, 0, (void *)&fwt_access);
628
629 if (ret == 0)
630 print_route(fwt_access, pbuf);
631 else
632 pbuf += sprintf(pbuf, " (null)");
633
634 wrq->u.data.length = strlen(out_str);
635 if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
636 wrq->u.data.length)) {
637 lbs_deb_ioctl("FWT_LIST: Copy to user failed!\n");
638 ret = -EFAULT;
639 goto out;
640 }
641
642 ret = 0;
643
644 out:
645 lbs_deb_leave(LBS_DEB_IOCTL);
646 return ret;
647 }
648
649 /**
650 * @brief List an entry from the FRT table
651 * @param priv A pointer to struct lbs_private structure
652 * @param req A pointer to ifreq structure
653 * @return 0 --success, otherwise fail
654 */
655 static int lbs_fwt_list_route_ioctl(struct lbs_private * priv, struct ifreq *req)
656 {
657 struct iwreq *wrq = (struct iwreq *)req;
658 char in_str[64];
659 static struct cmd_ds_fwt_access fwt_access;
660 char *ptr = in_str;
661 static char out_str[128];
662 char *pbuf = out_str;
663 int ret;
664
665 lbs_deb_enter(LBS_DEB_IOCTL);
666
667 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
668 return -EFAULT;
669
670 fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
671
672 #ifdef DEBUG
673 {
674 lbs_deb_ioctl("FWT_LIST_ROUTE: line is %s\n", in_str);
675 lbs_deb_ioctl("FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id));
676 }
677 #endif
678
679 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
680 CMD_ACT_FWT_ACCESS_LIST_ROUTE,
681 CMD_OPTION_WAITFORRSP, 0, (void *)&fwt_access);
682
683 if (ret == 0) {
684 print_route(fwt_access, pbuf);
685 } else
686 pbuf += sprintf(pbuf, " (null)");
687
688 wrq->u.data.length = strlen(out_str);
689 if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
690 wrq->u.data.length)) {
691 lbs_deb_ioctl("FWT_LIST_ROUTE: Copy to user failed!\n");
692 return -EFAULT;
693 }
694
695 lbs_deb_leave(LBS_DEB_IOCTL);
696 return 0;
697 }
698
699 /**
700 * @brief List an entry from the FNT table
701 * @param priv A pointer to struct lbs_private structure
702 * @param req A pointer to ifreq structure
703 * @return 0 --success, otherwise fail
704 */
705 static int lbs_fwt_list_neighbor_ioctl(struct lbs_private * priv, struct ifreq *req)
706 {
707 struct iwreq *wrq = (struct iwreq *)req;
708 char in_str[8];
709 static struct cmd_ds_fwt_access fwt_access;
710 char *ptr = in_str;
711 static char out_str[128];
712 char *pbuf = out_str;
713 int ret;
714
715 lbs_deb_enter(LBS_DEB_IOCTL);
716
717 if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
718 return -EFAULT;
719
720 memset(&fwt_access, 0, sizeof(fwt_access));
721 fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
722
723 #ifdef DEBUG
724 {
725 lbs_deb_ioctl("FWT_LIST_NEIGHBOR: line is %s\n", in_str);
726 lbs_deb_ioctl("FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id));
727 }
728 #endif
729
730 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
731 CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR,
732 CMD_OPTION_WAITFORRSP, 0,
733 (void *)&fwt_access);
734
735 if (ret == 0) {
736 pbuf += sprintf(pbuf, " ra ");
737 pbuf += eth_addr2str(fwt_access.ra, pbuf);
738 pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode);
739 pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr));
740 pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references));
741 } else
742 pbuf += sprintf(pbuf, " (null)");
743
744 wrq->u.data.length = strlen(out_str);
745 if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
746 wrq->u.data.length)) {
747 lbs_deb_ioctl("FWT_LIST_NEIGHBOR: Copy to user failed!\n");
748 return -EFAULT;
749 }
750
751 lbs_deb_leave(LBS_DEB_IOCTL);
752 return 0;
753 }
754
755 /**
756 * @brief Cleans up the route (FRT) and neighbor (FNT) tables
757 * (Garbage Collection)
758 * @param priv A pointer to struct lbs_private structure
759 * @param req A pointer to ifreq structure
760 * @return 0 --success, otherwise fail
761 */
762 static int lbs_fwt_cleanup_ioctl(struct lbs_private * priv, struct ifreq *req)
763 {
764 struct iwreq *wrq = (struct iwreq *)req;
765 static struct cmd_ds_fwt_access fwt_access;
766 int ret;
767
768 lbs_deb_enter(LBS_DEB_IOCTL);
769
770 lbs_deb_ioctl("FWT: cleaning up\n");
771
772 memset(&fwt_access, 0, sizeof(fwt_access));
773
774 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
775 CMD_ACT_FWT_ACCESS_CLEANUP,
776 CMD_OPTION_WAITFORRSP, 0,
777 (void *)&fwt_access);
778
779 if (ret == 0)
780 wrq->u.param.value = le32_to_cpu(fwt_access.references);
781 else
782 return -EFAULT;
783
784 lbs_deb_leave(LBS_DEB_IOCTL);
785 return 0;
786 }
787
788 /**
789 * @brief Gets firmware internal time (debug purposes)
790 * @param priv A pointer to struct lbs_private structure
791 * @param req A pointer to ifreq structure
792 * @return 0 --success, otherwise fail
793 */
794 static int lbs_fwt_time_ioctl(struct lbs_private * priv, struct ifreq *req)
795 {
796 struct iwreq *wrq = (struct iwreq *)req;
797 static struct cmd_ds_fwt_access fwt_access;
798 int ret;
799
800 lbs_deb_enter(LBS_DEB_IOCTL);
801
802 lbs_deb_ioctl("FWT: getting time\n");
803
804 memset(&fwt_access, 0, sizeof(fwt_access));
805
806 ret = lbs_prepare_and_send_command(priv, CMD_FWT_ACCESS,
807 CMD_ACT_FWT_ACCESS_TIME,
808 CMD_OPTION_WAITFORRSP, 0,
809 (void *)&fwt_access);
810
811 if (ret == 0)
812 wrq->u.param.value = le32_to_cpu(fwt_access.references);
813 else
814 return -EFAULT;
815
816 lbs_deb_leave(LBS_DEB_IOCTL);
817 return 0;
818 }
819
820
821 /**
822 * @brief Manages all mesh related ioctls
823 * @param priv A pointer to struct lbs_private structure
824 * @param req A pointer to ifreq structure
825 * @param cmd The command type
826 * @param host_subcmd The device code for the subcommand
827 * 0: sets a value in the firmware
828 * 1: retrieves an int from the firmware
829 * @return 0 --success, otherwise fail
830 */
831 static int lbs_mesh_ioctl(struct lbs_private * priv, struct iwreq * wrq,
832 int cmd, int subcmd)
833 {
834 struct cmd_ds_mesh_access mesh_access;
835 int parameter;
836 char str[128];
837 char *ptr = str;
838 int ret, i;
839
840 lbs_deb_enter(LBS_DEB_IOCTL);
841
842 memset(&mesh_access, 0, sizeof(mesh_access));
843
844 if (cmd == LBS_SETONEINT_GETNONE) {
845 parameter = SUBCMD_DATA(wrq);
846
847 /* Convert rate from Mbps -> firmware rate index */
848 if (subcmd == CMD_ACT_MESH_SET_BCAST_RATE)
849 parameter = lbs_data_rate_to_fw_index(parameter);
850
851 if (parameter < 0)
852 return -EINVAL;
853 mesh_access.data[0] = cpu_to_le32(parameter);
854 } else if (subcmd == CMD_ACT_MESH_SET_LINK_COSTS) {
855 if (copy_from_user(str, wrq->u.data.pointer, sizeof(str)))
856 return -EFAULT;
857
858 for (i = 0; i < COSTS_LIST_SIZE; i++) {
859 mesh_access.data[i] = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
860 if (!(ptr = next_param(ptr)) && i!= (COSTS_LIST_SIZE - 1))
861 return -EINVAL;
862 }
863 }
864
865 ret = lbs_mesh_access(priv, subcmd, &mesh_access);
866
867 if (ret != 0)
868 return ret;
869
870 if (cmd == LBS_SETNONE_GETONEINT) {
871 u32 data = le32_to_cpu(mesh_access.data[0]);
872
873 if (subcmd == CMD_ACT_MESH_GET_BCAST_RATE)
874 wrq->u.param.value = lbs_fw_index_to_data_rate(data);
875 else
876 wrq->u.param.value = data;
877 } else if (subcmd == CMD_ACT_MESH_GET_LINK_COSTS) {
878 for (i = 0; i < COSTS_LIST_SIZE; i++)
879 ptr += sprintf (ptr, " %u", le32_to_cpu(mesh_access.data[i]));
880 wrq->u.data.length = strlen(str);
881
882 if (copy_to_user(wrq->u.data.pointer, (char *)str,
883 wrq->u.data.length)) {
884 lbs_deb_ioctl("MESH_IOCTL: Copy to user failed!\n");
885 ret = -EFAULT;
886 }
887 }
888
889 lbs_deb_leave(LBS_DEB_IOCTL);
890 return ret;
891 }
892
893 /**
894 * @brief Control Beacon transmissions
895 * @param priv A pointer to struct lbs_private structure
896 * @param wrq A pointer to iwreq structure
897 * @return 0 --success, otherwise fail
898 */
899 static int lbs_bcn_ioctl(struct lbs_private * priv, struct iwreq *wrq)
900 {
901 int ret;
902 int data[2];
903
904 memset(data, 0, sizeof(data));
905 if (!wrq->u.data.length) {
906 lbs_deb_ioctl("Get Beacon control\n");
907 ret = lbs_prepare_and_send_command(priv,
908 CMD_802_11_BEACON_CTRL,
909 CMD_ACT_GET,
910 CMD_OPTION_WAITFORRSP, 0, NULL);
911 data[0] = priv->beacon_enable;
912 data[1] = priv->beacon_period;
913 if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
914 lbs_deb_ioctl("Copy to user failed\n");
915 return -EFAULT;
916 }
917 #define GET_TWO_INT 2
918 wrq->u.data.length = GET_TWO_INT;
919 } else {
920 lbs_deb_ioctl("Set beacon control\n");
921 if (wrq->u.data.length > 2)
922 return -EINVAL;
923 if (copy_from_user (data, wrq->u.data.pointer,
924 sizeof(int) * wrq->u.data.length)) {
925 lbs_deb_ioctl("Copy from user failed\n");
926 return -EFAULT;
927 }
928 priv->beacon_enable = data[0];
929 if (wrq->u.data.length > 1) {
930 if ((data[1] > MRVDRV_MAX_BEACON_INTERVAL)
931 || (data[1] < MRVDRV_MIN_BEACON_INTERVAL))
932 return -ENOTSUPP;
933 priv->beacon_period= data[1];
934 }
935 ret = lbs_prepare_and_send_command(priv,
936 CMD_802_11_BEACON_CTRL,
937 CMD_ACT_SET,
938 CMD_OPTION_WAITFORRSP, 0, NULL);
939 }
940 return ret;
941 }
942
943 static int lbs_led_gpio_ioctl(struct lbs_private * priv, struct ifreq *req)
944 {
945 struct iwreq *wrq = (struct iwreq *)req;
946 int i, ret = 0;
947 int data[16];
948 struct cmd_ds_802_11_led_ctrl ctrl;
949 struct mrvlietypes_ledgpio *gpio = (struct mrvlietypes_ledgpio *) ctrl.data;
950 int len = wrq->u.data.length;
951
952 if ((len > MAX_LEDS * 2) || (len % 2 != 0))
953 return -ENOTSUPP;
954
955 memset(&ctrl, 0, sizeof(ctrl));
956 if (len == 0) {
957 ctrl.action = cpu_to_le16(CMD_ACT_GET);
958 } else {
959 if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * len)) {
960 lbs_deb_ioctl("Copy from user failed\n");
961 ret = -EFAULT;
962 goto out;
963 }
964
965 ctrl.action = cpu_to_le16(CMD_ACT_SET);
966 ctrl.numled = cpu_to_le16(0);
967 gpio->header.type = cpu_to_le16(TLV_TYPE_LED_GPIO);
968 gpio->header.len = cpu_to_le16(len);
969 for (i = 0; i < len; i += 2) {
970 gpio->ledpin[i / 2].led = data[i];
971 gpio->ledpin[i / 2].pin = data[i + 1];
972 }
973 }
974
975 ret = lbs_prepare_and_send_command(priv, CMD_802_11_LED_GPIO_CTRL,
976 0, CMD_OPTION_WAITFORRSP, 0, (void *)&ctrl);
977 if (ret) {
978 lbs_deb_ioctl("Error doing LED GPIO control: %d\n", ret);
979 goto out;
980 }
981 len = le16_to_cpu(gpio->header.len);
982 for (i = 0; i < len; i += 2) {
983 data[i] = gpio->ledpin[i / 2].led;
984 data[i + 1] = gpio->ledpin[i / 2].pin;
985 }
986
987 if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * len)) {
988 lbs_deb_ioctl("Copy to user failed\n");
989 ret = -EFAULT;
990 goto out;
991 }
992
993 wrq->u.data.length = len;
994
995 out:
996 return ret;
997 }
998
999
1000 static int lbs_led_bhv_ioctl(struct lbs_private * priv, struct ifreq *req)
1001 {
1002 struct iwreq *wrq = (struct iwreq *)req;
1003 int i, ret = 0;
1004 int data[MAX_LEDS*4];
1005 int firmwarestate = 0;
1006 struct cmd_ds_802_11_led_ctrl ctrl;
1007 struct mrvlietypes_ledbhv *bhv = (struct mrvlietypes_ledbhv *) ctrl.data;
1008 int len = wrq->u.data.length;
1009
1010 if ((len > MAX_LEDS * 4) ||(len == 0) )
1011 return -ENOTSUPP;
1012
1013 memset(&ctrl, 0, sizeof(ctrl));
1014 if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * len)) {
1015 lbs_deb_ioctl("Copy from user failed\n");
1016 ret = -EFAULT;
1017 goto out;
1018 }
1019 if (len == 1) {
1020 ctrl.action = cpu_to_le16(CMD_ACT_GET);
1021 firmwarestate = data[0];
1022 } else {
1023
1024 if (len % 4 != 0 )
1025 return -ENOTSUPP;
1026
1027 bhv->header.type = cpu_to_le16(TLV_TYPE_LEDBEHAVIOR);
1028 bhv->header.len = cpu_to_le16(len);
1029 ctrl.action = cpu_to_le16(CMD_ACT_SET);
1030 ctrl.numled = cpu_to_le16(0);
1031 for (i = 0; i < len; i += 4) {
1032 bhv->ledbhv[i / 4].firmwarestate = data[i];
1033 bhv->ledbhv[i / 4].led = data[i + 1];
1034 bhv->ledbhv[i / 4].ledstate = data[i + 2];
1035 bhv->ledbhv[i / 4].ledarg = data[i + 3];
1036 }
1037 }
1038
1039 ret = lbs_prepare_and_send_command(priv, CMD_802_11_LED_GPIO_CTRL,
1040 0, CMD_OPTION_WAITFORRSP, 0, (void *)&ctrl);
1041 if (ret) {
1042 lbs_deb_ioctl("Error doing LED GPIO control: %d\n", ret);
1043 goto out;
1044 }
1045
1046 /* Get LED behavior IE, we have received gpio control as well when len
1047 is equal to 1. */
1048 if (len ==1 ) {
1049 bhv = (struct mrvlietypes_ledbhv *)
1050 ((unsigned char *)bhv->ledbhv + le16_to_cpu(bhv->header.len));
1051 i = 0;
1052 while ( i < (MAX_LEDS*4) &&
1053 (bhv->header.type != cpu_to_le16(MRVL_TERMINATE_TLV_ID)) ) {
1054 if (bhv->ledbhv[0].firmwarestate == firmwarestate) {
1055 data[i++] = bhv->ledbhv[0].firmwarestate;
1056 data[i++] = bhv->ledbhv[0].led;
1057 data[i++] = bhv->ledbhv[0].ledstate;
1058 data[i++] = bhv->ledbhv[0].ledarg;
1059 }
1060 bhv++;
1061 }
1062 len = i;
1063 } else {
1064 for (i = 0; i < le16_to_cpu(bhv->header.len); i += 4) {
1065 data[i] = bhv->ledbhv[i / 4].firmwarestate;
1066 data[i + 1] = bhv->ledbhv[i / 4].led;
1067 data[i + 2] = bhv->ledbhv[i / 4].ledstate;
1068 data[i + 3] = bhv->ledbhv[i / 4].ledarg;
1069 }
1070 len = le16_to_cpu(bhv->header.len);
1071 }
1072
1073 if (copy_to_user(wrq->u.data.pointer, data,
1074 sizeof(int) * len)) {
1075 lbs_deb_ioctl("Copy to user failed\n");
1076 ret = -EFAULT;
1077 goto out;
1078 }
1079
1080 wrq->u.data.length = len;
1081
1082 out:
1083 return ret;
1084 }
1085
1086 /**
1087 * @brief ioctl function - entry point
1088 *
1089 * @param dev A pointer to net_device structure
1090 * @param req A pointer to ifreq structure
1091 * @param cmd command
1092 * @return 0--success, otherwise fail
1093 */
1094 int lbs_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
1095 {
1096 int *pdata;
1097 int ret = 0;
1098 struct lbs_private *priv = dev->priv;
1099 struct iwreq *wrq = (struct iwreq *)req;
1100
1101 lbs_deb_enter(LBS_DEB_IOCTL);
1102
1103 lbs_deb_ioctl("lbs_do_ioctl: ioctl cmd = 0x%x\n", cmd);
1104 switch (cmd) {
1105 case LBS_SETNONE_GETNONE:
1106 switch (wrq->u.data.flags) {
1107 case LBS_SUBCMD_BT_RESET:
1108 lbs_bt_reset_ioctl(priv);
1109 break;
1110 case LBS_SUBCMD_FWT_RESET:
1111 lbs_fwt_reset_ioctl(priv);
1112 break;
1113 }
1114 break;
1115
1116 case LBS_SETONEINT_GETNONE:
1117 switch (wrq->u.mode) {
1118 case LBS_SUBCMD_SET_REGION:
1119 ret = lbs_set_region(priv, (u16) SUBCMD_DATA(wrq));
1120 break;
1121 case LBS_SUBCMD_MESH_SET_TTL:
1122 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1123 CMD_ACT_MESH_SET_TTL);
1124 break;
1125 case LBS_SUBCMD_MESH_SET_BCAST_RATE:
1126 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1127 CMD_ACT_MESH_SET_BCAST_RATE);
1128 break;
1129 case LBS_SUBCMD_MESH_SET_RREQ_DELAY:
1130 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1131 CMD_ACT_MESH_SET_RREQ_DELAY);
1132 break;
1133 case LBS_SUBCMD_MESH_SET_ROUTE_EXP:
1134 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1135 CMD_ACT_MESH_SET_ROUTE_EXP);
1136 break;
1137 case LBS_SUBCMD_BT_SET_INVERT:
1138 ret = lbs_bt_set_invert_ioctl(priv, req);
1139 break;
1140 default:
1141 ret = -EOPNOTSUPP;
1142 break;
1143 }
1144 break;
1145
1146 case LBS_SET128CHAR_GET128CHAR:
1147 switch ((int)wrq->u.data.flags) {
1148 case LBS_SUBCMD_BT_ADD:
1149 ret = lbs_bt_add_ioctl(priv, req);
1150 break;
1151 case LBS_SUBCMD_BT_DEL:
1152 ret = lbs_bt_del_ioctl(priv, req);
1153 break;
1154 case LBS_SUBCMD_BT_LIST:
1155 ret = lbs_bt_list_ioctl(priv, req);
1156 break;
1157 case LBS_SUBCMD_FWT_ADD:
1158 ret = lbs_fwt_add_ioctl(priv, req);
1159 break;
1160 case LBS_SUBCMD_FWT_DEL:
1161 ret = lbs_fwt_del_ioctl(priv, req);
1162 break;
1163 case LBS_SUBCMD_FWT_LOOKUP:
1164 ret = lbs_fwt_lookup_ioctl(priv, req);
1165 break;
1166 case LBS_SUBCMD_FWT_LIST_NEIGHBOR:
1167 ret = lbs_fwt_list_neighbor_ioctl(priv, req);
1168 break;
1169 case LBS_SUBCMD_FWT_LIST:
1170 ret = lbs_fwt_list_ioctl(priv, req);
1171 break;
1172 case LBS_SUBCMD_FWT_LIST_ROUTE:
1173 ret = lbs_fwt_list_route_ioctl(priv, req);
1174 break;
1175 case LBS_SUBCMD_MESH_SET_LINK_COSTS:
1176 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1177 CMD_ACT_MESH_SET_LINK_COSTS);
1178 break ;
1179 case LBS_SUBCMD_MESH_GET_LINK_COSTS:
1180 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1181 CMD_ACT_MESH_GET_LINK_COSTS);
1182 break;
1183 }
1184 break;
1185
1186 case LBS_SETNONE_GETONEINT:
1187 switch (wrq->u.mode) {
1188 case LBS_SUBCMD_GET_REGION:
1189 pdata = (int *)wrq->u.name;
1190 *pdata = (int)priv->regioncode;
1191 break;
1192 case LBS_SUBCMD_FWT_CLEANUP:
1193 ret = lbs_fwt_cleanup_ioctl(priv, req);
1194 break;
1195 case LBS_SUBCMD_FWT_TIME:
1196 ret = lbs_fwt_time_ioctl(priv, req);
1197 break;
1198 case LBS_SUBCMD_MESH_GET_TTL:
1199 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1200 CMD_ACT_MESH_GET_TTL);
1201 break;
1202 case LBS_SUBCMD_MESH_GET_BCAST_RATE:
1203 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1204 CMD_ACT_MESH_GET_BCAST_RATE);
1205 break;
1206 case LBS_SUBCMD_MESH_GET_RREQ_DELAY:
1207 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1208 CMD_ACT_MESH_GET_RREQ_DELAY);
1209 break;
1210 case LBS_SUBCMD_MESH_GET_ROUTE_EXP:
1211 ret = lbs_mesh_ioctl(priv, wrq, cmd,
1212 CMD_ACT_MESH_GET_ROUTE_EXP);
1213 break;
1214 case LBS_SUBCMD_BT_GET_INVERT:
1215 ret = lbs_bt_get_invert_ioctl(priv, req);
1216 break;
1217 default:
1218 ret = -EOPNOTSUPP;
1219 }
1220 break;
1221
1222 case LBS_SET_GET_SIXTEEN_INT:
1223 switch ((int)wrq->u.data.flags) {
1224 case LBS_LED_GPIO_CTRL:
1225 ret = lbs_led_gpio_ioctl(priv, req);
1226 break;
1227 case LBS_BCN_CTRL:
1228 ret = lbs_bcn_ioctl(priv,wrq);
1229 break;
1230 case LBS_LED_BEHAVIOR_CTRL:
1231 ret = lbs_led_bhv_ioctl(priv, req);
1232 break;
1233 }
1234 break;
1235
1236 default:
1237 ret = -EINVAL;
1238 break;
1239 }
1240
1241 lbs_deb_leave_args(LBS_DEB_IOCTL, "ret %d", ret);
1242 return ret;
1243 }