]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - drivers/net/wireless/bcm43xx/bcm43xx_wx.c
Merge branch 'master' into upstream
[linux-2.6.git] / drivers / net / wireless / bcm43xx / bcm43xx_wx.c
1 /*
2
3   Broadcom BCM43xx wireless driver
4
5   Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
6                      Stefano Brivio <st3@riseup.net>
7                      Michael Buesch <mbuesch@freenet.de>
8                      Danny van Dyk <kugelfang@gentoo.org>
9                      Andreas Jaggi <andreas.jaggi@waterwave.ch>
10
11   Some parts of the code in this file are derived from the ipw2200
12   driver  Copyright(c) 2003 - 2004 Intel Corporation.
13
14   This program is free software; you can redistribute it and/or modify
15   it under the terms of the GNU General Public License as published by
16   the Free Software Foundation; either version 2 of the License, or
17   (at your option) any later version.
18
19   This program is distributed in the hope that it will be useful,
20   but WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22   GNU General Public License for more details.
23
24   You should have received a copy of the GNU General Public License
25   along with this program; see the file COPYING.  If not, write to
26   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
27   Boston, MA 02110-1301, USA.
28
29 */
30
31 #include <linux/wireless.h>
32 #include <net/iw_handler.h>
33 #include <net/ieee80211softmac.h>
34 #include <net/ieee80211softmac_wx.h>
35 #include <linux/capability.h>
36 #include <linux/delay.h>
37
38 #include "bcm43xx.h"
39 #include "bcm43xx_wx.h"
40 #include "bcm43xx_main.h"
41 #include "bcm43xx_radio.h"
42 #include "bcm43xx_phy.h"
43
44
45 /* The WIRELESS_EXT version, which is implemented by this driver. */
46 #define BCM43xx_WX_VERSION      18
47
48 #define MAX_WX_STRING           80
49
50 static int bcm43xx_wx_get_name(struct net_device *net_dev,
51                                struct iw_request_info *info,
52                                union iwreq_data *data,
53                                char *extra)
54 {
55         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
56         int i;
57         struct bcm43xx_phyinfo *phy;
58         char suffix[7] = { 0 };
59         int have_a = 0, have_b = 0, have_g = 0;
60
61         mutex_lock(&bcm->mutex);
62         for (i = 0; i < bcm->nr_80211_available; i++) {
63                 phy = &(bcm->core_80211_ext[i].phy);
64                 switch (phy->type) {
65                 case BCM43xx_PHYTYPE_A:
66                         have_a = 1;
67                         break;
68                 case BCM43xx_PHYTYPE_G:
69                         have_g = 1;
70                 case BCM43xx_PHYTYPE_B:
71                         have_b = 1;
72                         break;
73                 default:
74                         assert(0);
75                 }
76         }
77         mutex_unlock(&bcm->mutex);
78
79         i = 0;
80         if (have_a) {
81                 suffix[i++] = 'a';
82                 suffix[i++] = '/';
83         }
84         if (have_b) {
85                 suffix[i++] = 'b';
86                 suffix[i++] = '/';
87         }
88         if (have_g) {
89                 suffix[i++] = 'g';
90                 suffix[i++] = '/';
91         }
92         if (i != 0) 
93                 suffix[i - 1] = '\0';
94
95         snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);
96
97         return 0;
98 }
99
100 static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,
101                                       struct iw_request_info *info,
102                                       union iwreq_data *data,
103                                       char *extra)
104 {
105         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
106         unsigned long flags;
107         u8 channel;
108         int freq;
109         int err = -EINVAL;
110
111         mutex_lock(&bcm->mutex);
112         spin_lock_irqsave(&bcm->irq_lock, flags);
113
114         if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
115                 channel = data->freq.m;
116                 freq = bcm43xx_channel_to_freq(bcm, channel);
117         } else {
118                 channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
119                 freq = data->freq.m;
120         }
121         if (!ieee80211_is_valid_channel(bcm->ieee, channel))
122                 goto out_unlock;
123         if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
124                 //ieee80211softmac_disassoc(softmac, $REASON);
125                 bcm43xx_mac_suspend(bcm);
126                 err = bcm43xx_radio_selectchannel(bcm, channel, 0);
127                 bcm43xx_mac_enable(bcm);
128         } else {
129                 bcm43xx_current_radio(bcm)->initial_channel = channel;
130                 err = 0;
131         }
132 out_unlock:
133         spin_unlock_irqrestore(&bcm->irq_lock, flags);
134         mutex_unlock(&bcm->mutex);
135
136         return err;
137 }
138
139 static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,
140                                       struct iw_request_info *info,
141                                       union iwreq_data *data,
142                                       char *extra)
143 {
144         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
145         struct bcm43xx_radioinfo *radio;
146         int err = -ENODEV;
147         u16 channel;
148
149         mutex_lock(&bcm->mutex);
150         radio = bcm43xx_current_radio(bcm);
151         channel = radio->channel;
152         if (channel == 0xFF) {
153                 channel = radio->initial_channel;
154                 if (channel == 0xFF)
155                         goto out_unlock;
156         }
157         assert(channel > 0 && channel <= 1000);
158         data->freq.e = 1;
159         data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000;
160         data->freq.flags = 1;
161
162         err = 0;
163 out_unlock:
164         mutex_unlock(&bcm->mutex);
165
166         return err;
167 }
168
169 static int bcm43xx_wx_set_mode(struct net_device *net_dev,
170                                struct iw_request_info *info,
171                                union iwreq_data *data,
172                                char *extra)
173 {
174         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
175         unsigned long flags;
176         int mode;
177
178         mode = data->mode;
179         if (mode == IW_MODE_AUTO)
180                 mode = BCM43xx_INITIAL_IWMODE;
181
182         mutex_lock(&bcm->mutex);
183         spin_lock_irqsave(&bcm->irq_lock, flags);
184         if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
185                 if (bcm->ieee->iw_mode != mode)
186                         bcm43xx_set_iwmode(bcm, mode);
187         } else
188                 bcm->ieee->iw_mode = mode;
189         spin_unlock_irqrestore(&bcm->irq_lock, flags);
190         mutex_unlock(&bcm->mutex);
191
192         return 0;
193 }
194
195 static int bcm43xx_wx_get_mode(struct net_device *net_dev,
196                                struct iw_request_info *info,
197                                union iwreq_data *data,
198                                char *extra)
199 {
200         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
201
202         mutex_lock(&bcm->mutex);
203         data->mode = bcm->ieee->iw_mode;
204         mutex_unlock(&bcm->mutex);
205
206         return 0;
207 }
208
209 static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
210                                       struct iw_request_info *info,
211                                       union iwreq_data *data,
212                                       char *extra)
213 {
214         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
215         struct iw_range *range = (struct iw_range *)extra;
216         const struct ieee80211_geo *geo;
217         int i, j;
218         struct bcm43xx_phyinfo *phy;
219
220         data->data.length = sizeof(*range);
221         memset(range, 0, sizeof(*range));
222
223         //TODO: What about 802.11b?
224         /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
225         range->throughput = 27 * 1000 * 1000;
226
227         range->max_qual.qual = 100;
228         range->max_qual.level = 146; /* set floor at -110 dBm (146 - 256) */
229         range->max_qual.noise = 146;
230         range->max_qual.updated = IW_QUAL_ALL_UPDATED;
231
232         range->avg_qual.qual = 50;
233         range->avg_qual.level = 0;
234         range->avg_qual.noise = 0;
235         range->avg_qual.updated = IW_QUAL_ALL_UPDATED;
236
237         range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
238         range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
239         range->min_frag = MIN_FRAG_THRESHOLD;
240         range->max_frag = MAX_FRAG_THRESHOLD;
241
242         range->encoding_size[0] = 5;
243         range->encoding_size[1] = 13;
244         range->num_encoding_sizes = 2;
245         range->max_encoding_tokens = WEP_KEYS;
246
247         range->we_version_compiled = WIRELESS_EXT;
248         range->we_version_source = BCM43xx_WX_VERSION;
249
250         range->enc_capa = IW_ENC_CAPA_WPA |
251                           IW_ENC_CAPA_WPA2 |
252                           IW_ENC_CAPA_CIPHER_TKIP |
253                           IW_ENC_CAPA_CIPHER_CCMP;
254
255         mutex_lock(&bcm->mutex);
256         phy = bcm43xx_current_phy(bcm);
257
258         range->num_bitrates = 0;
259         i = 0;
260         if (phy->type == BCM43xx_PHYTYPE_A ||
261             phy->type == BCM43xx_PHYTYPE_G) {
262                 range->num_bitrates = 8;
263                 range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB * 500000;
264                 range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB * 500000;
265                 range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB * 500000;
266                 range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB * 500000;
267                 range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB * 500000;
268                 range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB * 500000;
269                 range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB * 500000;
270                 range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB * 500000;
271         }
272         if (phy->type == BCM43xx_PHYTYPE_B ||
273             phy->type == BCM43xx_PHYTYPE_G) {
274                 range->num_bitrates += 4;
275                 range->bitrate[i++] = IEEE80211_CCK_RATE_1MB * 500000;
276                 range->bitrate[i++] = IEEE80211_CCK_RATE_2MB * 500000;
277                 range->bitrate[i++] = IEEE80211_CCK_RATE_5MB * 500000;
278                 range->bitrate[i++] = IEEE80211_CCK_RATE_11MB * 500000;
279         }
280
281         geo = ieee80211_get_geo(bcm->ieee);
282         range->num_channels = geo->a_channels + geo->bg_channels;
283         j = 0;
284         for (i = 0; i < geo->a_channels; i++) {
285                 if (j == IW_MAX_FREQUENCIES)
286                         break;
287                 range->freq[j].i = j + 1;
288                 range->freq[j].m = geo->a[i].freq * 100000;
289                 range->freq[j].e = 1;
290                 j++;
291         }
292         for (i = 0; i < geo->bg_channels; i++) {
293                 if (j == IW_MAX_FREQUENCIES)
294                         break;
295                 range->freq[j].i = j + 1;
296                 range->freq[j].m = geo->bg[i].freq * 100000;
297                 range->freq[j].e = 1;
298                 j++;
299         }
300         range->num_frequency = j;
301
302         mutex_unlock(&bcm->mutex);
303
304         return 0;
305 }
306
307 static int bcm43xx_wx_set_nick(struct net_device *net_dev,
308                                struct iw_request_info *info,
309                                union iwreq_data *data,
310                                char *extra)
311 {
312         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
313         size_t len;
314
315         mutex_lock(&bcm->mutex);
316         len =  min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
317         memcpy(bcm->nick, extra, len);
318         bcm->nick[len] = '\0';
319         mutex_unlock(&bcm->mutex);
320
321         return 0;
322 }
323
324 static int bcm43xx_wx_get_nick(struct net_device *net_dev,
325                                struct iw_request_info *info,
326                                union iwreq_data *data,
327                                char *extra)
328 {
329         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
330         size_t len;
331
332         mutex_lock(&bcm->mutex);
333         len = strlen(bcm->nick);
334         memcpy(extra, bcm->nick, len);
335         data->data.length = (__u16)len;
336         data->data.flags = 1;
337         mutex_unlock(&bcm->mutex);
338
339         return 0;
340 }
341
342 static int bcm43xx_wx_set_rts(struct net_device *net_dev,
343                               struct iw_request_info *info,
344                               union iwreq_data *data,
345                               char *extra)
346 {
347         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
348         unsigned long flags;
349         int err = -EINVAL;
350
351         mutex_lock(&bcm->mutex);
352         spin_lock_irqsave(&bcm->irq_lock, flags);
353         if (data->rts.disabled) {
354                 bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
355                 err = 0;
356         } else {
357                 if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
358                     data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
359                         bcm->rts_threshold = data->rts.value;
360                         err = 0;
361                 }
362         }
363         spin_unlock_irqrestore(&bcm->irq_lock, flags);
364         mutex_unlock(&bcm->mutex);
365
366         return err;
367 }
368
369 static int bcm43xx_wx_get_rts(struct net_device *net_dev,
370                               struct iw_request_info *info,
371                               union iwreq_data *data,
372                               char *extra)
373 {
374         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
375
376         mutex_lock(&bcm->mutex);
377         data->rts.value = bcm->rts_threshold;
378         data->rts.fixed = 0;
379         data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
380         mutex_unlock(&bcm->mutex);
381
382         return 0;
383 }
384
385 static int bcm43xx_wx_set_frag(struct net_device *net_dev,
386                                struct iw_request_info *info,
387                                union iwreq_data *data,
388                                char *extra)
389 {
390         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
391         unsigned long flags;
392         int err = -EINVAL;
393
394         mutex_lock(&bcm->mutex);
395         spin_lock_irqsave(&bcm->irq_lock, flags);
396         if (data->frag.disabled) {
397                 bcm->ieee->fts = MAX_FRAG_THRESHOLD;
398                 err = 0;
399         } else {
400                 if (data->frag.value >= MIN_FRAG_THRESHOLD &&
401                     data->frag.value <= MAX_FRAG_THRESHOLD) {
402                         bcm->ieee->fts = data->frag.value & ~0x1;
403                         err = 0;
404                 }
405         }
406         spin_unlock_irqrestore(&bcm->irq_lock, flags);
407         mutex_unlock(&bcm->mutex);
408
409         return err;
410 }
411
412 static int bcm43xx_wx_get_frag(struct net_device *net_dev,
413                                struct iw_request_info *info,
414                                union iwreq_data *data,
415                                char *extra)
416 {
417         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
418
419         mutex_lock(&bcm->mutex);
420         data->frag.value = bcm->ieee->fts;
421         data->frag.fixed = 0;
422         data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
423         mutex_unlock(&bcm->mutex);
424
425         return 0;
426 }
427
428 static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
429                                     struct iw_request_info *info,
430                                     union iwreq_data *data,
431                                     char *extra)
432 {
433         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
434         struct bcm43xx_radioinfo *radio;
435         struct bcm43xx_phyinfo *phy;
436         unsigned long flags;
437         int err = -ENODEV;
438         u16 maxpower;
439
440         if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
441                 printk(PFX KERN_ERR "TX power not in dBm.\n");
442                 return -EOPNOTSUPP;
443         }
444
445         mutex_lock(&bcm->mutex);
446         spin_lock_irqsave(&bcm->irq_lock, flags);
447         if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
448                 goto out_unlock;
449         radio = bcm43xx_current_radio(bcm);
450         phy = bcm43xx_current_phy(bcm);
451         if (data->txpower.disabled != (!(radio->enabled))) {
452                 if (data->txpower.disabled)
453                         bcm43xx_radio_turn_off(bcm);
454                 else
455                         bcm43xx_radio_turn_on(bcm);
456         }
457         if (data->txpower.value > 0) {
458                 /* desired and maxpower dBm values are in Q5.2 */
459                 if (phy->type == BCM43xx_PHYTYPE_A)
460                         maxpower = bcm->sprom.maxpower_aphy;
461                 else
462                         maxpower = bcm->sprom.maxpower_bgphy;
463                 radio->txpower_desired = limit_value(data->txpower.value << 2,
464                                                      0, maxpower);
465                 bcm43xx_phy_xmitpower(bcm);
466         }
467         err = 0;
468
469 out_unlock:
470         spin_unlock_irqrestore(&bcm->irq_lock, flags);
471         mutex_unlock(&bcm->mutex);
472
473         return err;
474 }
475
476 static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
477                                     struct iw_request_info *info,
478                                     union iwreq_data *data,
479                                     char *extra)
480 {
481         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
482         struct bcm43xx_radioinfo *radio;
483         int err = -ENODEV;
484
485         mutex_lock(&bcm->mutex);
486         if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
487                 goto out_unlock;
488         radio = bcm43xx_current_radio(bcm);
489         /* desired dBm value is in Q5.2 */
490         data->txpower.value = radio->txpower_desired >> 2;
491         data->txpower.fixed = 1;
492         data->txpower.flags = IW_TXPOW_DBM;
493         data->txpower.disabled = !(radio->enabled);
494
495         err = 0;
496 out_unlock:
497         mutex_unlock(&bcm->mutex);
498
499         return err;
500 }
501
502 static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
503                                    struct iw_request_info *info,
504                                    union iwreq_data *data,
505                                    char *extra)
506 {
507         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
508         int err;
509
510         err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
511
512         return err;
513 }
514
515 static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
516                                    struct iw_request_info *info,
517                                    union iwreq_data *data,
518                                    char *extra)
519 {
520         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
521         int err;
522
523         err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
524
525         return err;
526 }
527
528 static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
529                                    struct iw_request_info *info,
530                                    union iwreq_data *data,
531                                    char *extra)
532 {
533         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
534         int err;
535
536         err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
537
538         return err;
539 }
540
541 static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
542                                    struct iw_request_info *info,
543                                    union iwreq_data *data,
544                                    char *extra)
545 {
546         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
547         int err;
548
549         err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
550
551         return err;
552 }
553
554 static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
555                                      struct iw_request_info *info,
556                                      union iwreq_data *data,
557                                      char *extra)
558 {
559         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
560         unsigned long flags;
561         int mode, err = 0;
562
563         mode = *((int *)extra);
564         switch (mode) {
565         case 0:
566                 mode = BCM43xx_RADIO_INTERFMODE_NONE;
567                 break;
568         case 1:
569                 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
570                 break;
571         case 2:
572                 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
573                 break;
574         case 3:
575                 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
576                 break;
577         default:
578                 printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
579                                     "0 => None,  1 => Non-WLAN,  2 => WLAN,  "
580                                     "3 => Auto-WLAN\n");
581                 return -EINVAL;
582         }
583
584         mutex_lock(&bcm->mutex);
585         spin_lock_irqsave(&bcm->irq_lock, flags);
586         if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
587                 err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
588                 if (err) {
589                         printk(KERN_ERR PFX "Interference Mitigation not "
590                                             "supported by device\n");
591                 }
592         } else {
593                 if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
594                         printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
595                                             "not supported while the interface is down.\n");
596                         err = -ENODEV;
597                 } else
598                         bcm43xx_current_radio(bcm)->interfmode = mode;
599         }
600         spin_unlock_irqrestore(&bcm->irq_lock, flags);
601         mutex_unlock(&bcm->mutex);
602
603         return err;
604 }
605
606 static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
607                                      struct iw_request_info *info,
608                                      union iwreq_data *data,
609                                      char *extra)
610 {
611         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
612         int mode;
613
614         mutex_lock(&bcm->mutex);
615         mode = bcm43xx_current_radio(bcm)->interfmode;
616         mutex_unlock(&bcm->mutex);
617
618         switch (mode) {
619         case BCM43xx_RADIO_INTERFMODE_NONE:
620                 strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
621                 break;
622         case BCM43xx_RADIO_INTERFMODE_NONWLAN:
623                 strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
624                 break;
625         case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
626                 strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
627                 break;
628         default:
629                 assert(0);
630         }
631         data->data.length = strlen(extra) + 1;
632
633         return 0;
634 }
635
636 static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
637                                         struct iw_request_info *info,
638                                         union iwreq_data *data,
639                                         char *extra)
640 {
641         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
642         unsigned long flags;
643         int on;
644
645         on = *((int *)extra);
646         mutex_lock(&bcm->mutex);
647         spin_lock_irqsave(&bcm->irq_lock, flags);
648         bcm->short_preamble = !!on;
649         spin_unlock_irqrestore(&bcm->irq_lock, flags);
650         mutex_unlock(&bcm->mutex);
651
652         return 0;
653 }
654
655 static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
656                                         struct iw_request_info *info,
657                                         union iwreq_data *data,
658                                         char *extra)
659 {
660         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
661         int on;
662
663         mutex_lock(&bcm->mutex);
664         on = bcm->short_preamble;
665         mutex_unlock(&bcm->mutex);
666
667         if (on)
668                 strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
669         else
670                 strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
671         data->data.length = strlen(extra) + 1;
672
673         return 0;
674 }
675
676 static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
677                                        struct iw_request_info *info,
678                                        union iwreq_data *data,
679                                        char *extra)
680 {
681         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
682         unsigned long flags;
683         int on;
684         
685         on = *((int *)extra);
686
687         mutex_lock(&bcm->mutex);
688         spin_lock_irqsave(&bcm->irq_lock, flags);
689         bcm->ieee->host_encrypt = !!on;
690         bcm->ieee->host_decrypt = !!on;
691         bcm->ieee->host_build_iv = !on;
692         bcm->ieee->host_strip_iv_icv = !on;
693         spin_unlock_irqrestore(&bcm->irq_lock, flags);
694         mutex_unlock(&bcm->mutex);
695
696         return 0;
697 }
698
699 static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
700                                        struct iw_request_info *info,
701                                        union iwreq_data *data,
702                                        char *extra)
703 {
704         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
705         int on;
706
707         mutex_lock(&bcm->mutex);
708         on = bcm->ieee->host_encrypt;
709         mutex_unlock(&bcm->mutex);
710
711         if (on)
712                 strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
713         else
714                 strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
715         data->data.length = strlen(extra + 1);
716
717         return 0;
718 }
719
720 /* Enough buffer to hold a hexdump of the sprom data. */
721 #define SPROM_BUFFERSIZE        512
722
723 static int sprom2hex(const u16 *sprom, char *dump)
724 {
725         int i, pos = 0;
726
727         for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
728                 pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
729                                 "%04X", swab16(sprom[i]) & 0xFFFF);
730         }
731
732         return pos + 1;
733 }
734
735 static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
736 {
737         char tmp[5] = { 0 };
738         int cnt = 0;
739         unsigned long parsed;
740
741         if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
742                 return -EINVAL;
743         while (cnt < BCM43xx_SPROM_SIZE) {
744                 memcpy(tmp, dump, 4);
745                 dump += 4;
746                 parsed = simple_strtoul(tmp, NULL, 16);
747                 sprom[cnt++] = swab16((u16)parsed);
748         }
749
750         return 0;
751 }
752
753 static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
754                                  struct iw_request_info *info,
755                                  union iwreq_data *data,
756                                  char *extra)
757 {
758         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
759         int err = -EPERM;
760         u16 *sprom;
761         unsigned long flags;
762
763         if (!capable(CAP_SYS_RAWIO))
764                 goto out;
765
766         err = -ENOMEM;
767         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
768                         GFP_KERNEL);
769         if (!sprom)
770                 goto out;
771
772         mutex_lock(&bcm->mutex);
773         spin_lock_irqsave(&bcm->irq_lock, flags);
774         err = -ENODEV;
775         if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
776                 err = bcm43xx_sprom_read(bcm, sprom);
777         spin_unlock_irqrestore(&bcm->irq_lock, flags);
778         mutex_unlock(&bcm->mutex);
779         if (!err)
780                 data->data.length = sprom2hex(sprom, extra);
781         kfree(sprom);
782 out:
783         return err;
784 }
785
786 static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
787                                   struct iw_request_info *info,
788                                   union iwreq_data *data,
789                                   char *extra)
790 {
791         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
792         int err = -EPERM;
793         u16 *sprom;
794         unsigned long flags;
795         char *input;
796         unsigned int len;
797
798         if (!capable(CAP_SYS_RAWIO))
799                 goto out;
800
801         err = -ENOMEM;
802         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
803                         GFP_KERNEL);
804         if (!sprom)
805                 goto out;
806
807         len = data->data.length;
808         extra[len - 1] = '\0';
809         input = strchr(extra, ':');
810         if (input) {
811                 input++;
812                 len -= input - extra;
813         } else
814                 input = extra;
815         err = hex2sprom(sprom, input, len);
816         if (err)
817                 goto out_kfree;
818
819         mutex_lock(&bcm->mutex);
820         spin_lock_irqsave(&bcm->irq_lock, flags);
821         spin_lock(&bcm->leds_lock);
822         err = -ENODEV;
823         if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
824                 err = bcm43xx_sprom_write(bcm, sprom);
825         spin_unlock(&bcm->leds_lock);
826         spin_unlock_irqrestore(&bcm->irq_lock, flags);
827         mutex_unlock(&bcm->mutex);
828 out_kfree:
829         kfree(sprom);
830 out:
831         return err;
832 }
833
834 /* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
835
836 static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
837 {
838         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
839         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
840         struct iw_statistics *wstats;
841         struct ieee80211_network *network = NULL;
842         static int tmp_level = 0;
843         static int tmp_qual = 0;
844         unsigned long flags;
845
846         wstats = &bcm->stats.wstats;
847         if (!mac->associnfo.associated) {
848                 wstats->miss.beacon = 0;
849 //              bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
850                 wstats->discard.retries = 0;
851 //              bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
852                 wstats->discard.nwid = 0;
853 //              bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
854                 wstats->discard.code = 0;
855 //              bcm->ieee->ieee_stats.rx_fragments = 0;  // FIXME: same here
856                 wstats->discard.fragment = 0;
857                 wstats->discard.misc = 0;
858                 wstats->qual.qual = 0;
859                 wstats->qual.level = 0;
860                 wstats->qual.noise = 0;
861                 wstats->qual.updated = 7;
862                 wstats->qual.updated |= IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
863                 return wstats;
864         }
865         /* fill in the real statistics when iface associated */
866         spin_lock_irqsave(&mac->ieee->lock, flags);
867         list_for_each_entry(network, &mac->ieee->network_list, list) {
868                 if (!memcmp(mac->associnfo.bssid, network->bssid, ETH_ALEN)) {
869                         if (!tmp_level) {       /* get initial values */
870                                 tmp_level = network->stats.signal;
871                                 tmp_qual = network->stats.rssi;
872                         } else {                /* smooth results */
873                                 tmp_level = (15 * tmp_level + network->stats.signal)/16;
874                                 tmp_qual = (15 * tmp_qual + network->stats.rssi)/16;
875                         }
876                         break;
877                 }
878         }
879         spin_unlock_irqrestore(&mac->ieee->lock, flags);
880         wstats->qual.level = tmp_level;
881         wstats->qual.qual = 100 * tmp_qual / RX_RSSI_MAX;
882         wstats->qual.noise = bcm->stats.noise;
883         wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
884         wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
885         wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
886         wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
887         wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
888         wstats->discard.misc = 0;       // FIXME
889         wstats->miss.beacon = 0;        // FIXME
890         return wstats;
891 }
892
893
894 #ifdef WX
895 # undef WX
896 #endif
897 #define WX(ioctl)  [(ioctl) - SIOCSIWCOMMIT]
898 static const iw_handler bcm43xx_wx_handlers[] = {
899         /* Wireless Identification */
900         WX(SIOCGIWNAME)         = bcm43xx_wx_get_name,
901         /* Basic operations */
902         WX(SIOCSIWFREQ)         = bcm43xx_wx_set_channelfreq,
903         WX(SIOCGIWFREQ)         = bcm43xx_wx_get_channelfreq,
904         WX(SIOCSIWMODE)         = bcm43xx_wx_set_mode,
905         WX(SIOCGIWMODE)         = bcm43xx_wx_get_mode,
906         /* Informative stuff */
907         WX(SIOCGIWRANGE)        = bcm43xx_wx_get_rangeparams,
908         /* Access Point manipulation */
909         WX(SIOCSIWAP)           = ieee80211softmac_wx_set_wap,
910         WX(SIOCGIWAP)           = ieee80211softmac_wx_get_wap,
911         WX(SIOCSIWSCAN)         = ieee80211softmac_wx_trigger_scan,
912         WX(SIOCGIWSCAN)         = ieee80211softmac_wx_get_scan_results,
913         /* 802.11 specific support */
914         WX(SIOCSIWESSID)        = ieee80211softmac_wx_set_essid,
915         WX(SIOCGIWESSID)        = ieee80211softmac_wx_get_essid,
916         WX(SIOCSIWNICKN)        = bcm43xx_wx_set_nick,
917         WX(SIOCGIWNICKN)        = bcm43xx_wx_get_nick,
918         /* Other parameters */
919         WX(SIOCSIWRATE)         = ieee80211softmac_wx_set_rate,
920         WX(SIOCGIWRATE)         = ieee80211softmac_wx_get_rate,
921         WX(SIOCSIWRTS)          = bcm43xx_wx_set_rts,
922         WX(SIOCGIWRTS)          = bcm43xx_wx_get_rts,
923         WX(SIOCSIWFRAG)         = bcm43xx_wx_set_frag,
924         WX(SIOCGIWFRAG)         = bcm43xx_wx_get_frag,
925         WX(SIOCSIWTXPOW)        = bcm43xx_wx_set_xmitpower,
926         WX(SIOCGIWTXPOW)        = bcm43xx_wx_get_xmitpower,
927 //TODO  WX(SIOCSIWRETRY)        = bcm43xx_wx_set_retry,
928 //TODO  WX(SIOCGIWRETRY)        = bcm43xx_wx_get_retry,
929         /* Encoding */
930         WX(SIOCSIWENCODE)       = bcm43xx_wx_set_encoding,
931         WX(SIOCGIWENCODE)       = bcm43xx_wx_get_encoding,
932         WX(SIOCSIWENCODEEXT)    = bcm43xx_wx_set_encodingext,
933         WX(SIOCGIWENCODEEXT)    = bcm43xx_wx_get_encodingext,
934         /* Power saving */
935 //TODO  WX(SIOCSIWPOWER)        = bcm43xx_wx_set_power,
936 //TODO  WX(SIOCGIWPOWER)        = bcm43xx_wx_get_power,
937         WX(SIOCSIWGENIE)        = ieee80211softmac_wx_set_genie,
938         WX(SIOCGIWGENIE)        = ieee80211softmac_wx_get_genie,
939         WX(SIOCSIWAUTH)         = ieee80211_wx_set_auth,
940         WX(SIOCGIWAUTH)         = ieee80211_wx_get_auth,
941 };
942 #undef WX
943
944 static const iw_handler bcm43xx_priv_wx_handlers[] = {
945         /* Set Interference Mitigation Mode. */
946         bcm43xx_wx_set_interfmode,
947         /* Get Interference Mitigation Mode. */
948         bcm43xx_wx_get_interfmode,
949         /* Enable/Disable Short Preamble mode. */
950         bcm43xx_wx_set_shortpreamble,
951         /* Get Short Preamble mode. */
952         bcm43xx_wx_get_shortpreamble,
953         /* Enable/Disable Software Encryption mode */
954         bcm43xx_wx_set_swencryption,
955         /* Get Software Encryption mode */
956         bcm43xx_wx_get_swencryption,
957         /* Write SRPROM data. */
958         bcm43xx_wx_sprom_write,
959         /* Read SPROM data. */
960         bcm43xx_wx_sprom_read,
961 };
962
963 #define PRIV_WX_SET_INTERFMODE          (SIOCIWFIRSTPRIV + 0)
964 #define PRIV_WX_GET_INTERFMODE          (SIOCIWFIRSTPRIV + 1)
965 #define PRIV_WX_SET_SHORTPREAMBLE       (SIOCIWFIRSTPRIV + 2)
966 #define PRIV_WX_GET_SHORTPREAMBLE       (SIOCIWFIRSTPRIV + 3)
967 #define PRIV_WX_SET_SWENCRYPTION        (SIOCIWFIRSTPRIV + 4)
968 #define PRIV_WX_GET_SWENCRYPTION        (SIOCIWFIRSTPRIV + 5)
969 #define PRIV_WX_SPROM_WRITE             (SIOCIWFIRSTPRIV + 6)
970 #define PRIV_WX_SPROM_READ              (SIOCIWFIRSTPRIV + 7)
971
972 #define PRIV_WX_DUMMY(ioctl)    \
973         {                                       \
974                 .cmd            = (ioctl),      \
975                 .name           = "__unused"    \
976         }
977
978 static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
979         {
980                 .cmd            = PRIV_WX_SET_INTERFMODE,
981                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
982                 .name           = "set_interfmode",
983         },
984         {
985                 .cmd            = PRIV_WX_GET_INTERFMODE,
986                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
987                 .name           = "get_interfmode",
988         },
989         {
990                 .cmd            = PRIV_WX_SET_SHORTPREAMBLE,
991                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
992                 .name           = "set_shortpreamb",
993         },
994         {
995                 .cmd            = PRIV_WX_GET_SHORTPREAMBLE,
996                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
997                 .name           = "get_shortpreamb",
998         },
999         {
1000                 .cmd            = PRIV_WX_SET_SWENCRYPTION,
1001                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
1002                 .name           = "set_swencrypt",
1003         },
1004         {
1005                 .cmd            = PRIV_WX_GET_SWENCRYPTION,
1006                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
1007                 .name           = "get_swencrypt",
1008         },
1009         {
1010                 .cmd            = PRIV_WX_SPROM_WRITE,
1011                 .set_args       = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
1012                 .name           = "write_sprom",
1013         },
1014         {
1015                 .cmd            = PRIV_WX_SPROM_READ,
1016                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
1017                 .name           = "read_sprom",
1018         },
1019 };
1020
1021 const struct iw_handler_def bcm43xx_wx_handlers_def = {
1022         .standard               = bcm43xx_wx_handlers,
1023         .num_standard           = ARRAY_SIZE(bcm43xx_wx_handlers),
1024         .num_private            = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
1025         .num_private_args       = ARRAY_SIZE(bcm43xx_priv_wx_args),
1026         .private                = bcm43xx_priv_wx_handlers,
1027         .private_args           = bcm43xx_priv_wx_args,
1028         .get_wireless_stats     = bcm43xx_get_wireless_stats,
1029 };