3daee828ef4b7a78ad68a6e4cb3ddfb8d148c768
[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->ieee->iw_mode != mode)
186                 bcm43xx_set_iwmode(bcm, mode);
187         bcm43xx_unlock_mmio(bcm, flags);
188
189         return 0;
190 }
191
192 static int bcm43xx_wx_get_mode(struct net_device *net_dev,
193                                struct iw_request_info *info,
194                                union iwreq_data *data,
195                                char *extra)
196 {
197         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
198         unsigned long flags;
199
200         bcm43xx_lock(bcm, flags);
201         data->mode = bcm->ieee->iw_mode;
202         bcm43xx_unlock(bcm, flags);
203
204         return 0;
205 }
206
207 static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
208                                       struct iw_request_info *info,
209                                       union iwreq_data *data,
210                                       char *extra)
211 {
212         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
213         struct iw_range *range = (struct iw_range *)extra;
214         const struct ieee80211_geo *geo;
215         unsigned long flags;
216         int i, j;
217         struct bcm43xx_phyinfo *phy;
218
219         data->data.length = sizeof(*range);
220         memset(range, 0, sizeof(*range));
221
222         //TODO: What about 802.11b?
223         /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
224         range->throughput = 27 * 1000 * 1000;
225
226         range->max_qual.qual = 100;
227         /* TODO: Real max RSSI */
228         range->max_qual.level = 3;
229         range->max_qual.noise = 100;
230         range->max_qual.updated = 7;
231
232         range->avg_qual.qual = 70;
233         range->avg_qual.level = 2;
234         range->avg_qual.noise = 40;
235         range->avg_qual.updated = 7;
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         bcm43xx_lock(bcm, flags);
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;
264                 range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;
265                 range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;
266                 range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;
267                 range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;
268                 range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;
269                 range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;
270                 range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;
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;
276                 range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;
277                 range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;
278                 range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;
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;//FIXME?
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;//FIXME?
297                 range->freq[j].e = 1;
298                 j++;
299         }
300         range->num_frequency = j;
301
302         bcm43xx_unlock(bcm, flags);
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         unsigned long flags;
314         size_t len;
315
316         bcm43xx_lock(bcm, flags);
317         len =  min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
318         memcpy(bcm->nick, extra, len);
319         bcm->nick[len] = '\0';
320         bcm43xx_unlock(bcm, flags);
321
322         return 0;
323 }
324
325 static int bcm43xx_wx_get_nick(struct net_device *net_dev,
326                                struct iw_request_info *info,
327                                union iwreq_data *data,
328                                char *extra)
329 {
330         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
331         unsigned long flags;
332         size_t len;
333
334         bcm43xx_lock(bcm, flags);
335         len = strlen(bcm->nick) + 1;
336         memcpy(extra, bcm->nick, len);
337         data->data.length = (__u16)len;
338         data->data.flags = 1;
339         bcm43xx_unlock(bcm, flags);
340
341         return 0;
342 }
343
344 static int bcm43xx_wx_set_rts(struct net_device *net_dev,
345                               struct iw_request_info *info,
346                               union iwreq_data *data,
347                               char *extra)
348 {
349         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
350         unsigned long flags;
351         int err = -EINVAL;
352
353         bcm43xx_lock(bcm, flags);
354         if (data->rts.disabled) {
355                 bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
356                 err = 0;
357         } else {
358                 if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
359                     data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
360                         bcm->rts_threshold = data->rts.value;
361                         err = 0;
362                 }
363         }
364         bcm43xx_unlock(bcm, flags);
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         unsigned long flags;
376
377         bcm43xx_lock(bcm, flags);
378         data->rts.value = bcm->rts_threshold;
379         data->rts.fixed = 0;
380         data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
381         bcm43xx_unlock(bcm, flags);
382
383         return 0;
384 }
385
386 static int bcm43xx_wx_set_frag(struct net_device *net_dev,
387                                struct iw_request_info *info,
388                                union iwreq_data *data,
389                                char *extra)
390 {
391         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
392         unsigned long flags;
393         int err = -EINVAL;
394
395         bcm43xx_lock(bcm, 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         bcm43xx_unlock(bcm, flags);
407
408         return err;
409 }
410
411 static int bcm43xx_wx_get_frag(struct net_device *net_dev,
412                                struct iw_request_info *info,
413                                union iwreq_data *data,
414                                char *extra)
415 {
416         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
417         unsigned long flags;
418
419         bcm43xx_lock(bcm, flags);
420         data->frag.value = bcm->ieee->fts;
421         data->frag.fixed = 0;
422         data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
423         bcm43xx_unlock(bcm, flags);
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         bcm43xx_lock_mmio(bcm, flags);
446         if (!bcm->initialized)
447                 goto out_unlock;
448         radio = bcm43xx_current_radio(bcm);
449         phy = bcm43xx_current_phy(bcm);
450         if (data->txpower.disabled != (!(radio->enabled))) {
451                 if (data->txpower.disabled)
452                         bcm43xx_radio_turn_off(bcm);
453                 else
454                         bcm43xx_radio_turn_on(bcm);
455         }
456         if (data->txpower.value > 0) {
457                 /* desired and maxpower dBm values are in Q5.2 */
458                 if (phy->type == BCM43xx_PHYTYPE_A)
459                         maxpower = bcm->sprom.maxpower_aphy;
460                 else
461                         maxpower = bcm->sprom.maxpower_bgphy;
462                 radio->txpower_desired = limit_value(data->txpower.value << 2,
463                                                      0, maxpower);
464                 bcm43xx_phy_xmitpower(bcm);
465         }
466         err = 0;
467
468 out_unlock:
469         bcm43xx_unlock_mmio(bcm, flags);
470
471         return err;
472 }
473
474 static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
475                                     struct iw_request_info *info,
476                                     union iwreq_data *data,
477                                     char *extra)
478 {
479         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
480         struct bcm43xx_radioinfo *radio;
481         unsigned long flags;
482         int err = -ENODEV;
483
484         bcm43xx_lock(bcm, flags);
485         if (!bcm->initialized)
486                 goto out_unlock;
487         radio = bcm43xx_current_radio(bcm);
488         /* desired dBm value is in Q5.2 */
489         data->txpower.value = radio->txpower_desired >> 2;
490         data->txpower.fixed = 1;
491         data->txpower.flags = IW_TXPOW_DBM;
492         data->txpower.disabled = !(radio->enabled);
493
494         err = 0;
495 out_unlock:
496         bcm43xx_unlock(bcm, flags);
497
498         return err;
499 }
500
501 static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
502                                    struct iw_request_info *info,
503                                    union iwreq_data *data,
504                                    char *extra)
505 {
506         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
507         int err;
508
509         err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
510
511         return err;
512 }
513
514 static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
515                                    struct iw_request_info *info,
516                                    union iwreq_data *data,
517                                    char *extra)
518 {
519         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
520         int err;
521
522         err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
523
524         return err;
525 }
526
527 static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
528                                    struct iw_request_info *info,
529                                    union iwreq_data *data,
530                                    char *extra)
531 {
532         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
533         int err;
534
535         err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
536
537         return err;
538 }
539
540 static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
541                                    struct iw_request_info *info,
542                                    union iwreq_data *data,
543                                    char *extra)
544 {
545         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
546         int err;
547
548         err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
549
550         return err;
551 }
552
553 static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
554                                      struct iw_request_info *info,
555                                      union iwreq_data *data,
556                                      char *extra)
557 {
558         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
559         unsigned long flags;
560         int mode, err = 0;
561
562         mode = *((int *)extra);
563         switch (mode) {
564         case 0:
565                 mode = BCM43xx_RADIO_INTERFMODE_NONE;
566                 break;
567         case 1:
568                 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
569                 break;
570         case 2:
571                 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
572                 break;
573         case 3:
574                 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
575                 break;
576         default:
577                 printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
578                                     "0 => None,  1 => Non-WLAN,  2 => WLAN,  "
579                                     "3 => Auto-WLAN\n");
580                 return -EINVAL;
581         }
582
583         bcm43xx_lock_mmio(bcm, flags);
584         if (bcm->initialized) {
585                 err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
586                 if (err) {
587                         printk(KERN_ERR PFX "Interference Mitigation not "
588                                             "supported by device\n");
589                 }
590         } else {
591                 if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
592                         printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
593                                             "not supported while the interface is down.\n");
594                         err = -ENODEV;
595                 } else
596                         bcm43xx_current_radio(bcm)->interfmode = mode;
597         }
598         bcm43xx_unlock_mmio(bcm, flags);
599
600         return err;
601 }
602
603 static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
604                                      struct iw_request_info *info,
605                                      union iwreq_data *data,
606                                      char *extra)
607 {
608         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
609         unsigned long flags;
610         int mode;
611
612         bcm43xx_lock(bcm, flags);
613         mode = bcm43xx_current_radio(bcm)->interfmode;
614         bcm43xx_unlock(bcm, flags);
615
616         switch (mode) {
617         case BCM43xx_RADIO_INTERFMODE_NONE:
618                 strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
619                 break;
620         case BCM43xx_RADIO_INTERFMODE_NONWLAN:
621                 strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
622                 break;
623         case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
624                 strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
625                 break;
626         default:
627                 assert(0);
628         }
629         data->data.length = strlen(extra) + 1;
630
631         return 0;
632 }
633
634 static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
635                                         struct iw_request_info *info,
636                                         union iwreq_data *data,
637                                         char *extra)
638 {
639         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
640         unsigned long flags;
641         int on;
642
643         on = *((int *)extra);
644         bcm43xx_lock(bcm, flags);
645         bcm->short_preamble = !!on;
646         bcm43xx_unlock(bcm, flags);
647
648         return 0;
649 }
650
651 static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
652                                         struct iw_request_info *info,
653                                         union iwreq_data *data,
654                                         char *extra)
655 {
656         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
657         unsigned long flags;
658         int on;
659
660         bcm43xx_lock(bcm, flags);
661         on = bcm->short_preamble;
662         bcm43xx_unlock(bcm, flags);
663
664         if (on)
665                 strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
666         else
667                 strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
668         data->data.length = strlen(extra) + 1;
669
670         return 0;
671 }
672
673 static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
674                                        struct iw_request_info *info,
675                                        union iwreq_data *data,
676                                        char *extra)
677 {
678         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
679         unsigned long flags;
680         int on;
681         
682         on = *((int *)extra);
683
684         bcm43xx_lock(bcm, flags);
685         bcm->ieee->host_encrypt = !!on;
686         bcm->ieee->host_decrypt = !!on;
687         bcm->ieee->host_build_iv = !on;
688         bcm43xx_unlock(bcm, flags);
689
690         return 0;
691 }
692
693 static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
694                                        struct iw_request_info *info,
695                                        union iwreq_data *data,
696                                        char *extra)
697 {
698         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
699         unsigned long flags;
700         int on;
701
702         bcm43xx_lock(bcm, flags);
703         on = bcm->ieee->host_encrypt;
704         bcm43xx_unlock(bcm, flags);
705
706         if (on)
707                 strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
708         else
709                 strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
710         data->data.length = strlen(extra + 1);
711
712         return 0;
713 }
714
715 /* Enough buffer to hold a hexdump of the sprom data. */
716 #define SPROM_BUFFERSIZE        512
717
718 static int sprom2hex(const u16 *sprom, char *dump)
719 {
720         int i, pos = 0;
721
722         for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
723                 pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
724                                 "%04X", swab16(sprom[i]) & 0xFFFF);
725         }
726
727         return pos + 1;
728 }
729
730 static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
731 {
732         char tmp[5] = { 0 };
733         int cnt = 0;
734         unsigned long parsed;
735
736         if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
737                 return -EINVAL;
738         while (cnt < BCM43xx_SPROM_SIZE) {
739                 memcpy(tmp, dump, 4);
740                 dump += 4;
741                 parsed = simple_strtoul(tmp, NULL, 16);
742                 sprom[cnt++] = swab16((u16)parsed);
743         }
744
745         return 0;
746 }
747
748 static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
749                                  struct iw_request_info *info,
750                                  union iwreq_data *data,
751                                  char *extra)
752 {
753         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
754         int err = -EPERM;
755         u16 *sprom;
756         unsigned long flags;
757
758         if (!capable(CAP_SYS_RAWIO))
759                 goto out;
760
761         err = -ENOMEM;
762         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
763                         GFP_KERNEL);
764         if (!sprom)
765                 goto out;
766
767         bcm43xx_lock_mmio(bcm, flags);
768         err = -ENODEV;
769         if (bcm->initialized)
770                 err = bcm43xx_sprom_read(bcm, sprom);
771         bcm43xx_unlock_mmio(bcm, flags);
772         if (!err)
773                 data->data.length = sprom2hex(sprom, extra);
774         kfree(sprom);
775 out:
776         return err;
777 }
778
779 static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
780                                   struct iw_request_info *info,
781                                   union iwreq_data *data,
782                                   char *extra)
783 {
784         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
785         int err = -EPERM;
786         u16 *sprom;
787         unsigned long flags;
788         char *input;
789         unsigned int len;
790
791         if (!capable(CAP_SYS_RAWIO))
792                 goto out;
793
794         err = -ENOMEM;
795         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
796                         GFP_KERNEL);
797         if (!sprom)
798                 goto out;
799
800         len = data->data.length;
801         extra[len - 1] = '\0';
802         input = strchr(extra, ':');
803         if (input) {
804                 input++;
805                 len -= input - extra;
806         } else
807                 input = extra;
808         err = hex2sprom(sprom, input, len);
809         if (err)
810                 goto out_kfree;
811
812         bcm43xx_lock_mmio(bcm, flags);
813         err = -ENODEV;
814         if (bcm->initialized)
815                 err = bcm43xx_sprom_write(bcm, sprom);
816         bcm43xx_unlock_mmio(bcm, flags);
817 out_kfree:
818         kfree(sprom);
819 out:
820         return err;
821 }
822
823 /* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
824
825 static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
826 {
827         struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
828         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
829         struct iw_statistics *wstats;
830
831         wstats = &bcm->stats.wstats;
832         if (!mac->associated) {
833                 wstats->miss.beacon = 0;
834 //              bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
835                 wstats->discard.retries = 0;
836 //              bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
837                 wstats->discard.nwid = 0;
838 //              bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
839                 wstats->discard.code = 0;
840 //              bcm->ieee->ieee_stats.rx_fragments = 0;  // FIXME: same here
841                 wstats->discard.fragment = 0;
842                 wstats->discard.misc = 0;
843                 wstats->qual.qual = 0;
844                 wstats->qual.level = 0;
845                 wstats->qual.noise = 0;
846                 wstats->qual.updated = 7;
847                 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
848                         IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
849                 return wstats;
850         }
851         /* fill in the real statistics when iface associated */
852         wstats->qual.qual = 100;     // TODO: get the real signal quality
853         wstats->qual.level = 3 - bcm->stats.link_quality;
854         wstats->qual.noise = bcm->stats.noise;
855         wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
856                         IW_QUAL_NOISE_UPDATED;
857         wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
858         wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
859         wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
860         wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
861         wstats->discard.misc = 0;       // FIXME
862         wstats->miss.beacon = 0;        // FIXME
863         return wstats;
864 }
865
866
867 #ifdef WX
868 # undef WX
869 #endif
870 #define WX(ioctl)  [(ioctl) - SIOCSIWCOMMIT]
871 static const iw_handler bcm43xx_wx_handlers[] = {
872         /* Wireless Identification */
873         WX(SIOCGIWNAME)         = bcm43xx_wx_get_name,
874         /* Basic operations */
875         WX(SIOCSIWFREQ)         = bcm43xx_wx_set_channelfreq,
876         WX(SIOCGIWFREQ)         = bcm43xx_wx_get_channelfreq,
877         WX(SIOCSIWMODE)         = bcm43xx_wx_set_mode,
878         WX(SIOCGIWMODE)         = bcm43xx_wx_get_mode,
879         /* Informative stuff */
880         WX(SIOCGIWRANGE)        = bcm43xx_wx_get_rangeparams,
881         /* Access Point manipulation */
882         WX(SIOCSIWAP)           = ieee80211softmac_wx_set_wap,
883         WX(SIOCGIWAP)           = ieee80211softmac_wx_get_wap,
884         WX(SIOCSIWSCAN)         = ieee80211softmac_wx_trigger_scan,
885         WX(SIOCGIWSCAN)         = ieee80211softmac_wx_get_scan_results,
886         /* 802.11 specific support */
887         WX(SIOCSIWESSID)        = ieee80211softmac_wx_set_essid,
888         WX(SIOCGIWESSID)        = ieee80211softmac_wx_get_essid,
889         WX(SIOCSIWNICKN)        = bcm43xx_wx_set_nick,
890         WX(SIOCGIWNICKN)        = bcm43xx_wx_get_nick,
891         /* Other parameters */
892         WX(SIOCSIWRATE)         = ieee80211softmac_wx_set_rate,
893         WX(SIOCGIWRATE)         = ieee80211softmac_wx_get_rate,
894         WX(SIOCSIWRTS)          = bcm43xx_wx_set_rts,
895         WX(SIOCGIWRTS)          = bcm43xx_wx_get_rts,
896         WX(SIOCSIWFRAG)         = bcm43xx_wx_set_frag,
897         WX(SIOCGIWFRAG)         = bcm43xx_wx_get_frag,
898         WX(SIOCSIWTXPOW)        = bcm43xx_wx_set_xmitpower,
899         WX(SIOCGIWTXPOW)        = bcm43xx_wx_get_xmitpower,
900 //TODO  WX(SIOCSIWRETRY)        = bcm43xx_wx_set_retry,
901 //TODO  WX(SIOCGIWRETRY)        = bcm43xx_wx_get_retry,
902         /* Encoding */
903         WX(SIOCSIWENCODE)       = bcm43xx_wx_set_encoding,
904         WX(SIOCGIWENCODE)       = bcm43xx_wx_get_encoding,
905         WX(SIOCSIWENCODEEXT)    = bcm43xx_wx_set_encodingext,
906         WX(SIOCGIWENCODEEXT)    = bcm43xx_wx_get_encodingext,
907         /* Power saving */
908 //TODO  WX(SIOCSIWPOWER)        = bcm43xx_wx_set_power,
909 //TODO  WX(SIOCGIWPOWER)        = bcm43xx_wx_get_power,
910         WX(SIOCSIWGENIE)        = ieee80211softmac_wx_set_genie,
911         WX(SIOCGIWGENIE)        = ieee80211softmac_wx_get_genie,
912         WX(SIOCSIWAUTH)         = ieee80211_wx_set_auth,
913         WX(SIOCGIWAUTH)         = ieee80211_wx_get_auth,
914 };
915 #undef WX
916
917 static const iw_handler bcm43xx_priv_wx_handlers[] = {
918         /* Set Interference Mitigation Mode. */
919         bcm43xx_wx_set_interfmode,
920         /* Get Interference Mitigation Mode. */
921         bcm43xx_wx_get_interfmode,
922         /* Enable/Disable Short Preamble mode. */
923         bcm43xx_wx_set_shortpreamble,
924         /* Get Short Preamble mode. */
925         bcm43xx_wx_get_shortpreamble,
926         /* Enable/Disable Software Encryption mode */
927         bcm43xx_wx_set_swencryption,
928         /* Get Software Encryption mode */
929         bcm43xx_wx_get_swencryption,
930         /* Write SRPROM data. */
931         bcm43xx_wx_sprom_write,
932         /* Read SPROM data. */
933         bcm43xx_wx_sprom_read,
934 };
935
936 #define PRIV_WX_SET_INTERFMODE          (SIOCIWFIRSTPRIV + 0)
937 #define PRIV_WX_GET_INTERFMODE          (SIOCIWFIRSTPRIV + 1)
938 #define PRIV_WX_SET_SHORTPREAMBLE       (SIOCIWFIRSTPRIV + 2)
939 #define PRIV_WX_GET_SHORTPREAMBLE       (SIOCIWFIRSTPRIV + 3)
940 #define PRIV_WX_SET_SWENCRYPTION        (SIOCIWFIRSTPRIV + 4)
941 #define PRIV_WX_GET_SWENCRYPTION        (SIOCIWFIRSTPRIV + 5)
942 #define PRIV_WX_SPROM_WRITE             (SIOCIWFIRSTPRIV + 6)
943 #define PRIV_WX_SPROM_READ              (SIOCIWFIRSTPRIV + 7)
944
945 #define PRIV_WX_DUMMY(ioctl)    \
946         {                                       \
947                 .cmd            = (ioctl),      \
948                 .name           = "__unused"    \
949         }
950
951 static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
952         {
953                 .cmd            = PRIV_WX_SET_INTERFMODE,
954                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
955                 .name           = "set_interfmode",
956         },
957         {
958                 .cmd            = PRIV_WX_GET_INTERFMODE,
959                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
960                 .name           = "get_interfmode",
961         },
962         {
963                 .cmd            = PRIV_WX_SET_SHORTPREAMBLE,
964                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
965                 .name           = "set_shortpreambl",
966         },
967         {
968                 .cmd            = PRIV_WX_GET_SHORTPREAMBLE,
969                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
970                 .name           = "get_shortpreambl",
971         },
972         {
973                 .cmd            = PRIV_WX_SET_SWENCRYPTION,
974                 .set_args       = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
975                 .name           = "set_swencryption",
976         },
977         {
978                 .cmd            = PRIV_WX_GET_SWENCRYPTION,
979                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
980                 .name           = "get_swencryption",
981         },
982         {
983                 .cmd            = PRIV_WX_SPROM_WRITE,
984                 .set_args       = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
985                 .name           = "write_sprom",
986         },
987         {
988                 .cmd            = PRIV_WX_SPROM_READ,
989                 .get_args       = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
990                 .name           = "read_sprom",
991         },
992 };
993
994 const struct iw_handler_def bcm43xx_wx_handlers_def = {
995         .standard               = bcm43xx_wx_handlers,
996         .num_standard           = ARRAY_SIZE(bcm43xx_wx_handlers),
997         .num_private            = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
998         .num_private_args       = ARRAY_SIZE(bcm43xx_priv_wx_args),
999         .private                = bcm43xx_priv_wx_handlers,
1000         .private_args           = bcm43xx_priv_wx_args,
1001         .get_wireless_stats     = bcm43xx_get_wireless_stats,
1002 };