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