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