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