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