38a451edb70359052877d66fce0293ad65b0aac3
[linux-2.6.git] / drivers / net / wireless / libertas / wext.c
1 /**
2   * This file contains ioctl functions
3   */
4 #include <linux/ctype.h>
5 #include <linux/delay.h>
6 #include <linux/if.h>
7 #include <linux/if_arp.h>
8 #include <linux/wireless.h>
9 #include <linux/bitops.h>
10
11 #include <net/lib80211.h>
12 #include <net/iw_handler.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 "scan.h"
21 #include "assoc.h"
22 #include "cmd.h"
23
24
25 static inline void lbs_postpone_association_work(struct lbs_private *priv)
26 {
27         if (priv->surpriseremoved)
28                 return;
29         cancel_delayed_work(&priv->assoc_work);
30         queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2);
31 }
32
33 static inline void lbs_do_association_work(struct lbs_private *priv)
34 {
35         if (priv->surpriseremoved)
36                 return;
37         cancel_delayed_work(&priv->assoc_work);
38         queue_delayed_work(priv->work_thread, &priv->assoc_work, 0);
39 }
40
41 static inline void lbs_cancel_association_work(struct lbs_private *priv)
42 {
43         cancel_delayed_work(&priv->assoc_work);
44         kfree(priv->pending_assoc_req);
45         priv->pending_assoc_req = NULL;
46 }
47
48 /**
49  *  @brief This function checks if the command is allowed.
50  *
51  *  @param priv         A pointer to lbs_private structure
52  *  @return             allowed or not allowed.
53  */
54
55 int lbs_is_cmd_allowed(struct lbs_private *priv)
56 {
57         int         ret = 1;
58
59         lbs_deb_enter(LBS_DEB_WEXT);
60
61         if (!priv->is_auto_deep_sleep_enabled) {
62                 if (priv->is_deep_sleep) {
63                         lbs_deb_wext("IOCTLS called when station"
64                                         "is in deep sleep\n");
65                         ret = 0;
66                 }
67         }
68
69         lbs_deb_leave(LBS_DEB_WEXT);
70         return ret;
71 }
72
73
74 /**
75  *  @brief Find the channel frequency power info with specific channel
76  *
77  *  @param priv         A pointer to struct lbs_private structure
78  *  @param band         it can be BAND_A, BAND_G or BAND_B
79  *  @param channel      the channel for looking
80  *  @return             A pointer to struct chan_freq_power structure or NULL if not find.
81  */
82 struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
83         struct lbs_private *priv,
84         u8 band,
85         u16 channel)
86 {
87         struct chan_freq_power *cfp = NULL;
88         struct region_channel *rc;
89         int i, j;
90
91         for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
92                 rc = &priv->region_channel[j];
93
94                 if (priv->enable11d)
95                         rc = &priv->universal_channel[j];
96                 if (!rc->valid || !rc->CFP)
97                         continue;
98                 if (rc->band != band)
99                         continue;
100                 for (i = 0; i < rc->nrcfp; i++) {
101                         if (rc->CFP[i].channel == channel) {
102                                 cfp = &rc->CFP[i];
103                                 break;
104                         }
105                 }
106         }
107
108         if (!cfp && channel)
109                 lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
110                        "cfp by band %d / channel %d\n", band, channel);
111
112         return cfp;
113 }
114
115 /**
116  *  @brief Find the channel frequency power info with specific frequency
117  *
118  *  @param priv         A pointer to struct lbs_private structure
119  *  @param band         it can be BAND_A, BAND_G or BAND_B
120  *  @param freq         the frequency for looking
121  *  @return             A pointer to struct chan_freq_power structure or NULL if not find.
122  */
123 static struct chan_freq_power *find_cfp_by_band_and_freq(
124         struct lbs_private *priv,
125         u8 band,
126         u32 freq)
127 {
128         struct chan_freq_power *cfp = NULL;
129         struct region_channel *rc;
130         int i, j;
131
132         for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
133                 rc = &priv->region_channel[j];
134
135                 if (priv->enable11d)
136                         rc = &priv->universal_channel[j];
137                 if (!rc->valid || !rc->CFP)
138                         continue;
139                 if (rc->band != band)
140                         continue;
141                 for (i = 0; i < rc->nrcfp; i++) {
142                         if (rc->CFP[i].freq == freq) {
143                                 cfp = &rc->CFP[i];
144                                 break;
145                         }
146                 }
147         }
148
149         if (!cfp && freq)
150                 lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
151                        "band %d / freq %d\n", band, freq);
152
153         return cfp;
154 }
155
156 /**
157  *  @brief Copy active data rates based on adapter mode and status
158  *
159  *  @param priv              A pointer to struct lbs_private structure
160  *  @param rate                 The buf to return the active rates
161  */
162 static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
163 {
164         lbs_deb_enter(LBS_DEB_WEXT);
165
166         if ((priv->connect_status != LBS_CONNECTED) &&
167                 (priv->mesh_connect_status != LBS_CONNECTED))
168                 memcpy(rates, lbs_bg_rates, MAX_RATES);
169         else
170                 memcpy(rates, priv->curbssparams.rates, MAX_RATES);
171
172         lbs_deb_leave(LBS_DEB_WEXT);
173 }
174
175 static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
176                          char *cwrq, char *extra)
177 {
178
179         lbs_deb_enter(LBS_DEB_WEXT);
180
181         /* We could add support for 802.11n here as needed. Jean II */
182         snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
183
184         lbs_deb_leave(LBS_DEB_WEXT);
185         return 0;
186 }
187
188 static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
189                          struct iw_freq *fwrq, char *extra)
190 {
191         struct lbs_private *priv = dev->ml_priv;
192         struct chan_freq_power *cfp;
193
194         lbs_deb_enter(LBS_DEB_WEXT);
195
196         if (!lbs_is_cmd_allowed(priv)) {
197                 lbs_deb_leave(LBS_DEB_WEXT);
198                 return -EBUSY;
199         }
200
201         cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
202                                            priv->curbssparams.channel);
203
204         if (!cfp) {
205                 if (priv->curbssparams.channel)
206                         lbs_deb_wext("invalid channel %d\n",
207                                priv->curbssparams.channel);
208                 return -EINVAL;
209         }
210
211         fwrq->m = (long)cfp->freq * 100000;
212         fwrq->e = 1;
213
214         lbs_deb_wext("freq %u\n", fwrq->m);
215         lbs_deb_leave(LBS_DEB_WEXT);
216         return 0;
217 }
218
219 static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
220                         struct sockaddr *awrq, char *extra)
221 {
222         struct lbs_private *priv = dev->ml_priv;
223
224         lbs_deb_enter(LBS_DEB_WEXT);
225
226         if (priv->connect_status == LBS_CONNECTED) {
227                 memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
228         } else {
229                 memset(awrq->sa_data, 0, ETH_ALEN);
230         }
231         awrq->sa_family = ARPHRD_ETHER;
232
233         lbs_deb_leave(LBS_DEB_WEXT);
234         return 0;
235 }
236
237 static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
238                          struct iw_point *dwrq, char *extra)
239 {
240         struct lbs_private *priv = dev->ml_priv;
241
242         lbs_deb_enter(LBS_DEB_WEXT);
243
244         /*
245          * Check the size of the string
246          */
247
248         if (dwrq->length > 16) {
249                 return -E2BIG;
250         }
251
252         mutex_lock(&priv->lock);
253         memset(priv->nodename, 0, sizeof(priv->nodename));
254         memcpy(priv->nodename, extra, dwrq->length);
255         mutex_unlock(&priv->lock);
256
257         lbs_deb_leave(LBS_DEB_WEXT);
258         return 0;
259 }
260
261 static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
262                          struct iw_point *dwrq, char *extra)
263 {
264         struct lbs_private *priv = dev->ml_priv;
265
266         lbs_deb_enter(LBS_DEB_WEXT);
267
268         dwrq->length = strlen(priv->nodename);
269         memcpy(extra, priv->nodename, dwrq->length);
270         extra[dwrq->length] = '\0';
271
272         dwrq->flags = 1;        /* active */
273
274         lbs_deb_leave(LBS_DEB_WEXT);
275         return 0;
276 }
277
278 static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
279                          struct iw_point *dwrq, char *extra)
280 {
281         struct lbs_private *priv = dev->ml_priv;
282
283         lbs_deb_enter(LBS_DEB_WEXT);
284
285         /* Use nickname to indicate that mesh is on */
286
287         if (priv->mesh_connect_status == LBS_CONNECTED) {
288                 strncpy(extra, "Mesh", 12);
289                 extra[12] = '\0';
290                 dwrq->length = strlen(extra);
291         }
292
293         else {
294                 extra[0] = '\0';
295                 dwrq->length = 0;
296         }
297
298         lbs_deb_leave(LBS_DEB_WEXT);
299         return 0;
300 }
301
302 static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
303                         struct iw_param *vwrq, char *extra)
304 {
305         int ret = 0;
306         struct lbs_private *priv = dev->ml_priv;
307         u32 val = vwrq->value;
308
309         lbs_deb_enter(LBS_DEB_WEXT);
310
311         if (!lbs_is_cmd_allowed(priv)) {
312                 ret = -EBUSY;
313                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
314                 return ret;
315         }
316
317         if (vwrq->disabled)
318                 val = MRVDRV_RTS_MAX_VALUE;
319
320         if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
321                 return -EINVAL;
322
323         ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, (u16) val);
324
325         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
326         return ret;
327 }
328
329 static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
330                         struct iw_param *vwrq, char *extra)
331 {
332         struct lbs_private *priv = dev->ml_priv;
333         int ret = 0;
334         u16 val = 0;
335
336         lbs_deb_enter(LBS_DEB_WEXT);
337
338         if (!lbs_is_cmd_allowed(priv)) {
339                 ret = -EBUSY;
340                 goto out;
341         }
342
343         ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
344         if (ret)
345                 goto out;
346
347         vwrq->value = val;
348         vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
349         vwrq->fixed = 1;
350
351 out:
352         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
353         return ret;
354 }
355
356 static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
357                          struct iw_param *vwrq, char *extra)
358 {
359         struct lbs_private *priv = dev->ml_priv;
360         int ret = 0;
361         u32 val = vwrq->value;
362
363         lbs_deb_enter(LBS_DEB_WEXT);
364
365         if (!lbs_is_cmd_allowed(priv)) {
366                 ret = -EBUSY;
367                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
368                 return ret;
369         }
370
371         if (vwrq->disabled)
372                 val = MRVDRV_FRAG_MAX_VALUE;
373
374         if (val < MRVDRV_FRAG_MIN_VALUE || val > MRVDRV_FRAG_MAX_VALUE)
375                 return -EINVAL;
376
377         ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
378
379         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
380         return ret;
381 }
382
383 static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
384                          struct iw_param *vwrq, char *extra)
385 {
386         struct lbs_private *priv = dev->ml_priv;
387         int ret = 0;
388         u16 val = 0;
389
390         lbs_deb_enter(LBS_DEB_WEXT);
391
392         if (!lbs_is_cmd_allowed(priv)) {
393                 ret = -EBUSY;
394                 goto out;
395         }
396
397         ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
398         if (ret)
399                 goto out;
400
401         vwrq->value = val;
402         vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
403                           || (val > MRVDRV_FRAG_MAX_VALUE));
404         vwrq->fixed = 1;
405
406 out:
407         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
408         return ret;
409 }
410
411 static int lbs_get_mode(struct net_device *dev,
412                          struct iw_request_info *info, u32 * uwrq, char *extra)
413 {
414         struct lbs_private *priv = dev->ml_priv;
415
416         lbs_deb_enter(LBS_DEB_WEXT);
417
418         *uwrq = priv->mode;
419
420         lbs_deb_leave(LBS_DEB_WEXT);
421         return 0;
422 }
423
424 static int mesh_wlan_get_mode(struct net_device *dev,
425                               struct iw_request_info *info, u32 * uwrq,
426                               char *extra)
427 {
428         lbs_deb_enter(LBS_DEB_WEXT);
429
430         *uwrq = IW_MODE_REPEAT;
431
432         lbs_deb_leave(LBS_DEB_WEXT);
433         return 0;
434 }
435
436 static int lbs_get_txpow(struct net_device *dev,
437                           struct iw_request_info *info,
438                           struct iw_param *vwrq, char *extra)
439 {
440         struct lbs_private *priv = dev->ml_priv;
441         s16 curlevel = 0;
442         int ret = 0;
443
444         lbs_deb_enter(LBS_DEB_WEXT);
445
446         if (!lbs_is_cmd_allowed(priv)) {
447                 ret = -EBUSY;
448                 goto out;
449         }
450
451         if (!priv->radio_on) {
452                 lbs_deb_wext("tx power off\n");
453                 vwrq->value = 0;
454                 vwrq->disabled = 1;
455                 goto out;
456         }
457
458         ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
459         if (ret)
460                 goto out;
461
462         lbs_deb_wext("tx power level %d dbm\n", curlevel);
463         priv->txpower_cur = curlevel;
464
465         vwrq->value = curlevel;
466         vwrq->fixed = 1;
467         vwrq->disabled = 0;
468         vwrq->flags = IW_TXPOW_DBM;
469
470 out:
471         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
472         return ret;
473 }
474
475 static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
476                           struct iw_param *vwrq, char *extra)
477 {
478         struct lbs_private *priv = dev->ml_priv;
479         int ret = 0;
480         u16 slimit = 0, llimit = 0;
481
482         lbs_deb_enter(LBS_DEB_WEXT);
483
484         if (!lbs_is_cmd_allowed(priv)) {
485                 ret = -EBUSY;
486                 goto out;
487         }
488
489         if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
490                 return -EOPNOTSUPP;
491
492         /* The MAC has a 4-bit Total_Tx_Count register
493            Total_Tx_Count = 1 + Tx_Retry_Count */
494 #define TX_RETRY_MIN 0
495 #define TX_RETRY_MAX 14
496         if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
497                 return -EINVAL;
498
499         /* Add 1 to convert retry count to try count */
500         if (vwrq->flags & IW_RETRY_SHORT)
501                 slimit = (u16) (vwrq->value + 1);
502         else if (vwrq->flags & IW_RETRY_LONG)
503                 llimit = (u16) (vwrq->value + 1);
504         else
505                 slimit = llimit = (u16) (vwrq->value + 1); /* set both */
506
507         if (llimit) {
508                 ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
509                                        llimit);
510                 if (ret)
511                         goto out;
512         }
513
514         if (slimit) {
515                 /* txretrycount follows the short retry limit */
516                 priv->txretrycount = slimit;
517                 ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
518                                        slimit);
519                 if (ret)
520                         goto out;
521         }
522
523 out:
524         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
525         return ret;
526 }
527
528 static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
529                           struct iw_param *vwrq, char *extra)
530 {
531         struct lbs_private *priv = dev->ml_priv;
532         int ret = 0;
533         u16 val = 0;
534
535         lbs_deb_enter(LBS_DEB_WEXT);
536
537         if (!lbs_is_cmd_allowed(priv)) {
538                 ret = -EBUSY;
539                 goto out;
540         }
541
542         vwrq->disabled = 0;
543
544         if (vwrq->flags & IW_RETRY_LONG) {
545                 ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, &val);
546                 if (ret)
547                         goto out;
548
549                 /* Subtract 1 to convert try count to retry count */
550                 vwrq->value = val - 1;
551                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
552         } else {
553                 ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, &val);
554                 if (ret)
555                         goto out;
556
557                 /* txretry count follows the short retry limit */
558                 priv->txretrycount = val;
559                 /* Subtract 1 to convert try count to retry count */
560                 vwrq->value = val - 1;
561                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
562         }
563
564 out:
565         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
566         return ret;
567 }
568
569 static inline void sort_channels(struct iw_freq *freq, int num)
570 {
571         int i, j;
572         struct iw_freq temp;
573
574         for (i = 0; i < num; i++)
575                 for (j = i + 1; j < num; j++)
576                         if (freq[i].i > freq[j].i) {
577                                 temp.i = freq[i].i;
578                                 temp.m = freq[i].m;
579
580                                 freq[i].i = freq[j].i;
581                                 freq[i].m = freq[j].m;
582
583                                 freq[j].i = temp.i;
584                                 freq[j].m = temp.m;
585                         }
586 }
587
588 /* data rate listing
589         MULTI_BANDS:
590                 abg             a       b       b/g
591    Infra        G(12)           A(8)    B(4)    G(12)
592    Adhoc        A+B(12)         A(8)    B(4)    B(4)
593
594         non-MULTI_BANDS:
595                                         b       b/g
596    Infra                                B(4)    G(12)
597    Adhoc                                B(4)    B(4)
598  */
599 /**
600  *  @brief Get Range Info
601  *
602  *  @param dev                  A pointer to net_device structure
603  *  @param info                 A pointer to iw_request_info structure
604  *  @param vwrq                 A pointer to iw_param structure
605  *  @param extra                A pointer to extra data buf
606  *  @return                     0 --success, otherwise fail
607  */
608 static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
609                           struct iw_point *dwrq, char *extra)
610 {
611         int i, j;
612         struct lbs_private *priv = dev->ml_priv;
613         struct iw_range *range = (struct iw_range *)extra;
614         struct chan_freq_power *cfp;
615         u8 rates[MAX_RATES + 1];
616
617         u8 flag = 0;
618
619         lbs_deb_enter(LBS_DEB_WEXT);
620
621         dwrq->length = sizeof(struct iw_range);
622         memset(range, 0, sizeof(struct iw_range));
623
624         range->min_nwid = 0;
625         range->max_nwid = 0;
626
627         memset(rates, 0, sizeof(rates));
628         copy_active_data_rates(priv, rates);
629         range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
630         for (i = 0; i < range->num_bitrates; i++)
631                 range->bitrate[i] = rates[i] * 500000;
632         range->num_bitrates = i;
633         lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
634                range->num_bitrates);
635
636         range->num_frequency = 0;
637
638         range->scan_capa = IW_SCAN_CAPA_ESSID;
639
640         if (priv->enable11d &&
641             (priv->connect_status == LBS_CONNECTED ||
642             priv->mesh_connect_status == LBS_CONNECTED)) {
643                 u8 chan_no;
644                 u8 band;
645
646                 struct parsed_region_chan_11d *parsed_region_chan =
647                     &priv->parsed_region_chan;
648
649                 if (parsed_region_chan == NULL) {
650                         lbs_deb_wext("11d: parsed_region_chan is NULL\n");
651                         goto out;
652                 }
653                 band = parsed_region_chan->band;
654                 lbs_deb_wext("band %d, nr_char %d\n", band,
655                        parsed_region_chan->nr_chan);
656
657                 for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
658                      && (i < parsed_region_chan->nr_chan); i++) {
659                         chan_no = parsed_region_chan->chanpwr[i].chan;
660                         lbs_deb_wext("chan_no %d\n", chan_no);
661                         range->freq[range->num_frequency].i = (long)chan_no;
662                         range->freq[range->num_frequency].m =
663                             (long)lbs_chan_2_freq(chan_no) * 100000;
664                         range->freq[range->num_frequency].e = 1;
665                         range->num_frequency++;
666                 }
667                 flag = 1;
668         }
669         if (!flag) {
670                 for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
671                      && (j < ARRAY_SIZE(priv->region_channel)); j++) {
672                         cfp = priv->region_channel[j].CFP;
673                         for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
674                              && priv->region_channel[j].valid
675                              && cfp
676                              && (i < priv->region_channel[j].nrcfp); i++) {
677                                 range->freq[range->num_frequency].i =
678                                     (long)cfp->channel;
679                                 range->freq[range->num_frequency].m =
680                                     (long)cfp->freq * 100000;
681                                 range->freq[range->num_frequency].e = 1;
682                                 cfp++;
683                                 range->num_frequency++;
684                         }
685                 }
686         }
687
688         lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
689                IW_MAX_FREQUENCIES, range->num_frequency);
690
691         range->num_channels = range->num_frequency;
692
693         sort_channels(&range->freq[0], range->num_frequency);
694
695         /*
696          * Set an indication of the max TCP throughput in bit/s that we can
697          * expect using this interface
698          */
699         if (i > 2)
700                 range->throughput = 5000 * 1000;
701         else
702                 range->throughput = 1500 * 1000;
703
704         range->min_rts = MRVDRV_RTS_MIN_VALUE;
705         range->max_rts = MRVDRV_RTS_MAX_VALUE;
706         range->min_frag = MRVDRV_FRAG_MIN_VALUE;
707         range->max_frag = MRVDRV_FRAG_MAX_VALUE;
708
709         range->encoding_size[0] = 5;
710         range->encoding_size[1] = 13;
711         range->num_encoding_sizes = 2;
712         range->max_encoding_tokens = 4;
713
714         /*
715          * Right now we support only "iwconfig ethX power on|off"
716          */
717         range->pm_capa = IW_POWER_ON;
718
719         /*
720          * Minimum version we recommend
721          */
722         range->we_version_source = 15;
723
724         /*
725          * Version we are compiled with
726          */
727         range->we_version_compiled = WIRELESS_EXT;
728
729         range->retry_capa = IW_RETRY_LIMIT;
730         range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
731
732         range->min_retry = TX_RETRY_MIN;
733         range->max_retry = TX_RETRY_MAX;
734
735         /*
736          * Set the qual, level and noise range values
737          */
738         range->max_qual.qual = 100;
739         range->max_qual.level = 0;
740         range->max_qual.noise = 0;
741         range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
742
743         range->avg_qual.qual = 70;
744         /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
745         range->avg_qual.level = 0;
746         range->avg_qual.noise = 0;
747         range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
748
749         range->sensitivity = 0;
750
751         /* Setup the supported power level ranges */
752         memset(range->txpower, 0, sizeof(range->txpower));
753         range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE;
754         range->txpower[0] = priv->txpower_min;
755         range->txpower[1] = priv->txpower_max;
756         range->num_txpower = 2;
757
758         range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
759                                 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
760                                 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
761         range->event_capa[1] = IW_EVENT_CAPA_K_1;
762
763         if (priv->fwcapinfo & FW_CAPINFO_WPA) {
764                 range->enc_capa =   IW_ENC_CAPA_WPA
765                                   | IW_ENC_CAPA_WPA2
766                                   | IW_ENC_CAPA_CIPHER_TKIP
767                                   | IW_ENC_CAPA_CIPHER_CCMP;
768         }
769
770 out:
771         lbs_deb_leave(LBS_DEB_WEXT);
772         return 0;
773 }
774
775 static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
776                           struct iw_param *vwrq, char *extra)
777 {
778         struct lbs_private *priv = dev->ml_priv;
779         int ret = 0;
780
781         lbs_deb_enter(LBS_DEB_WEXT);
782
783         if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
784                 if (vwrq->disabled)
785                         return 0;
786                 else
787                         return -EINVAL;
788         }
789
790         /* PS is currently supported only in Infrastructure mode
791          * Remove this check if it is to be supported in IBSS mode also
792          */
793
794         if (vwrq->disabled) {
795                 priv->psmode = LBS802_11POWERMODECAM;
796                 if (priv->psstate != PS_STATE_FULL_POWER) {
797                         lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
798                 }
799
800                 return 0;
801         }
802
803         if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
804                 lbs_deb_wext(
805                        "setting power timeout is not supported\n");
806                 return -EINVAL;
807         } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
808                 vwrq->value = vwrq->value / 1000;
809                 if (!priv->enter_deep_sleep) {
810                         lbs_pr_err("deep sleep feature is not implemented "
811                                         "for this interface driver\n");
812                         return -EINVAL;
813                 }
814
815                 if (priv->connect_status == LBS_CONNECTED) {
816                         if ((priv->is_auto_deep_sleep_enabled) &&
817                                                 (vwrq->value == -1000)) {
818                                 lbs_exit_auto_deep_sleep(priv);
819                                 return 0;
820                         } else {
821                                 lbs_pr_err("can't use deep sleep cmd in "
822                                                 "connected state\n");
823                                 return -EINVAL;
824                         }
825                 }
826
827                 if ((vwrq->value < 0) && (vwrq->value != -1000)) {
828                         lbs_pr_err("unknown option\n");
829                         return -EINVAL;
830                 }
831
832                 if (vwrq->value > 0) {
833                         if (!priv->is_auto_deep_sleep_enabled) {
834                                 priv->is_activity_detected = 0;
835                                 priv->auto_deep_sleep_timeout = vwrq->value;
836                                 lbs_enter_auto_deep_sleep(priv);
837                         } else {
838                                 priv->auto_deep_sleep_timeout = vwrq->value;
839                                 lbs_deb_debugfs("auto deep sleep: "
840                                                 "already enabled\n");
841                         }
842                         return 0;
843                 } else {
844                         if (priv->is_auto_deep_sleep_enabled) {
845                                 lbs_exit_auto_deep_sleep(priv);
846                                 /* Try to exit deep sleep if auto */
847                                 /*deep sleep disabled */
848                                 ret = lbs_set_deep_sleep(priv, 0);
849                         }
850                         if (vwrq->value == 0)
851                                 ret = lbs_set_deep_sleep(priv, 1);
852                         else if (vwrq->value == -1000)
853                                 ret = lbs_set_deep_sleep(priv, 0);
854                         return ret;
855                 }
856         }
857
858         if (priv->psmode != LBS802_11POWERMODECAM) {
859                 return 0;
860         }
861
862         priv->psmode = LBS802_11POWERMODEMAX_PSP;
863
864         if (priv->connect_status == LBS_CONNECTED) {
865                 lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
866         }
867
868         lbs_deb_leave(LBS_DEB_WEXT);
869
870         return 0;
871 }
872
873 static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
874                           struct iw_param *vwrq, char *extra)
875 {
876         struct lbs_private *priv = dev->ml_priv;
877
878         lbs_deb_enter(LBS_DEB_WEXT);
879
880         vwrq->value = 0;
881         vwrq->flags = 0;
882         vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
883                 || priv->connect_status == LBS_DISCONNECTED;
884
885         lbs_deb_leave(LBS_DEB_WEXT);
886         return 0;
887 }
888
889 static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
890 {
891         enum {
892                 POOR = 30,
893                 FAIR = 60,
894                 GOOD = 80,
895                 VERY_GOOD = 90,
896                 EXCELLENT = 95,
897                 PERFECT = 100
898         };
899         struct lbs_private *priv = dev->ml_priv;
900         u32 rssi_qual;
901         u32 tx_qual;
902         u32 quality = 0;
903         int stats_valid = 0;
904         u8 rssi;
905         u32 tx_retries;
906         struct cmd_ds_802_11_get_log log;
907
908         lbs_deb_enter(LBS_DEB_WEXT);
909
910         if (!lbs_is_cmd_allowed(priv))
911                 return NULL;
912
913         priv->wstats.status = priv->mode;
914
915         /* If we're not associated, all quality values are meaningless */
916         if ((priv->connect_status != LBS_CONNECTED) &&
917             (priv->mesh_connect_status != LBS_CONNECTED))
918                 goto out;
919
920         /* Quality by RSSI */
921         priv->wstats.qual.level =
922             CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
923              priv->NF[TYPE_BEACON][TYPE_NOAVG]);
924
925         if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
926                 priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
927         } else {
928                 priv->wstats.qual.noise =
929                     CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
930         }
931
932         lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
933         lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
934
935         rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
936         if (rssi < 15)
937                 rssi_qual = rssi * POOR / 10;
938         else if (rssi < 20)
939                 rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
940         else if (rssi < 30)
941                 rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
942         else if (rssi < 40)
943                 rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
944                     10 + GOOD;
945         else
946                 rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
947                     10 + VERY_GOOD;
948         quality = rssi_qual;
949
950         /* Quality by TX errors */
951         priv->wstats.discard.retries = dev->stats.tx_errors;
952
953         memset(&log, 0, sizeof(log));
954         log.hdr.size = cpu_to_le16(sizeof(log));
955         lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
956
957         tx_retries = le32_to_cpu(log.retry);
958
959         if (tx_retries > 75)
960                 tx_qual = (90 - tx_retries) * POOR / 15;
961         else if (tx_retries > 70)
962                 tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
963         else if (tx_retries > 65)
964                 tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
965         else if (tx_retries > 50)
966                 tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
967                     15 + GOOD;
968         else
969                 tx_qual = (50 - tx_retries) *
970                     (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
971         quality = min(quality, tx_qual);
972
973         priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
974         priv->wstats.discard.retries = tx_retries;
975         priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
976
977         /* Calculate quality */
978         priv->wstats.qual.qual = min_t(u8, quality, 100);
979         priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
980         stats_valid = 1;
981
982         /* update stats asynchronously for future calls */
983         lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
984                                         0, 0, NULL);
985 out:
986         if (!stats_valid) {
987                 priv->wstats.miss.beacon = 0;
988                 priv->wstats.discard.retries = 0;
989                 priv->wstats.qual.qual = 0;
990                 priv->wstats.qual.level = 0;
991                 priv->wstats.qual.noise = 0;
992                 priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
993                 priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
994                     IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
995         }
996
997         lbs_deb_leave(LBS_DEB_WEXT);
998         return &priv->wstats;
999
1000
1001 }
1002
1003 static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
1004                   struct iw_freq *fwrq, char *extra)
1005 {
1006         int ret = -EINVAL;
1007         struct lbs_private *priv = dev->ml_priv;
1008         struct chan_freq_power *cfp;
1009         struct assoc_request * assoc_req;
1010
1011         lbs_deb_enter(LBS_DEB_WEXT);
1012
1013         if (!lbs_is_cmd_allowed(priv)) {
1014                 ret = -EBUSY;
1015                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1016                 return ret;
1017         }
1018
1019         mutex_lock(&priv->lock);
1020         assoc_req = lbs_get_association_request(priv);
1021         if (!assoc_req) {
1022                 ret = -ENOMEM;
1023                 goto out;
1024         }
1025
1026         /* If setting by frequency, convert to a channel */
1027         if (fwrq->e == 1) {
1028                 long f = fwrq->m / 100000;
1029
1030                 cfp = find_cfp_by_band_and_freq(priv, 0, f);
1031                 if (!cfp) {
1032                         lbs_deb_wext("invalid freq %ld\n", f);
1033                         goto out;
1034                 }
1035
1036                 fwrq->e = 0;
1037                 fwrq->m = (int) cfp->channel;
1038         }
1039
1040         /* Setting by channel number */
1041         if (fwrq->m > 1000 || fwrq->e > 0) {
1042                 goto out;
1043         }
1044
1045         cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
1046         if (!cfp) {
1047                 goto out;
1048         }
1049
1050         assoc_req->channel = fwrq->m;
1051         ret = 0;
1052
1053 out:
1054         if (ret == 0) {
1055                 set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
1056                 lbs_postpone_association_work(priv);
1057         } else {
1058                 lbs_cancel_association_work(priv);
1059         }
1060         mutex_unlock(&priv->lock);
1061
1062         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1063         return ret;
1064 }
1065
1066 static int lbs_mesh_set_freq(struct net_device *dev,
1067                              struct iw_request_info *info,
1068                              struct iw_freq *fwrq, char *extra)
1069 {
1070         struct lbs_private *priv = dev->ml_priv;
1071         struct chan_freq_power *cfp;
1072         int ret = -EINVAL;
1073
1074         lbs_deb_enter(LBS_DEB_WEXT);
1075
1076         /* If setting by frequency, convert to a channel */
1077         if (fwrq->e == 1) {
1078                 long f = fwrq->m / 100000;
1079
1080                 cfp = find_cfp_by_band_and_freq(priv, 0, f);
1081                 if (!cfp) {
1082                         lbs_deb_wext("invalid freq %ld\n", f);
1083                         goto out;
1084                 }
1085
1086                 fwrq->e = 0;
1087                 fwrq->m = (int) cfp->channel;
1088         }
1089
1090         /* Setting by channel number */
1091         if (fwrq->m > 1000 || fwrq->e > 0) {
1092                 goto out;
1093         }
1094
1095         cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
1096         if (!cfp) {
1097                 goto out;
1098         }
1099
1100         if (fwrq->m != priv->curbssparams.channel) {
1101                 lbs_deb_wext("mesh channel change forces eth disconnect\n");
1102                 if (priv->mode == IW_MODE_INFRA)
1103                         lbs_cmd_80211_deauthenticate(priv,
1104                                                      priv->curbssparams.bssid,
1105                                                      WLAN_REASON_DEAUTH_LEAVING);
1106                 else if (priv->mode == IW_MODE_ADHOC)
1107                         lbs_adhoc_stop(priv);
1108         }
1109         lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
1110         lbs_update_channel(priv);
1111         ret = 0;
1112
1113 out:
1114         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1115         return ret;
1116 }
1117
1118 static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
1119                   struct iw_param *vwrq, char *extra)
1120 {
1121         struct lbs_private *priv = dev->ml_priv;
1122         u8 new_rate = 0;
1123         int ret = -EINVAL;
1124         u8 rates[MAX_RATES + 1];
1125
1126         lbs_deb_enter(LBS_DEB_WEXT);
1127
1128         if (!lbs_is_cmd_allowed(priv)) {
1129                 ret = -EBUSY;
1130                 goto out;
1131         }
1132
1133         lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1134         lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
1135
1136         if (vwrq->fixed && vwrq->value == -1)
1137                 goto out;
1138
1139         /* Auto rate? */
1140         priv->enablehwauto = !vwrq->fixed;
1141
1142         if (vwrq->value == -1)
1143                 priv->cur_rate = 0;
1144         else {
1145                 if (vwrq->value % 100000)
1146                         goto out;
1147
1148                 new_rate = vwrq->value / 500000;
1149                 priv->cur_rate = new_rate;
1150                 /* the rest is only needed for lbs_set_data_rate() */
1151                 memset(rates, 0, sizeof(rates));
1152                 copy_active_data_rates(priv, rates);
1153                 if (!memchr(rates, new_rate, sizeof(rates))) {
1154                         lbs_pr_alert("fixed data rate 0x%X out of range\n",
1155                                 new_rate);
1156                         goto out;
1157                 }
1158                 if (priv->fwrelease < 0x09000000) {
1159                         ret = lbs_set_power_adapt_cfg(priv, 0,
1160                                         POW_ADAPT_DEFAULT_P0,
1161                                         POW_ADAPT_DEFAULT_P1,
1162                                         POW_ADAPT_DEFAULT_P2);
1163                         if (ret)
1164                                 goto out;
1165                 }
1166                 ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
1167                                 TPC_DEFAULT_P2, 1);
1168                 if (ret)
1169                         goto out;
1170         }
1171
1172         /* Try the newer command first (Firmware Spec 5.1 and above) */
1173         ret = lbs_cmd_802_11_rate_adapt_rateset(priv, CMD_ACT_SET);
1174
1175         /* Fallback to older version */
1176         if (ret)
1177                 ret = lbs_set_data_rate(priv, new_rate);
1178
1179 out:
1180         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1181         return ret;
1182 }
1183
1184 static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1185                   struct iw_param *vwrq, char *extra)
1186 {
1187         struct lbs_private *priv = dev->ml_priv;
1188
1189         lbs_deb_enter(LBS_DEB_WEXT);
1190
1191         if (!lbs_is_cmd_allowed(priv)) {
1192                 lbs_deb_leave(LBS_DEB_WEXT);
1193                 return -EBUSY;
1194         }
1195
1196         if (priv->connect_status == LBS_CONNECTED) {
1197                 vwrq->value = priv->cur_rate * 500000;
1198
1199                 if (priv->enablehwauto)
1200                         vwrq->fixed = 0;
1201                 else
1202                         vwrq->fixed = 1;
1203
1204         } else {
1205                 vwrq->fixed = 0;
1206                 vwrq->value = 0;
1207         }
1208
1209         lbs_deb_leave(LBS_DEB_WEXT);
1210         return 0;
1211 }
1212
1213 static int lbs_set_mode(struct net_device *dev,
1214                   struct iw_request_info *info, u32 * uwrq, char *extra)
1215 {
1216         int ret = 0;
1217         struct lbs_private *priv = dev->ml_priv;
1218         struct assoc_request * assoc_req;
1219
1220         lbs_deb_enter(LBS_DEB_WEXT);
1221
1222         if (!lbs_is_cmd_allowed(priv)) {
1223                 ret = -EBUSY;
1224                 goto out;
1225         }
1226
1227         if (   (*uwrq != IW_MODE_ADHOC)
1228             && (*uwrq != IW_MODE_INFRA)
1229             && (*uwrq != IW_MODE_AUTO)) {
1230                 lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1231                 ret = -EINVAL;
1232                 goto out;
1233         }
1234
1235         mutex_lock(&priv->lock);
1236         assoc_req = lbs_get_association_request(priv);
1237         if (!assoc_req) {
1238                 ret = -ENOMEM;
1239                 lbs_cancel_association_work(priv);
1240         } else {
1241                 assoc_req->mode = *uwrq;
1242                 set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1243                 lbs_postpone_association_work(priv);
1244                 lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1245         }
1246         mutex_unlock(&priv->lock);
1247
1248 out:
1249         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1250         return ret;
1251 }
1252
1253
1254 /**
1255  *  @brief Get Encryption key
1256  *
1257  *  @param dev                  A pointer to net_device structure
1258  *  @param info                 A pointer to iw_request_info structure
1259  *  @param vwrq                 A pointer to iw_param structure
1260  *  @param extra                A pointer to extra data buf
1261  *  @return                     0 --success, otherwise fail
1262  */
1263 static int lbs_get_encode(struct net_device *dev,
1264                            struct iw_request_info *info,
1265                            struct iw_point *dwrq, u8 * extra)
1266 {
1267         struct lbs_private *priv = dev->ml_priv;
1268         int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1269
1270         lbs_deb_enter(LBS_DEB_WEXT);
1271
1272         lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1273                dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1274
1275         dwrq->flags = 0;
1276
1277         /* Authentication method */
1278         switch (priv->secinfo.auth_mode) {
1279         case IW_AUTH_ALG_OPEN_SYSTEM:
1280                 dwrq->flags = IW_ENCODE_OPEN;
1281                 break;
1282
1283         case IW_AUTH_ALG_SHARED_KEY:
1284         case IW_AUTH_ALG_LEAP:
1285                 dwrq->flags = IW_ENCODE_RESTRICTED;
1286                 break;
1287         default:
1288                 dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
1289                 break;
1290         }
1291
1292         memset(extra, 0, 16);
1293
1294         mutex_lock(&priv->lock);
1295
1296         /* Default to returning current transmit key */
1297         if (index < 0)
1298                 index = priv->wep_tx_keyidx;
1299
1300         if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) {
1301                 memcpy(extra, priv->wep_keys[index].key,
1302                        priv->wep_keys[index].len);
1303                 dwrq->length = priv->wep_keys[index].len;
1304
1305                 dwrq->flags |= (index + 1);
1306                 /* Return WEP enabled */
1307                 dwrq->flags &= ~IW_ENCODE_DISABLED;
1308         } else if ((priv->secinfo.WPAenabled)
1309                    || (priv->secinfo.WPA2enabled)) {
1310                 /* return WPA enabled */
1311                 dwrq->flags &= ~IW_ENCODE_DISABLED;
1312                 dwrq->flags |= IW_ENCODE_NOKEY;
1313         } else {
1314                 dwrq->flags |= IW_ENCODE_DISABLED;
1315         }
1316
1317         mutex_unlock(&priv->lock);
1318
1319         lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1320                extra[0], extra[1], extra[2],
1321                extra[3], extra[4], extra[5], dwrq->length);
1322
1323         lbs_deb_wext("return flags 0x%x\n", dwrq->flags);
1324
1325         lbs_deb_leave(LBS_DEB_WEXT);
1326         return 0;
1327 }
1328
1329 /**
1330  *  @brief Set Encryption key (internal)
1331  *
1332  *  @param priv                 A pointer to private card structure
1333  *  @param key_material         A pointer to key material
1334  *  @param key_length           length of key material
1335  *  @param index                key index to set
1336  *  @param set_tx_key           Force set TX key (1 = yes, 0 = no)
1337  *  @return                     0 --success, otherwise fail
1338  */
1339 static int lbs_set_wep_key(struct assoc_request *assoc_req,
1340                             const char *key_material,
1341                             u16 key_length,
1342                             u16 index,
1343                             int set_tx_key)
1344 {
1345         int ret = 0;
1346         struct enc_key *pkey;
1347
1348         lbs_deb_enter(LBS_DEB_WEXT);
1349
1350         /* Paranoid validation of key index */
1351         if (index > 3) {
1352                 ret = -EINVAL;
1353                 goto out;
1354         }
1355
1356         /* validate max key length */
1357         if (key_length > KEY_LEN_WEP_104) {
1358                 ret = -EINVAL;
1359                 goto out;
1360         }
1361
1362         pkey = &assoc_req->wep_keys[index];
1363
1364         if (key_length > 0) {
1365                 memset(pkey, 0, sizeof(struct enc_key));
1366                 pkey->type = KEY_TYPE_ID_WEP;
1367
1368                 /* Standardize the key length */
1369                 pkey->len = (key_length > KEY_LEN_WEP_40) ?
1370                                 KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
1371                 memcpy(pkey->key, key_material, key_length);
1372         }
1373
1374         if (set_tx_key) {
1375                 /* Ensure the chosen key is valid */
1376                 if (!pkey->len) {
1377                         lbs_deb_wext("key not set, so cannot enable it\n");
1378                         ret = -EINVAL;
1379                         goto out;
1380                 }
1381                 assoc_req->wep_tx_keyidx = index;
1382         }
1383
1384         assoc_req->secinfo.wep_enabled = 1;
1385
1386 out:
1387         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1388         return ret;
1389 }
1390
1391 static int validate_key_index(u16 def_index, u16 raw_index,
1392                               u16 *out_index, u16 *is_default)
1393 {
1394         if (!out_index || !is_default)
1395                 return -EINVAL;
1396
1397         /* Verify index if present, otherwise use default TX key index */
1398         if (raw_index > 0) {
1399                 if (raw_index > 4)
1400                         return -EINVAL;
1401                 *out_index = raw_index - 1;
1402         } else {
1403                 *out_index = def_index;
1404                 *is_default = 1;
1405         }
1406         return 0;
1407 }
1408
1409 static void disable_wep(struct assoc_request *assoc_req)
1410 {
1411         int i;
1412
1413         lbs_deb_enter(LBS_DEB_WEXT);
1414
1415         /* Set Open System auth mode */
1416         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1417
1418         /* Clear WEP keys and mark WEP as disabled */
1419         assoc_req->secinfo.wep_enabled = 0;
1420         for (i = 0; i < 4; i++)
1421                 assoc_req->wep_keys[i].len = 0;
1422
1423         set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1424         set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1425
1426         lbs_deb_leave(LBS_DEB_WEXT);
1427 }
1428
1429 static void disable_wpa(struct assoc_request *assoc_req)
1430 {
1431         lbs_deb_enter(LBS_DEB_WEXT);
1432
1433         memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1434         assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
1435         set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
1436
1437         memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1438         assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
1439         set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
1440
1441         assoc_req->secinfo.WPAenabled = 0;
1442         assoc_req->secinfo.WPA2enabled = 0;
1443         set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1444
1445         lbs_deb_leave(LBS_DEB_WEXT);
1446 }
1447
1448 /**
1449  *  @brief Set Encryption key
1450  *
1451  *  @param dev                  A pointer to net_device structure
1452  *  @param info                 A pointer to iw_request_info structure
1453  *  @param vwrq                 A pointer to iw_param structure
1454  *  @param extra                A pointer to extra data buf
1455  *  @return                     0 --success, otherwise fail
1456  */
1457 static int lbs_set_encode(struct net_device *dev,
1458                     struct iw_request_info *info,
1459                     struct iw_point *dwrq, char *extra)
1460 {
1461         int ret = 0;
1462         struct lbs_private *priv = dev->ml_priv;
1463         struct assoc_request * assoc_req;
1464         u16 is_default = 0, index = 0, set_tx_key = 0;
1465
1466         lbs_deb_enter(LBS_DEB_WEXT);
1467
1468         if (!lbs_is_cmd_allowed(priv)) {
1469                 ret = -EBUSY;
1470                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1471                 return ret;
1472         }
1473
1474         mutex_lock(&priv->lock);
1475         assoc_req = lbs_get_association_request(priv);
1476         if (!assoc_req) {
1477                 ret = -ENOMEM;
1478                 goto out;
1479         }
1480
1481         if (dwrq->flags & IW_ENCODE_DISABLED) {
1482                 disable_wep (assoc_req);
1483                 disable_wpa (assoc_req);
1484                 goto out;
1485         }
1486
1487         ret = validate_key_index(assoc_req->wep_tx_keyidx,
1488                                  (dwrq->flags & IW_ENCODE_INDEX),
1489                                  &index, &is_default);
1490         if (ret) {
1491                 ret = -EINVAL;
1492                 goto out;
1493         }
1494
1495         /* If WEP isn't enabled, or if there is no key data but a valid
1496          * index, set the TX key.
1497          */
1498         if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1499                 set_tx_key = 1;
1500
1501         ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1502         if (ret)
1503                 goto out;
1504
1505         if (dwrq->length)
1506                 set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1507         if (set_tx_key)
1508                 set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
1509
1510         if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1511                 assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1512         } else if (dwrq->flags & IW_ENCODE_OPEN) {
1513                 assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1514         }
1515
1516 out:
1517         if (ret == 0) {
1518                 set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1519                 lbs_postpone_association_work(priv);
1520         } else {
1521                 lbs_cancel_association_work(priv);
1522         }
1523         mutex_unlock(&priv->lock);
1524
1525         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1526         return ret;
1527 }
1528
1529 /**
1530  *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
1531  *
1532  *  @param dev                  A pointer to net_device structure
1533  *  @param info                 A pointer to iw_request_info structure
1534  *  @param vwrq                 A pointer to iw_param structure
1535  *  @param extra                A pointer to extra data buf
1536  *  @return                     0 on success, otherwise failure
1537  */
1538 static int lbs_get_encodeext(struct net_device *dev,
1539                               struct iw_request_info *info,
1540                               struct iw_point *dwrq,
1541                               char *extra)
1542 {
1543         int ret = -EINVAL;
1544         struct lbs_private *priv = dev->ml_priv;
1545         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
1546         int index, max_key_len;
1547
1548         lbs_deb_enter(LBS_DEB_WEXT);
1549
1550         max_key_len = dwrq->length - sizeof(*ext);
1551         if (max_key_len < 0)
1552                 goto out;
1553
1554         index = dwrq->flags & IW_ENCODE_INDEX;
1555         if (index) {
1556                 if (index < 1 || index > 4)
1557                         goto out;
1558                 index--;
1559         } else {
1560                 index = priv->wep_tx_keyidx;
1561         }
1562
1563         if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1564             ext->alg != IW_ENCODE_ALG_WEP) {
1565                 if (index != 0 || priv->mode != IW_MODE_INFRA)
1566                         goto out;
1567         }
1568
1569         dwrq->flags = index + 1;
1570         memset(ext, 0, sizeof(*ext));
1571
1572         if (   !priv->secinfo.wep_enabled
1573             && !priv->secinfo.WPAenabled
1574             && !priv->secinfo.WPA2enabled) {
1575                 ext->alg = IW_ENCODE_ALG_NONE;
1576                 ext->key_len = 0;
1577                 dwrq->flags |= IW_ENCODE_DISABLED;
1578         } else {
1579                 u8 *key = NULL;
1580
1581                 if (   priv->secinfo.wep_enabled
1582                     && !priv->secinfo.WPAenabled
1583                     && !priv->secinfo.WPA2enabled) {
1584                         /* WEP */
1585                         ext->alg = IW_ENCODE_ALG_WEP;
1586                         ext->key_len = priv->wep_keys[index].len;
1587                         key = &priv->wep_keys[index].key[0];
1588                 } else if (   !priv->secinfo.wep_enabled
1589                            && (priv->secinfo.WPAenabled ||
1590                                priv->secinfo.WPA2enabled)) {
1591                         /* WPA */
1592                         struct enc_key * pkey = NULL;
1593
1594                         if (   priv->wpa_mcast_key.len
1595                             && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
1596                                 pkey = &priv->wpa_mcast_key;
1597                         else if (   priv->wpa_unicast_key.len
1598                                  && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
1599                                 pkey = &priv->wpa_unicast_key;
1600
1601                         if (pkey) {
1602                                 if (pkey->type == KEY_TYPE_ID_AES) {
1603                                         ext->alg = IW_ENCODE_ALG_CCMP;
1604                                 } else {
1605                                         ext->alg = IW_ENCODE_ALG_TKIP;
1606                                 }
1607                                 ext->key_len = pkey->len;
1608                                 key = &pkey->key[0];
1609                         } else {
1610                                 ext->alg = IW_ENCODE_ALG_TKIP;
1611                                 ext->key_len = 0;
1612                         }
1613                 } else {
1614                         goto out;
1615                 }
1616
1617                 if (ext->key_len > max_key_len) {
1618                         ret = -E2BIG;
1619                         goto out;
1620                 }
1621
1622                 if (ext->key_len)
1623                         memcpy(ext->key, key, ext->key_len);
1624                 else
1625                         dwrq->flags |= IW_ENCODE_NOKEY;
1626                 dwrq->flags |= IW_ENCODE_ENABLED;
1627         }
1628         ret = 0;
1629
1630 out:
1631         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1632         return ret;
1633 }
1634
1635 /**
1636  *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
1637  *
1638  *  @param dev                  A pointer to net_device structure
1639  *  @param info                 A pointer to iw_request_info structure
1640  *  @param vwrq                 A pointer to iw_param structure
1641  *  @param extra                A pointer to extra data buf
1642  *  @return                     0 --success, otherwise fail
1643  */
1644 static int lbs_set_encodeext(struct net_device *dev,
1645                               struct iw_request_info *info,
1646                               struct iw_point *dwrq,
1647                               char *extra)
1648 {
1649         int ret = 0;
1650         struct lbs_private *priv = dev->ml_priv;
1651         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
1652         int alg = ext->alg;
1653         struct assoc_request * assoc_req;
1654
1655         lbs_deb_enter(LBS_DEB_WEXT);
1656
1657         if (!lbs_is_cmd_allowed(priv)) {
1658                 ret = -EBUSY;
1659                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1660                 return ret;
1661         }
1662
1663         mutex_lock(&priv->lock);
1664         assoc_req = lbs_get_association_request(priv);
1665         if (!assoc_req) {
1666                 ret = -ENOMEM;
1667                 goto out;
1668         }
1669
1670         if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
1671                 disable_wep (assoc_req);
1672                 disable_wpa (assoc_req);
1673         } else if (alg == IW_ENCODE_ALG_WEP) {
1674                 u16 is_default = 0, index, set_tx_key = 0;
1675
1676                 ret = validate_key_index(assoc_req->wep_tx_keyidx,
1677                                          (dwrq->flags & IW_ENCODE_INDEX),
1678                                          &index, &is_default);
1679                 if (ret)
1680                         goto out;
1681
1682                 /* If WEP isn't enabled, or if there is no key data but a valid
1683                  * index, or if the set-TX-key flag was passed, set the TX key.
1684                  */
1685                 if (   !assoc_req->secinfo.wep_enabled
1686                     || (dwrq->length == 0 && !is_default)
1687                     || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
1688                         set_tx_key = 1;
1689
1690                 /* Copy key to driver */
1691                 ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1692                                         set_tx_key);
1693                 if (ret)
1694                         goto out;
1695
1696                 if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1697                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1698                 } else if (dwrq->flags & IW_ENCODE_OPEN) {
1699                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1700                 }
1701
1702                 /* Mark the various WEP bits as modified */
1703                 set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1704                 if (dwrq->length)
1705                         set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1706                 if (set_tx_key)
1707                         set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
1708         } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
1709                 struct enc_key * pkey;
1710
1711                 /* validate key length */
1712                 if (((alg == IW_ENCODE_ALG_TKIP)
1713                         && (ext->key_len != KEY_LEN_WPA_TKIP))
1714                     || ((alg == IW_ENCODE_ALG_CCMP)
1715                         && (ext->key_len != KEY_LEN_WPA_AES))) {
1716                                 lbs_deb_wext("invalid size %d for key of alg "
1717                                        "type %d\n",
1718                                        ext->key_len,
1719                                        alg);
1720                                 ret = -EINVAL;
1721                                 goto out;
1722                 }
1723
1724                 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1725                         pkey = &assoc_req->wpa_mcast_key;
1726                         set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
1727                 } else {
1728                         pkey = &assoc_req->wpa_unicast_key;
1729                         set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
1730                 }
1731
1732                 memset(pkey, 0, sizeof (struct enc_key));
1733                 memcpy(pkey->key, ext->key, ext->key_len);
1734                 pkey->len = ext->key_len;
1735                 if (pkey->len)
1736                         pkey->flags |= KEY_INFO_WPA_ENABLED;
1737
1738                 /* Do this after zeroing key structure */
1739                 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1740                         pkey->flags |= KEY_INFO_WPA_MCAST;
1741                 } else {
1742                         pkey->flags |= KEY_INFO_WPA_UNICAST;
1743                 }
1744
1745                 if (alg == IW_ENCODE_ALG_TKIP) {
1746                         pkey->type = KEY_TYPE_ID_TKIP;
1747                 } else if (alg == IW_ENCODE_ALG_CCMP) {
1748                         pkey->type = KEY_TYPE_ID_AES;
1749                 }
1750
1751                 /* If WPA isn't enabled yet, do that now */
1752                 if (   assoc_req->secinfo.WPAenabled == 0
1753                     && assoc_req->secinfo.WPA2enabled == 0) {
1754                         assoc_req->secinfo.WPAenabled = 1;
1755                         assoc_req->secinfo.WPA2enabled = 1;
1756                         set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1757                 }
1758
1759                 /* Only disable wep if necessary: can't waste time here. */
1760                 if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
1761                         disable_wep(assoc_req);
1762         }
1763
1764 out:
1765         if (ret == 0) {
1766                 /* 802.1x and WPA rekeying must happen as quickly as possible,
1767                  * especially during the 4-way handshake; thus if in
1768                  * infrastructure mode, and either (a) 802.1x is enabled or
1769                  * (b) WPA is being used, set the key right away.
1770                  */
1771                 if (assoc_req->mode == IW_MODE_INFRA &&
1772                     ((assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ||
1773                      (assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_PSK) ||
1774                       assoc_req->secinfo.WPAenabled ||
1775                       assoc_req->secinfo.WPA2enabled)) {
1776                         lbs_do_association_work(priv);
1777                 } else
1778                         lbs_postpone_association_work(priv);
1779         } else {
1780                 lbs_cancel_association_work(priv);
1781         }
1782         mutex_unlock(&priv->lock);
1783
1784         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1785         return ret;
1786 }
1787
1788
1789 static int lbs_set_genie(struct net_device *dev,
1790                           struct iw_request_info *info,
1791                           struct iw_point *dwrq,
1792                           char *extra)
1793 {
1794         struct lbs_private *priv = dev->ml_priv;
1795         int ret = 0;
1796         struct assoc_request * assoc_req;
1797
1798         lbs_deb_enter(LBS_DEB_WEXT);
1799
1800         mutex_lock(&priv->lock);
1801         assoc_req = lbs_get_association_request(priv);
1802         if (!assoc_req) {
1803                 ret = -ENOMEM;
1804                 goto out;
1805         }
1806
1807         if (dwrq->length > MAX_WPA_IE_LEN ||
1808             (dwrq->length && extra == NULL)) {
1809                 ret = -EINVAL;
1810                 goto out;
1811         }
1812
1813         if (dwrq->length) {
1814                 memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
1815                 assoc_req->wpa_ie_len = dwrq->length;
1816         } else {
1817                 memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1818                 assoc_req->wpa_ie_len = 0;
1819         }
1820
1821 out:
1822         if (ret == 0) {
1823                 set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1824                 lbs_postpone_association_work(priv);
1825         } else {
1826                 lbs_cancel_association_work(priv);
1827         }
1828         mutex_unlock(&priv->lock);
1829
1830         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1831         return ret;
1832 }
1833
1834 static int lbs_get_genie(struct net_device *dev,
1835                           struct iw_request_info *info,
1836                           struct iw_point *dwrq,
1837                           char *extra)
1838 {
1839         int ret = 0;
1840         struct lbs_private *priv = dev->ml_priv;
1841
1842         lbs_deb_enter(LBS_DEB_WEXT);
1843
1844         if (priv->wpa_ie_len == 0) {
1845                 dwrq->length = 0;
1846                 goto out;
1847         }
1848
1849         if (dwrq->length < priv->wpa_ie_len) {
1850                 ret = -E2BIG;
1851                 goto out;
1852         }
1853
1854         dwrq->length = priv->wpa_ie_len;
1855         memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1856
1857 out:
1858         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1859         return ret;
1860 }
1861
1862
1863 static int lbs_set_auth(struct net_device *dev,
1864                          struct iw_request_info *info,
1865                          struct iw_param *dwrq,
1866                          char *extra)
1867 {
1868         struct lbs_private *priv = dev->ml_priv;
1869         struct assoc_request * assoc_req;
1870         int ret = 0;
1871         int updated = 0;
1872
1873         lbs_deb_enter(LBS_DEB_WEXT);
1874
1875         if (!lbs_is_cmd_allowed(priv)) {
1876                 ret = -EBUSY;
1877                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1878                 return ret;
1879         }
1880
1881         mutex_lock(&priv->lock);
1882         assoc_req = lbs_get_association_request(priv);
1883         if (!assoc_req) {
1884                 ret = -ENOMEM;
1885                 goto out;
1886         }
1887
1888         switch (dwrq->flags & IW_AUTH_INDEX) {
1889         case IW_AUTH_PRIVACY_INVOKED:
1890         case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1891         case IW_AUTH_TKIP_COUNTERMEASURES:
1892         case IW_AUTH_CIPHER_PAIRWISE:
1893         case IW_AUTH_CIPHER_GROUP:
1894         case IW_AUTH_DROP_UNENCRYPTED:
1895                 /*
1896                  * libertas does not use these parameters
1897                  */
1898                 break;
1899
1900         case IW_AUTH_KEY_MGMT:
1901                 assoc_req->secinfo.key_mgmt = dwrq->value;
1902                 updated = 1;
1903                 break;
1904
1905         case IW_AUTH_WPA_VERSION:
1906                 if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
1907                         assoc_req->secinfo.WPAenabled = 0;
1908                         assoc_req->secinfo.WPA2enabled = 0;
1909                         disable_wpa (assoc_req);
1910                 }
1911                 if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
1912                         assoc_req->secinfo.WPAenabled = 1;
1913                         assoc_req->secinfo.wep_enabled = 0;
1914                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1915                 }
1916                 if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
1917                         assoc_req->secinfo.WPA2enabled = 1;
1918                         assoc_req->secinfo.wep_enabled = 0;
1919                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1920                 }
1921                 updated = 1;
1922                 break;
1923
1924         case IW_AUTH_80211_AUTH_ALG:
1925                 if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1926                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1927                 } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1928                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1929                 } else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1930                         assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1931                 } else {
1932                         ret = -EINVAL;
1933                 }
1934                 updated = 1;
1935                 break;
1936
1937         case IW_AUTH_WPA_ENABLED:
1938                 if (dwrq->value) {
1939                         if (!assoc_req->secinfo.WPAenabled &&
1940                             !assoc_req->secinfo.WPA2enabled) {
1941                                 assoc_req->secinfo.WPAenabled = 1;
1942                                 assoc_req->secinfo.WPA2enabled = 1;
1943                                 assoc_req->secinfo.wep_enabled = 0;
1944                                 assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1945                         }
1946                 } else {
1947                         assoc_req->secinfo.WPAenabled = 0;
1948                         assoc_req->secinfo.WPA2enabled = 0;
1949                         disable_wpa (assoc_req);
1950                 }
1951                 updated = 1;
1952                 break;
1953
1954         default:
1955                 ret = -EOPNOTSUPP;
1956                 break;
1957         }
1958
1959 out:
1960         if (ret == 0) {
1961                 if (updated)
1962                         set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1963                 lbs_postpone_association_work(priv);
1964         } else if (ret != -EOPNOTSUPP) {
1965                 lbs_cancel_association_work(priv);
1966         }
1967         mutex_unlock(&priv->lock);
1968
1969         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1970         return ret;
1971 }
1972
1973 static int lbs_get_auth(struct net_device *dev,
1974                          struct iw_request_info *info,
1975                          struct iw_param *dwrq,
1976                          char *extra)
1977 {
1978         int ret = 0;
1979         struct lbs_private *priv = dev->ml_priv;
1980
1981         lbs_deb_enter(LBS_DEB_WEXT);
1982
1983         if (!lbs_is_cmd_allowed(priv)) {
1984                 ret = -EBUSY;
1985                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1986                 return ret;
1987         }
1988
1989         switch (dwrq->flags & IW_AUTH_INDEX) {
1990         case IW_AUTH_KEY_MGMT:
1991                 dwrq->value = priv->secinfo.key_mgmt;
1992                 break;
1993
1994         case IW_AUTH_WPA_VERSION:
1995                 dwrq->value = 0;
1996                 if (priv->secinfo.WPAenabled)
1997                         dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1998                 if (priv->secinfo.WPA2enabled)
1999                         dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
2000                 if (!dwrq->value)
2001                         dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
2002                 break;
2003
2004         case IW_AUTH_80211_AUTH_ALG:
2005                 dwrq->value = priv->secinfo.auth_mode;
2006                 break;
2007
2008         case IW_AUTH_WPA_ENABLED:
2009                 if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
2010                         dwrq->value = 1;
2011                 break;
2012
2013         default:
2014                 ret = -EOPNOTSUPP;
2015         }
2016
2017         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2018         return ret;
2019 }
2020
2021
2022 static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
2023                    struct iw_param *vwrq, char *extra)
2024 {
2025         int ret = 0;
2026         struct lbs_private *priv = dev->ml_priv;
2027         s16 dbm = (s16) vwrq->value;
2028
2029         lbs_deb_enter(LBS_DEB_WEXT);
2030
2031         if (!lbs_is_cmd_allowed(priv)) {
2032                 ret = -EBUSY;
2033                 goto out;
2034         }
2035
2036         if (vwrq->disabled) {
2037                 lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
2038                 goto out;
2039         }
2040
2041         if (vwrq->fixed == 0) {
2042                 /* User requests automatic tx power control, however there are
2043                  * many auto tx settings.  For now use firmware defaults until
2044                  * we come up with a good way to expose these to the user. */
2045                 if (priv->fwrelease < 0x09000000) {
2046                         ret = lbs_set_power_adapt_cfg(priv, 1,
2047                                         POW_ADAPT_DEFAULT_P0,
2048                                         POW_ADAPT_DEFAULT_P1,
2049                                         POW_ADAPT_DEFAULT_P2);
2050                         if (ret)
2051                                 goto out;
2052                 }
2053                 ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
2054                                 TPC_DEFAULT_P2, 1);
2055                 if (ret)
2056                         goto out;
2057                 dbm = priv->txpower_max;
2058         } else {
2059                 /* Userspace check in iwrange if it should use dBm or mW,
2060                  * therefore this should never happen... Jean II */
2061                 if ((vwrq->flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
2062                         ret = -EOPNOTSUPP;
2063                         goto out;
2064                 }
2065
2066                 /* Validate requested power level against firmware allowed
2067                  * levels */
2068                 if (priv->txpower_min && (dbm < priv->txpower_min)) {
2069                         ret = -EINVAL;
2070                         goto out;
2071                 }
2072
2073                 if (priv->txpower_max && (dbm > priv->txpower_max)) {
2074                         ret = -EINVAL;
2075                         goto out;
2076                 }
2077                 if (priv->fwrelease < 0x09000000) {
2078                         ret = lbs_set_power_adapt_cfg(priv, 0,
2079                                         POW_ADAPT_DEFAULT_P0,
2080                                         POW_ADAPT_DEFAULT_P1,
2081                                         POW_ADAPT_DEFAULT_P2);
2082                         if (ret)
2083                                 goto out;
2084                 }
2085                 ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
2086                                 TPC_DEFAULT_P2, 1);
2087                 if (ret)
2088                         goto out;
2089         }
2090
2091         /* If the radio was off, turn it on */
2092         if (!priv->radio_on) {
2093                 ret = lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 1);
2094                 if (ret)
2095                         goto out;
2096         }
2097
2098         lbs_deb_wext("txpower set %d dBm\n", dbm);
2099
2100         ret = lbs_set_tx_power(priv, dbm);
2101
2102 out:
2103         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2104         return ret;
2105 }
2106
2107 static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
2108                    struct iw_point *dwrq, char *extra)
2109 {
2110         struct lbs_private *priv = dev->ml_priv;
2111
2112         lbs_deb_enter(LBS_DEB_WEXT);
2113
2114         /*
2115          * Note : if dwrq->flags != 0, we should get the relevant SSID from
2116          * the SSID list...
2117          */
2118
2119         /*
2120          * Get the current SSID
2121          */
2122         if (priv->connect_status == LBS_CONNECTED) {
2123                 memcpy(extra, priv->curbssparams.ssid,
2124                        priv->curbssparams.ssid_len);
2125                 extra[priv->curbssparams.ssid_len] = '\0';
2126         } else {
2127                 memset(extra, 0, 32);
2128                 extra[priv->curbssparams.ssid_len] = '\0';
2129         }
2130         /*
2131          * If none, we may want to get the one that was set
2132          */
2133
2134         dwrq->length = priv->curbssparams.ssid_len;
2135
2136         dwrq->flags = 1;        /* active */
2137
2138         lbs_deb_leave(LBS_DEB_WEXT);
2139         return 0;
2140 }
2141
2142 static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
2143                    struct iw_point *dwrq, char *extra)
2144 {
2145         struct lbs_private *priv = dev->ml_priv;
2146         int ret = 0;
2147         u8 ssid[IW_ESSID_MAX_SIZE];
2148         u8 ssid_len = 0;
2149         struct assoc_request * assoc_req;
2150         int in_ssid_len = dwrq->length;
2151         DECLARE_SSID_BUF(ssid_buf);
2152
2153         lbs_deb_enter(LBS_DEB_WEXT);
2154
2155         if (!lbs_is_cmd_allowed(priv)) {
2156                 ret = -EBUSY;
2157                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2158                 return ret;
2159         }
2160
2161         if (!priv->radio_on) {
2162                 ret = -EINVAL;
2163                 goto out;
2164         }
2165
2166         /* Check the size of the string */
2167         if (in_ssid_len > IW_ESSID_MAX_SIZE) {
2168                 ret = -E2BIG;
2169                 goto out;
2170         }
2171
2172         memset(&ssid, 0, sizeof(ssid));
2173
2174         if (!dwrq->flags || !in_ssid_len) {
2175                 /* "any" SSID requested; leave SSID blank */
2176         } else {
2177                 /* Specific SSID requested */
2178                 memcpy(&ssid, extra, in_ssid_len);
2179                 ssid_len = in_ssid_len;
2180         }
2181
2182         if (!ssid_len) {
2183                 lbs_deb_wext("requested any SSID\n");
2184         } else {
2185                 lbs_deb_wext("requested SSID '%s'\n",
2186                              print_ssid(ssid_buf, ssid, ssid_len));
2187         }
2188
2189 out:
2190         mutex_lock(&priv->lock);
2191         if (ret == 0) {
2192                 /* Get or create the current association request */
2193                 assoc_req = lbs_get_association_request(priv);
2194                 if (!assoc_req) {
2195                         ret = -ENOMEM;
2196                 } else {
2197                         /* Copy the SSID to the association request */
2198                         memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
2199                         assoc_req->ssid_len = ssid_len;
2200                         set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2201                         lbs_postpone_association_work(priv);
2202                 }
2203         }
2204
2205         /* Cancel the association request if there was an error */
2206         if (ret != 0) {
2207                 lbs_cancel_association_work(priv);
2208         }
2209
2210         mutex_unlock(&priv->lock);
2211
2212         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2213         return ret;
2214 }
2215
2216 static int lbs_mesh_get_essid(struct net_device *dev,
2217                               struct iw_request_info *info,
2218                               struct iw_point *dwrq, char *extra)
2219 {
2220         struct lbs_private *priv = dev->ml_priv;
2221
2222         lbs_deb_enter(LBS_DEB_WEXT);
2223
2224         memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len);
2225
2226         dwrq->length = priv->mesh_ssid_len;
2227
2228         dwrq->flags = 1;        /* active */
2229
2230         lbs_deb_leave(LBS_DEB_WEXT);
2231         return 0;
2232 }
2233
2234 static int lbs_mesh_set_essid(struct net_device *dev,
2235                               struct iw_request_info *info,
2236                               struct iw_point *dwrq, char *extra)
2237 {
2238         struct lbs_private *priv = dev->ml_priv;
2239         int ret = 0;
2240
2241         lbs_deb_enter(LBS_DEB_WEXT);
2242
2243         if (!priv->radio_on) {
2244                 ret = -EINVAL;
2245                 goto out;
2246         }
2247
2248         /* Check the size of the string */
2249         if (dwrq->length > IW_ESSID_MAX_SIZE) {
2250                 ret = -E2BIG;
2251                 goto out;
2252         }
2253
2254         if (!dwrq->flags || !dwrq->length) {
2255                 ret = -EINVAL;
2256                 goto out;
2257         } else {
2258                 /* Specific SSID requested */
2259                 memcpy(priv->mesh_ssid, extra, dwrq->length);
2260                 priv->mesh_ssid_len = dwrq->length;
2261         }
2262
2263         lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
2264                         priv->curbssparams.channel);
2265  out:
2266         lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2267         return ret;
2268 }
2269
2270 /**
2271  *  @brief Connect to the AP or Ad-hoc Network with specific bssid
2272  *
2273  *  @param dev          A pointer to net_device structure
2274  *  @param info         A pointer to iw_request_info structure
2275  *  @param awrq         A pointer to iw_param structure
2276  *  @param extra        A pointer to extra data buf
2277  *  @return             0 --success, otherwise fail
2278  */
2279 static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2280                  struct sockaddr *awrq, char *extra)
2281 {
2282         struct lbs_private *priv = dev->ml_priv;
2283         struct assoc_request * assoc_req;
2284         int ret = 0;
2285
2286         lbs_deb_enter(LBS_DEB_WEXT);
2287
2288         if (!lbs_is_cmd_allowed(priv)) {
2289                 ret = -EBUSY;
2290                 lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2291                 return ret;
2292         }
2293
2294         if (!priv->radio_on)
2295                 return -EINVAL;
2296
2297         if (awrq->sa_family != ARPHRD_ETHER)
2298                 return -EINVAL;
2299
2300         lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
2301
2302         mutex_lock(&priv->lock);
2303
2304         /* Get or create the current association request */
2305         assoc_req = lbs_get_association_request(priv);
2306         if (!assoc_req) {
2307                 lbs_cancel_association_work(priv);
2308                 ret = -ENOMEM;
2309         } else {
2310                 /* Copy the BSSID to the association request */
2311                 memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
2312                 set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
2313                 lbs_postpone_association_work(priv);
2314         }
2315
2316         mutex_unlock(&priv->lock);
2317
2318         return ret;
2319 }
2320
2321 /*
2322  * iwconfig settable callbacks
2323  */
2324 static const iw_handler lbs_handler[] = {
2325         (iw_handler) NULL,      /* SIOCSIWCOMMIT */
2326         (iw_handler) lbs_get_name,      /* SIOCGIWNAME */
2327         (iw_handler) NULL,      /* SIOCSIWNWID */
2328         (iw_handler) NULL,      /* SIOCGIWNWID */
2329         (iw_handler) lbs_set_freq,      /* SIOCSIWFREQ */
2330         (iw_handler) lbs_get_freq,      /* SIOCGIWFREQ */
2331         (iw_handler) lbs_set_mode,      /* SIOCSIWMODE */
2332         (iw_handler) lbs_get_mode,      /* SIOCGIWMODE */
2333         (iw_handler) NULL,      /* SIOCSIWSENS */
2334         (iw_handler) NULL,      /* SIOCGIWSENS */
2335         (iw_handler) NULL,      /* SIOCSIWRANGE */
2336         (iw_handler) lbs_get_range,     /* SIOCGIWRANGE */
2337         (iw_handler) NULL,      /* SIOCSIWPRIV */
2338         (iw_handler) NULL,      /* SIOCGIWPRIV */
2339         (iw_handler) NULL,      /* SIOCSIWSTATS */
2340         (iw_handler) NULL,      /* SIOCGIWSTATS */
2341         iw_handler_set_spy,     /* SIOCSIWSPY */
2342         iw_handler_get_spy,     /* SIOCGIWSPY */
2343         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2344         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2345         (iw_handler) lbs_set_wap,       /* SIOCSIWAP */
2346         (iw_handler) lbs_get_wap,       /* SIOCGIWAP */
2347         (iw_handler) NULL,      /* SIOCSIWMLME */
2348         (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
2349         (iw_handler) lbs_set_scan,      /* SIOCSIWSCAN */
2350         (iw_handler) lbs_get_scan,      /* SIOCGIWSCAN */
2351         (iw_handler) lbs_set_essid,     /* SIOCSIWESSID */
2352         (iw_handler) lbs_get_essid,     /* SIOCGIWESSID */
2353         (iw_handler) lbs_set_nick,      /* SIOCSIWNICKN */
2354         (iw_handler) lbs_get_nick,      /* SIOCGIWNICKN */
2355         (iw_handler) NULL,      /* -- hole -- */
2356         (iw_handler) NULL,      /* -- hole -- */
2357         (iw_handler) lbs_set_rate,      /* SIOCSIWRATE */
2358         (iw_handler) lbs_get_rate,      /* SIOCGIWRATE */
2359         (iw_handler) lbs_set_rts,       /* SIOCSIWRTS */
2360         (iw_handler) lbs_get_rts,       /* SIOCGIWRTS */
2361         (iw_handler) lbs_set_frag,      /* SIOCSIWFRAG */
2362         (iw_handler) lbs_get_frag,      /* SIOCGIWFRAG */
2363         (iw_handler) lbs_set_txpow,     /* SIOCSIWTXPOW */
2364         (iw_handler) lbs_get_txpow,     /* SIOCGIWTXPOW */
2365         (iw_handler) lbs_set_retry,     /* SIOCSIWRETRY */
2366         (iw_handler) lbs_get_retry,     /* SIOCGIWRETRY */
2367         (iw_handler) lbs_set_encode,    /* SIOCSIWENCODE */
2368         (iw_handler) lbs_get_encode,    /* SIOCGIWENCODE */
2369         (iw_handler) lbs_set_power,     /* SIOCSIWPOWER */
2370         (iw_handler) lbs_get_power,     /* SIOCGIWPOWER */
2371         (iw_handler) NULL,      /* -- hole -- */
2372         (iw_handler) NULL,      /* -- hole -- */
2373         (iw_handler) lbs_set_genie,     /* SIOCSIWGENIE */
2374         (iw_handler) lbs_get_genie,     /* SIOCGIWGENIE */
2375         (iw_handler) lbs_set_auth,      /* SIOCSIWAUTH */
2376         (iw_handler) lbs_get_auth,      /* SIOCGIWAUTH */
2377         (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
2378         (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2379         (iw_handler) NULL,              /* SIOCSIWPMKSA */
2380 };
2381
2382 static const iw_handler mesh_wlan_handler[] = {
2383         (iw_handler) NULL,      /* SIOCSIWCOMMIT */
2384         (iw_handler) lbs_get_name,      /* SIOCGIWNAME */
2385         (iw_handler) NULL,      /* SIOCSIWNWID */
2386         (iw_handler) NULL,      /* SIOCGIWNWID */
2387         (iw_handler) lbs_mesh_set_freq, /* SIOCSIWFREQ */
2388         (iw_handler) lbs_get_freq,      /* SIOCGIWFREQ */
2389         (iw_handler) NULL,              /* SIOCSIWMODE */
2390         (iw_handler) mesh_wlan_get_mode,        /* SIOCGIWMODE */
2391         (iw_handler) NULL,      /* SIOCSIWSENS */
2392         (iw_handler) NULL,      /* SIOCGIWSENS */
2393         (iw_handler) NULL,      /* SIOCSIWRANGE */
2394         (iw_handler) lbs_get_range,     /* SIOCGIWRANGE */
2395         (iw_handler) NULL,      /* SIOCSIWPRIV */
2396         (iw_handler) NULL,      /* SIOCGIWPRIV */
2397         (iw_handler) NULL,      /* SIOCSIWSTATS */
2398         (iw_handler) NULL,      /* SIOCGIWSTATS */
2399         iw_handler_set_spy,     /* SIOCSIWSPY */
2400         iw_handler_get_spy,     /* SIOCGIWSPY */
2401         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2402         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2403         (iw_handler) NULL,      /* SIOCSIWAP */
2404         (iw_handler) NULL,      /* SIOCGIWAP */
2405         (iw_handler) NULL,      /* SIOCSIWMLME */
2406         (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
2407         (iw_handler) lbs_set_scan,      /* SIOCSIWSCAN */
2408         (iw_handler) lbs_get_scan,      /* SIOCGIWSCAN */
2409         (iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
2410         (iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2411         (iw_handler) NULL,              /* SIOCSIWNICKN */
2412         (iw_handler) mesh_get_nick,     /* SIOCGIWNICKN */
2413         (iw_handler) NULL,      /* -- hole -- */
2414         (iw_handler) NULL,      /* -- hole -- */
2415         (iw_handler) lbs_set_rate,      /* SIOCSIWRATE */
2416         (iw_handler) lbs_get_rate,      /* SIOCGIWRATE */
2417         (iw_handler) lbs_set_rts,       /* SIOCSIWRTS */
2418         (iw_handler) lbs_get_rts,       /* SIOCGIWRTS */
2419         (iw_handler) lbs_set_frag,      /* SIOCSIWFRAG */
2420         (iw_handler) lbs_get_frag,      /* SIOCGIWFRAG */
2421         (iw_handler) lbs_set_txpow,     /* SIOCSIWTXPOW */
2422         (iw_handler) lbs_get_txpow,     /* SIOCGIWTXPOW */
2423         (iw_handler) lbs_set_retry,     /* SIOCSIWRETRY */
2424         (iw_handler) lbs_get_retry,     /* SIOCGIWRETRY */
2425         (iw_handler) lbs_set_encode,    /* SIOCSIWENCODE */
2426         (iw_handler) lbs_get_encode,    /* SIOCGIWENCODE */
2427         (iw_handler) lbs_set_power,     /* SIOCSIWPOWER */
2428         (iw_handler) lbs_get_power,     /* SIOCGIWPOWER */
2429         (iw_handler) NULL,      /* -- hole -- */
2430         (iw_handler) NULL,      /* -- hole -- */
2431         (iw_handler) lbs_set_genie,     /* SIOCSIWGENIE */
2432         (iw_handler) lbs_get_genie,     /* SIOCGIWGENIE */
2433         (iw_handler) lbs_set_auth,      /* SIOCSIWAUTH */
2434         (iw_handler) lbs_get_auth,      /* SIOCGIWAUTH */
2435         (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
2436         (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2437         (iw_handler) NULL,              /* SIOCSIWPMKSA */
2438 };
2439 struct iw_handler_def lbs_handler_def = {
2440         .num_standard   = ARRAY_SIZE(lbs_handler),
2441         .standard       = (iw_handler *) lbs_handler,
2442         .get_wireless_stats = lbs_get_wireless_stats,
2443 };
2444
2445 struct iw_handler_def mesh_handler_def = {
2446         .num_standard   = ARRAY_SIZE(mesh_wlan_handler),
2447         .standard       = (iw_handler *) mesh_wlan_handler,
2448         .get_wireless_stats = lbs_get_wireless_stats,
2449 };