blob: aa2d9930c436eb0c322bb640810b05a253ae8ada [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;
Michael Buesch10d8dd82006-02-19 22:08:48 +0100114 int err = -EINVAL;
John W. Linvillef2223132006-01-23 16:59:58 -0500115
Michael Buesch10d8dd82006-02-19 22:08:48 +0100116 spin_lock_irqsave(&bcm->lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500117 if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
118 channel = data->freq.m;
119 freq = bcm43xx_channel_to_freq(bcm, channel);
120 } else {
121 channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
122 freq = data->freq.m;
123 }
124 if (!bcm43xx_is_valid_channel(bcm, channel))
Michael Buesch10d8dd82006-02-19 22:08:48 +0100125 goto out_unlock;
John W. Linvillef2223132006-01-23 16:59:58 -0500126 if (bcm->initialized) {
127 //ieee80211softmac_disassoc(softmac, $REASON);
128 bcm43xx_mac_suspend(bcm);
129 err = bcm43xx_radio_selectchannel(bcm, channel, 0);
130 bcm43xx_mac_enable(bcm);
Michael Buesch10d8dd82006-02-19 22:08:48 +0100131 } else {
John W. Linvillef2223132006-01-23 16:59:58 -0500132 bcm->current_core->radio->initial_channel = channel;
Michael Buesch10d8dd82006-02-19 22:08:48 +0100133 err = 0;
134 }
135out_unlock:
John W. Linvillef2223132006-01-23 16:59:58 -0500136 spin_unlock_irqrestore(&bcm->lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500137
138 return err;
139}
140
141static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,
142 struct iw_request_info *info,
143 union iwreq_data *data,
144 char *extra)
145{
146 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
147 unsigned long flags;
148 int err = -ENODEV;
149 u16 channel;
150
John W. Linvillef2223132006-01-23 16:59:58 -0500151 spin_lock_irqsave(&bcm->lock, flags);
152 channel = bcm->current_core->radio->channel;
153 if (channel == 0xFF) {
154 assert(!bcm->initialized);
155 channel = bcm->current_core->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;
165out_unlock:
166 spin_unlock_irqrestore(&bcm->lock, flags);
167
168 return err;
169}
170
171static 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
John W. Linvillef2223132006-01-23 16:59:58 -0500180 mode = data->mode;
181 if (mode == IW_MODE_AUTO)
182 mode = BCM43xx_INITIAL_IWMODE;
183
184 spin_lock_irqsave(&bcm->lock, flags);
185 if (bcm->ieee->iw_mode != mode)
186 bcm43xx_set_iwmode(bcm, mode);
187 spin_unlock_irqrestore(&bcm->lock, flags);
188
189 return 0;
190}
191
192static 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
John W. Linvillef2223132006-01-23 16:59:58 -0500200 spin_lock_irqsave(&bcm->lock, flags);
201 data->mode = bcm->ieee->iw_mode;
202 spin_unlock_irqrestore(&bcm->lock, flags);
203
204 return 0;
205}
206
207static int bcm43xx_wx_set_sensitivity(struct net_device *net_dev,
208 struct iw_request_info *info,
209 union iwreq_data *data,
210 char *extra)
211{
John W. Linvillef2223132006-01-23 16:59:58 -0500212 /*TODO*/
213 return 0;
214}
215
216static int bcm43xx_wx_get_sensitivity(struct net_device *net_dev,
217 struct iw_request_info *info,
218 union iwreq_data *data,
219 char *extra)
220{
John W. Linvillef2223132006-01-23 16:59:58 -0500221 /*TODO*/
222 return 0;
223}
224
225static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
226 struct iw_request_info *info,
227 union iwreq_data *data,
228 char *extra)
229{
230 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
231 struct iw_range *range = (struct iw_range *)extra;
232 const struct ieee80211_geo *geo;
233 unsigned long flags;
234 int i, j;
235
John W. Linvillef2223132006-01-23 16:59:58 -0500236 data->data.length = sizeof(*range);
237 memset(range, 0, sizeof(*range));
238
239 //TODO: What about 802.11b?
240 /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
241 range->throughput = 27 * 1000 * 1000;
242
243 range->max_qual.qual = 100;
244 /* TODO: Real max RSSI */
245 range->max_qual.level = 0;
246 range->max_qual.noise = 0;
247 range->max_qual.updated = 7;
248
249 range->avg_qual.qual = 70;
250 range->avg_qual.level = 0;
251 range->avg_qual.noise = 0;
252 range->avg_qual.updated = 7;
253
254 range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
255 range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
256 range->min_frag = MIN_FRAG_THRESHOLD;
257 range->max_frag = MAX_FRAG_THRESHOLD;
258
259 range->encoding_size[0] = 5;
260 range->encoding_size[1] = 13;
261 range->num_encoding_sizes = 2;
262 range->max_encoding_tokens = WEP_KEYS;
263
264 range->we_version_compiled = WIRELESS_EXT;
Michael Buesch8fa252d2006-02-02 19:49:15 +0100265 range->we_version_source = BCM43xx_WX_VERSION;
266
267 range->enc_capa = IW_ENC_CAPA_WPA |
268 IW_ENC_CAPA_WPA2 |
269 IW_ENC_CAPA_CIPHER_TKIP |
270 IW_ENC_CAPA_CIPHER_CCMP;
John W. Linvillef2223132006-01-23 16:59:58 -0500271
272 spin_lock_irqsave(&bcm->lock, flags);
273
274 range->num_bitrates = 0;
275 i = 0;
276 if (bcm->current_core->phy->type == BCM43xx_PHYTYPE_A ||
277 bcm->current_core->phy->type == BCM43xx_PHYTYPE_G) {
278 range->num_bitrates = 8;
279 range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB;
280 range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;
281 range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;
282 range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;
283 range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;
284 range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;
285 range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;
286 range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;
287 }
288 if (bcm->current_core->phy->type == BCM43xx_PHYTYPE_B ||
289 bcm->current_core->phy->type == BCM43xx_PHYTYPE_G) {
290 range->num_bitrates += 4;
291 range->bitrate[i++] = IEEE80211_CCK_RATE_1MB;
292 range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;
293 range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;
294 range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;
295 }
296
297 geo = ieee80211_get_geo(bcm->ieee);
298 range->num_channels = geo->a_channels + geo->bg_channels;
299 j = 0;
300 for (i = 0; i < geo->a_channels; i++) {
301 if (j == IW_MAX_FREQUENCIES)
302 break;
303 range->freq[j].i = j + 1;
304 range->freq[j].m = geo->a[i].freq;//FIXME?
305 range->freq[j].e = 1;
306 j++;
307 }
308 for (i = 0; i < geo->bg_channels; i++) {
309 if (j == IW_MAX_FREQUENCIES)
310 break;
311 range->freq[j].i = j + 1;
312 range->freq[j].m = geo->bg[i].freq;//FIXME?
313 range->freq[j].e = 1;
314 j++;
315 }
316 range->num_frequency = j;
317
318 spin_unlock_irqrestore(&bcm->lock, flags);
319
320 return 0;
321}
322
323static int bcm43xx_wx_set_nick(struct net_device *net_dev,
324 struct iw_request_info *info,
325 union iwreq_data *data,
326 char *extra)
327{
328 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
329 unsigned long flags;
330 size_t len;
331
John W. Linvillef2223132006-01-23 16:59:58 -0500332 spin_lock_irqsave(&bcm->lock, flags);
333 len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
334 memcpy(bcm->nick, extra, len);
335 bcm->nick[len] = '\0';
336 spin_unlock_irqrestore(&bcm->lock, flags);
337
338 return 0;
339}
340
341static int bcm43xx_wx_get_nick(struct net_device *net_dev,
342 struct iw_request_info *info,
343 union iwreq_data *data,
344 char *extra)
345{
346 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
347 unsigned long flags;
348 size_t len;
349
John W. Linvillef2223132006-01-23 16:59:58 -0500350 spin_lock_irqsave(&bcm->lock, flags);
351 len = strlen(bcm->nick) + 1;
352 memcpy(extra, bcm->nick, len);
353 data->data.length = (__u16)len;
354 data->data.flags = 1;
355 spin_unlock_irqrestore(&bcm->lock, flags);
356
357 return 0;
358}
359
360static int bcm43xx_wx_set_rts(struct net_device *net_dev,
361 struct iw_request_info *info,
362 union iwreq_data *data,
363 char *extra)
364{
365 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
366 unsigned long flags;
367 int err = -EINVAL;
368
John W. Linvillef2223132006-01-23 16:59:58 -0500369 spin_lock_irqsave(&bcm->lock, flags);
370 if (data->rts.disabled) {
371 bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
372 err = 0;
373 } else {
374 if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
375 data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
376 bcm->rts_threshold = data->rts.value;
377 err = 0;
378 }
379 }
380 spin_unlock_irqrestore(&bcm->lock, flags);
381
382 return err;
383}
384
385static int bcm43xx_wx_get_rts(struct net_device *net_dev,
386 struct iw_request_info *info,
387 union iwreq_data *data,
388 char *extra)
389{
390 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
391 unsigned long flags;
392
John W. Linvillef2223132006-01-23 16:59:58 -0500393 spin_lock_irqsave(&bcm->lock, flags);
394 data->rts.value = bcm->rts_threshold;
395 data->rts.fixed = 0;
396 data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
397 spin_unlock_irqrestore(&bcm->lock, flags);
398
399 return 0;
400}
401
402static int bcm43xx_wx_set_frag(struct net_device *net_dev,
403 struct iw_request_info *info,
404 union iwreq_data *data,
405 char *extra)
406{
407 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
408 unsigned long flags;
409 int err = -EINVAL;
410
John W. Linvillef2223132006-01-23 16:59:58 -0500411 spin_lock_irqsave(&bcm->lock, flags);
412 if (data->frag.disabled) {
413 bcm->ieee->fts = MAX_FRAG_THRESHOLD;
414 err = 0;
415 } else {
416 if (data->frag.value >= MIN_FRAG_THRESHOLD &&
417 data->frag.value <= MAX_FRAG_THRESHOLD) {
418 bcm->ieee->fts = data->frag.value & ~0x1;
419 err = 0;
420 }
421 }
422 spin_unlock_irqrestore(&bcm->lock, flags);
423
424 return err;
425}
426
427static int bcm43xx_wx_get_frag(struct net_device *net_dev,
428 struct iw_request_info *info,
429 union iwreq_data *data,
430 char *extra)
431{
432 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
433 unsigned long flags;
434
John W. Linvillef2223132006-01-23 16:59:58 -0500435 spin_lock_irqsave(&bcm->lock, flags);
436 data->frag.value = bcm->ieee->fts;
437 data->frag.fixed = 0;
438 data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
439 spin_unlock_irqrestore(&bcm->lock, flags);
440
441 return 0;
442}
443
444static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
445 struct iw_request_info *info,
446 union iwreq_data *data,
447 char *extra)
448{
449 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
Michael Buesch393344f2006-02-05 15:28:20 +0100450 struct bcm43xx_radioinfo *radio;
451 struct bcm43xx_phyinfo *phy;
John W. Linvillef2223132006-01-23 16:59:58 -0500452 unsigned long flags;
453 int err = -ENODEV;
Michael Buesch393344f2006-02-05 15:28:20 +0100454 u16 maxpower;
John W. Linvillef2223132006-01-23 16:59:58 -0500455
Michael Buesch393344f2006-02-05 15:28:20 +0100456 if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
457 printk(PFX KERN_ERR "TX power not in dBm.\n");
458 return -EOPNOTSUPP;
459 }
460
John W. Linvillef2223132006-01-23 16:59:58 -0500461 spin_lock_irqsave(&bcm->lock, flags);
462 if (!bcm->initialized)
463 goto out_unlock;
Michael Buesch393344f2006-02-05 15:28:20 +0100464 radio = bcm->current_core->radio;
465 phy = bcm->current_core->phy;
466 if (data->txpower.disabled != (!(radio->enabled))) {
467 if (data->txpower.disabled)
John W. Linvillef2223132006-01-23 16:59:58 -0500468 bcm43xx_radio_turn_off(bcm);
469 else
470 bcm43xx_radio_turn_on(bcm);
471 }
Michael Buesch393344f2006-02-05 15:28:20 +0100472 if (data->txpower.value > 0) {
473 /* desired and maxpower dBm values are in Q5.2 */
474 if (phy->type == BCM43xx_PHYTYPE_A)
475 maxpower = bcm->sprom.maxpower_aphy;
476 else
477 maxpower = bcm->sprom.maxpower_bgphy;
478 radio->txpower_desired = limit_value(data->txpower.value << 2,
479 0, maxpower);
480 bcm43xx_phy_xmitpower(bcm);
481 }
John W. Linvillef2223132006-01-23 16:59:58 -0500482 err = 0;
483
484out_unlock:
485 spin_unlock_irqrestore(&bcm->lock, flags);
486
487 return err;
488}
489
490static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
491 struct iw_request_info *info,
492 union iwreq_data *data,
493 char *extra)
494{
495 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
Michael Buesch393344f2006-02-05 15:28:20 +0100496 struct bcm43xx_radioinfo *radio;
John W. Linvillef2223132006-01-23 16:59:58 -0500497 unsigned long flags;
Michael Buesch393344f2006-02-05 15:28:20 +0100498 int err = -ENODEV;
John W. Linvillef2223132006-01-23 16:59:58 -0500499
John W. Linvillef2223132006-01-23 16:59:58 -0500500 spin_lock_irqsave(&bcm->lock, flags);
Michael Buesch393344f2006-02-05 15:28:20 +0100501 if (!bcm->initialized)
502 goto out_unlock;
503 radio = bcm->current_core->radio;
504 /* desired dBm value is in Q5.2 */
505 data->txpower.value = radio->txpower_desired >> 2;
506 data->txpower.fixed = 1;
507 data->txpower.flags = IW_TXPOW_DBM;
508 data->txpower.disabled = !(radio->enabled);
509
510 err = 0;
511out_unlock:
John W. Linvillef2223132006-01-23 16:59:58 -0500512 spin_unlock_irqrestore(&bcm->lock, flags);
513
Michael Buesch393344f2006-02-05 15:28:20 +0100514 return err;
John W. Linvillef2223132006-01-23 16:59:58 -0500515}
516
517static int bcm43xx_wx_set_retry(struct net_device *net_dev,
518 struct iw_request_info *info,
519 union iwreq_data *data,
520 char *extra)
521{
John W. Linvillef2223132006-01-23 16:59:58 -0500522 /*TODO*/
523 return 0;
524}
525
526static int bcm43xx_wx_get_retry(struct net_device *net_dev,
527 struct iw_request_info *info,
528 union iwreq_data *data,
529 char *extra)
530{
John W. Linvillef2223132006-01-23 16:59:58 -0500531 /*TODO*/
532 return 0;
533}
534
535static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
536 struct iw_request_info *info,
537 union iwreq_data *data,
538 char *extra)
539{
540 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
541 int err;
542
John W. Linvillef2223132006-01-23 16:59:58 -0500543 err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
544
545 return err;
546}
547
548static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
549 struct iw_request_info *info,
550 union iwreq_data *data,
551 char *extra)
552{
553 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
554 int err;
555
John W. Linvillef2223132006-01-23 16:59:58 -0500556 err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
557
558 return err;
559}
560
561static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
562 struct iw_request_info *info,
563 union iwreq_data *data,
564 char *extra)
565{
566 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
567 int err;
568
John W. Linvillef2223132006-01-23 16:59:58 -0500569 err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
570
571 return err;
572}
573
574static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
575 struct iw_request_info *info,
576 union iwreq_data *data,
577 char *extra)
578{
579 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
580 int err;
581
John W. Linvillef2223132006-01-23 16:59:58 -0500582 err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
583
584 return err;
585}
586
587static int bcm43xx_wx_set_power(struct net_device *net_dev,
588 struct iw_request_info *info,
589 union iwreq_data *data,
590 char *extra)
591{
John W. Linvillef2223132006-01-23 16:59:58 -0500592 /*TODO*/
593 return 0;
594}
595
596static int bcm43xx_wx_get_power(struct net_device *net_dev,
597 struct iw_request_info *info,
598 union iwreq_data *data,
599 char *extra)
600{
John W. Linvillef2223132006-01-23 16:59:58 -0500601 /*TODO*/
602 return 0;
603}
604
605static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
606 struct iw_request_info *info,
607 union iwreq_data *data,
608 char *extra)
609{
610 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
611 unsigned long flags;
612 int mode, err = 0;
613
John W. Linvillef2223132006-01-23 16:59:58 -0500614 mode = *((int *)extra);
615 switch (mode) {
616 case 0:
617 mode = BCM43xx_RADIO_INTERFMODE_NONE;
618 break;
619 case 1:
620 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
621 break;
622 case 2:
623 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
624 break;
625 case 3:
626 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
627 break;
628 default:
629 printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
630 "0 => None, 1 => Non-WLAN, 2 => WLAN, "
631 "3 => Auto-WLAN\n");
632 return -EINVAL;
633 }
634
635 spin_lock_irqsave(&bcm->lock, flags);
636 if (bcm->initialized) {
637 err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
638 if (err) {
639 printk(KERN_ERR PFX "Interference Mitigation not "
640 "supported by device\n");
641 }
642 } else {
643 if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
644 printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
645 "not supported while the interface is down.\n");
646 err = -ENODEV;
647 } else
648 bcm->current_core->radio->interfmode = mode;
649 }
650 spin_unlock_irqrestore(&bcm->lock, flags);
651
652 return err;
653}
654
655static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
656 struct iw_request_info *info,
657 union iwreq_data *data,
658 char *extra)
659{
660 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
661 unsigned long flags;
662 int mode;
663
John W. Linvillef2223132006-01-23 16:59:58 -0500664 spin_lock_irqsave(&bcm->lock, flags);
665 mode = bcm->current_core->radio->interfmode;
666 spin_unlock_irqrestore(&bcm->lock, flags);
667
668 switch (mode) {
669 case BCM43xx_RADIO_INTERFMODE_NONE:
670 strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
671 break;
672 case BCM43xx_RADIO_INTERFMODE_NONWLAN:
673 strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
674 break;
675 case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
676 strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
677 break;
678 default:
679 assert(0);
680 }
681 data->data.length = strlen(extra) + 1;
682
683 return 0;
684}
685
686static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
687 struct iw_request_info *info,
688 union iwreq_data *data,
689 char *extra)
690{
691 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
692 unsigned long flags;
693 int on;
694
John W. Linvillef2223132006-01-23 16:59:58 -0500695 on = *((int *)extra);
696 spin_lock_irqsave(&bcm->lock, flags);
697 bcm->short_preamble = !!on;
698 spin_unlock_irqrestore(&bcm->lock, flags);
699
700 return 0;
701}
702
703static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
704 struct iw_request_info *info,
705 union iwreq_data *data,
706 char *extra)
707{
708 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
709 unsigned long flags;
710 int on;
711
John W. Linvillef2223132006-01-23 16:59:58 -0500712 spin_lock_irqsave(&bcm->lock, flags);
713 on = bcm->short_preamble;
714 spin_unlock_irqrestore(&bcm->lock, flags);
715
716 if (on)
717 strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
718 else
719 strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
720 data->data.length = strlen(extra) + 1;
721
722 return 0;
723}
724
725static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
726 struct iw_request_info *info,
727 union iwreq_data *data,
728 char *extra)
729{
730 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
731 unsigned long flags;
732 int on;
733
John W. Linvillef2223132006-01-23 16:59:58 -0500734 on = *((int *)extra);
735 spin_lock_irqsave(&bcm->lock, flags);
736 bcm->ieee->host_encrypt = !!on;
737 bcm->ieee->host_decrypt = !!on;
738 bcm->ieee->host_build_iv = !on;
739
740 spin_unlock_irqrestore(&bcm->lock, flags);
741
742 return 0;
743}
744
745static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
746 struct iw_request_info *info,
747 union iwreq_data *data,
748 char *extra)
749{
750 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
751 unsigned long flags;
752 int on;
753
John W. Linvillef2223132006-01-23 16:59:58 -0500754 spin_lock_irqsave(&bcm->lock, flags);
755 on = bcm->ieee->host_encrypt;
756 spin_unlock_irqrestore(&bcm->lock, flags);
757
758 if (on)
759 strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
760 else
761 strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
762 data->data.length = strlen(extra + 1);
763
764 return 0;
765}
766
767/* Enough buffer to hold a hexdump of the sprom data. */
768#define SPROM_BUFFERSIZE 512
769
770static int sprom2hex(const u16 *sprom, char *dump)
771{
772 int i, pos = 0;
773
774 for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
775 pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
776 "%04X", swab16(sprom[i]) & 0xFFFF);
777 }
778
779 return pos + 1;
780}
781
782static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
783{
784 char tmp[5] = { 0 };
785 int cnt = 0;
786 unsigned long parsed;
John W. Linvillef2223132006-01-23 16:59:58 -0500787
788 if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
789 return -EINVAL;
790 while (cnt < BCM43xx_SPROM_SIZE) {
791 memcpy(tmp, dump, 4);
792 dump += 4;
793 parsed = simple_strtoul(tmp, NULL, 16);
794 sprom[cnt++] = swab16((u16)parsed);
795 }
796
John W. Linvillef2223132006-01-23 16:59:58 -0500797 return 0;
798}
799
800static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
801 struct iw_request_info *info,
802 union iwreq_data *data,
803 char *extra)
804{
805 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
Michael Bueschea0922b2006-02-19 14:09:20 +0100806 int err = -EPERM;
John W. Linvillef2223132006-01-23 16:59:58 -0500807 u16 *sprom;
808 unsigned long flags;
809
810 if (!capable(CAP_SYS_RAWIO))
811 goto out;
812
813 err = -ENOMEM;
814 sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
815 GFP_KERNEL);
816 if (!sprom)
817 goto out;
818
819 spin_lock_irqsave(&bcm->lock, flags);
820 err = -ENODEV;
821 if (!bcm->initialized) {
822 spin_unlock_irqrestore(&bcm->lock, flags);
823 goto out_kfree;
824 }
Michael Bueschea0922b2006-02-19 14:09:20 +0100825 err = bcm43xx_sprom_read(bcm, sprom);
John W. Linvillef2223132006-01-23 16:59:58 -0500826 spin_unlock_irqrestore(&bcm->lock, flags);
Michael Bueschea0922b2006-02-19 14:09:20 +0100827 if (!err)
828 data->data.length = sprom2hex(sprom, extra);
John W. Linvillef2223132006-01-23 16:59:58 -0500829out_kfree:
830 kfree(sprom);
831out:
832 return err;
833}
834
835static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
836 struct iw_request_info *info,
837 union iwreq_data *data,
838 char *extra)
839{
840 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
841 int err = -EPERM;
842 u16 *sprom;
843 unsigned long flags;
844 char *input;
845 unsigned int len;
John W. Linvillef2223132006-01-23 16:59:58 -0500846
847 if (!capable(CAP_SYS_RAWIO))
848 goto out;
849
850 err = -ENOMEM;
851 sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
852 GFP_KERNEL);
853 if (!sprom)
854 goto out;
855
856 len = data->data.length;
857 extra[len - 1] = '\0';
858 input = strchr(extra, ':');
859 if (input) {
860 input++;
861 len -= input - extra;
862 } else
863 input = extra;
864 err = hex2sprom(sprom, input, len);
865 if (err)
866 goto out_kfree;
867
868 spin_lock_irqsave(&bcm->lock, flags);
869 err = -ENODEV;
Michael Bueschea0922b2006-02-19 14:09:20 +0100870 if (!bcm->initialized)
John W. Linvillef2223132006-01-23 16:59:58 -0500871 goto out_unlock;
Michael Bueschea0922b2006-02-19 14:09:20 +0100872 err = bcm43xx_sprom_write(bcm, sprom);
John W. Linvillef2223132006-01-23 16:59:58 -0500873out_unlock:
874 spin_unlock_irqrestore(&bcm->lock, flags);
875out_kfree:
876 kfree(sprom);
877out:
878 return err;
879}
880
881
882#ifdef WX
883# undef WX
884#endif
885#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
886static const iw_handler bcm43xx_wx_handlers[] = {
887 /* Wireless Identification */
888 WX(SIOCGIWNAME) = bcm43xx_wx_get_name,
889 /* Basic operations */
890 WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,
891 WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,
892 WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,
893 WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,
894 /* Informative stuff */
895 WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,
896 /* Access Point manipulation */
897 WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
898 WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
899 WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
900 WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
901 /* 802.11 specific support */
902 WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
903 WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
904 WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,
905 WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,
906 /* Other parameters */
907 WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
908 WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
909 WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,
910 WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,
911 WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,
912 WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,
913 WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,
914 WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,
915//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,
916//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,
917 /* Encoding */
918 WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,
919 WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,
920 WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,
921 WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,
922 /* Power saving */
923//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,
924//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,
925 WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
926 WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
927 WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
928 WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
929};
930#undef WX
931
932static const iw_handler bcm43xx_priv_wx_handlers[] = {
933 /* Set Interference Mitigation Mode. */
934 bcm43xx_wx_set_interfmode,
935 /* Get Interference Mitigation Mode. */
936 bcm43xx_wx_get_interfmode,
937 /* Enable/Disable Short Preamble mode. */
938 bcm43xx_wx_set_shortpreamble,
939 /* Get Short Preamble mode. */
940 bcm43xx_wx_get_shortpreamble,
941 /* Enable/Disable Software Encryption mode */
942 bcm43xx_wx_set_swencryption,
943 /* Get Software Encryption mode */
944 bcm43xx_wx_get_swencryption,
945 /* Write SRPROM data. */
946 bcm43xx_wx_sprom_write,
947 /* Read SPROM data. */
948 bcm43xx_wx_sprom_read,
949};
950
951#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)
952#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)
953#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)
954#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)
955#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)
956#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)
957#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)
958#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)
959
960#define PRIV_WX_DUMMY(ioctl) \
961 { \
962 .cmd = (ioctl), \
963 .name = "__unused" \
964 }
965
966static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
967 {
968 .cmd = PRIV_WX_SET_INTERFMODE,
969 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
970 .name = "set_interfmode",
971 },
972 {
973 .cmd = PRIV_WX_GET_INTERFMODE,
974 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
975 .name = "get_interfmode",
976 },
977 {
978 .cmd = PRIV_WX_SET_SHORTPREAMBLE,
979 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
980 .name = "set_shortpreambl",
981 },
982 {
983 .cmd = PRIV_WX_GET_SHORTPREAMBLE,
984 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
985 .name = "get_shortpreambl",
986 },
987 {
988 .cmd = PRIV_WX_SET_SWENCRYPTION,
989 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
990 .name = "set_swencryption",
991 },
992 {
993 .cmd = PRIV_WX_GET_SWENCRYPTION,
994 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
995 .name = "get_swencryption",
996 },
997 {
998 .cmd = PRIV_WX_SPROM_WRITE,
999 .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
1000 .name = "write_sprom",
1001 },
1002 {
1003 .cmd = PRIV_WX_SPROM_READ,
1004 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
1005 .name = "read_sprom",
1006 },
1007};
1008
1009const struct iw_handler_def bcm43xx_wx_handlers_def = {
1010 .standard = bcm43xx_wx_handlers,
1011 .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),
1012 .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
1013 .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),
1014 .private = bcm43xx_priv_wx_handlers,
1015 .private_args = bcm43xx_priv_wx_args,
1016};
1017
1018/* vim: set ts=8 sw=8 sts=8: */