blob: b45063974ae9dd196b93bb7a310bf2c62c7b5f3a [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;
Michael Buesche9357c02006-03-13 19:27:34 +010059 int i;
John W. Linvillef2223132006-01-23 16:59:58 -050060 struct bcm43xx_phyinfo *phy;
61 char suffix[7] = { 0 };
62 int have_a = 0, have_b = 0, have_g = 0;
63
Michael Bueschefccb642006-03-11 13:39:14 +010064 bcm43xx_lock(bcm, flags);
Michael Buesche9357c02006-03-13 19:27:34 +010065 for (i = 0; i < bcm->nr_80211_available; i++) {
66 phy = &(bcm->core_80211_ext[i].phy);
John W. Linvillef2223132006-01-23 16:59:58 -050067 switch (phy->type) {
68 case BCM43xx_PHYTYPE_A:
69 have_a = 1;
70 break;
71 case BCM43xx_PHYTYPE_G:
72 have_g = 1;
73 case BCM43xx_PHYTYPE_B:
74 have_b = 1;
75 break;
76 default:
77 assert(0);
78 }
79 }
Michael Bueschefccb642006-03-11 13:39:14 +010080 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -050081
82 i = 0;
83 if (have_a) {
84 suffix[i++] = 'a';
85 suffix[i++] = '/';
86 }
87 if (have_b) {
88 suffix[i++] = 'b';
89 suffix[i++] = '/';
90 }
91 if (have_g) {
92 suffix[i++] = 'g';
93 suffix[i++] = '/';
94 }
95 if (i != 0)
96 suffix[i - 1] = '\0';
97
98 snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);
99
100 return 0;
101}
102
103static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,
104 struct iw_request_info *info,
105 union iwreq_data *data,
106 char *extra)
107{
108 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
John W. Linvillef2223132006-01-23 16:59:58 -0500109 unsigned long flags;
110 u8 channel;
111 int freq;
Michael Buesch10d8dd82006-02-19 22:08:48 +0100112 int err = -EINVAL;
John W. Linvillef2223132006-01-23 16:59:58 -0500113
Michael Bueschefccb642006-03-11 13:39:14 +0100114 bcm43xx_lock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500115 if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
116 channel = data->freq.m;
117 freq = bcm43xx_channel_to_freq(bcm, channel);
118 } else {
119 channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
120 freq = data->freq.m;
121 }
122 if (!bcm43xx_is_valid_channel(bcm, channel))
Michael Buesch10d8dd82006-02-19 22:08:48 +0100123 goto out_unlock;
John W. Linvillef2223132006-01-23 16:59:58 -0500124 if (bcm->initialized) {
125 //ieee80211softmac_disassoc(softmac, $REASON);
126 bcm43xx_mac_suspend(bcm);
127 err = bcm43xx_radio_selectchannel(bcm, channel, 0);
128 bcm43xx_mac_enable(bcm);
Michael Buesch10d8dd82006-02-19 22:08:48 +0100129 } else {
Michael Buesche9357c02006-03-13 19:27:34 +0100130 bcm43xx_current_radio(bcm)->initial_channel = channel;
Michael Buesch10d8dd82006-02-19 22:08:48 +0100131 err = 0;
132 }
133out_unlock:
Michael Bueschefccb642006-03-11 13:39:14 +0100134 bcm43xx_unlock_mmio(bcm, 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);
Michael Buesche9357c02006-03-13 19:27:34 +0100145 struct bcm43xx_radioinfo *radio;
John W. Linvillef2223132006-01-23 16:59:58 -0500146 unsigned long flags;
147 int err = -ENODEV;
148 u16 channel;
149
Michael Bueschefccb642006-03-11 13:39:14 +0100150 bcm43xx_lock(bcm, flags);
Michael Buesche9357c02006-03-13 19:27:34 +0100151 radio = bcm43xx_current_radio(bcm);
152 channel = radio->channel;
John W. Linvillef2223132006-01-23 16:59:58 -0500153 if (channel == 0xFF) {
154 assert(!bcm->initialized);
Michael Buesche9357c02006-03-13 19:27:34 +0100155 channel = radio->initial_channel;
John W. Linvillef2223132006-01-23 16:59:58 -0500156 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:
Michael Bueschefccb642006-03-11 13:39:14 +0100166 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500167
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
Michael Bueschefccb642006-03-11 13:39:14 +0100184 bcm43xx_lock_mmio(bcm, flags);
Michael Buesch5b4b9772006-05-01 22:43:00 +0200185 if (bcm->initialized) {
186 if (bcm->ieee->iw_mode != mode)
187 bcm43xx_set_iwmode(bcm, mode);
188 } else
189 bcm->ieee->iw_mode = mode;
Michael Bueschefccb642006-03-11 13:39:14 +0100190 bcm43xx_unlock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500191
192 return 0;
193}
194
195static int bcm43xx_wx_get_mode(struct net_device *net_dev,
196 struct iw_request_info *info,
197 union iwreq_data *data,
198 char *extra)
199{
200 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
201 unsigned long flags;
202
Michael Bueschefccb642006-03-11 13:39:14 +0100203 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500204 data->mode = bcm->ieee->iw_mode;
Michael Bueschefccb642006-03-11 13:39:14 +0100205 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500206
207 return 0;
208}
209
John W. Linvillef2223132006-01-23 16:59:58 -0500210static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
211 struct iw_request_info *info,
212 union iwreq_data *data,
213 char *extra)
214{
215 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
216 struct iw_range *range = (struct iw_range *)extra;
217 const struct ieee80211_geo *geo;
218 unsigned long flags;
219 int i, j;
Michael Buesche9357c02006-03-13 19:27:34 +0100220 struct bcm43xx_phyinfo *phy;
John W. Linvillef2223132006-01-23 16:59:58 -0500221
John W. Linvillef2223132006-01-23 16:59:58 -0500222 data->data.length = sizeof(*range);
223 memset(range, 0, sizeof(*range));
224
225 //TODO: What about 802.11b?
226 /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
227 range->throughput = 27 * 1000 * 1000;
228
229 range->max_qual.qual = 100;
230 /* TODO: Real max RSSI */
Michael Buesch72fb8512006-03-22 18:10:19 +0100231 range->max_qual.level = 3;
232 range->max_qual.noise = 100;
John W. Linvillef2223132006-01-23 16:59:58 -0500233 range->max_qual.updated = 7;
234
235 range->avg_qual.qual = 70;
Michael Buesch72fb8512006-03-22 18:10:19 +0100236 range->avg_qual.level = 2;
237 range->avg_qual.noise = 40;
John W. Linvillef2223132006-01-23 16:59:58 -0500238 range->avg_qual.updated = 7;
239
240 range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
241 range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
242 range->min_frag = MIN_FRAG_THRESHOLD;
243 range->max_frag = MAX_FRAG_THRESHOLD;
244
245 range->encoding_size[0] = 5;
246 range->encoding_size[1] = 13;
247 range->num_encoding_sizes = 2;
248 range->max_encoding_tokens = WEP_KEYS;
249
250 range->we_version_compiled = WIRELESS_EXT;
Michael Buesch8fa252d2006-02-02 19:49:15 +0100251 range->we_version_source = BCM43xx_WX_VERSION;
252
253 range->enc_capa = IW_ENC_CAPA_WPA |
254 IW_ENC_CAPA_WPA2 |
255 IW_ENC_CAPA_CIPHER_TKIP |
256 IW_ENC_CAPA_CIPHER_CCMP;
John W. Linvillef2223132006-01-23 16:59:58 -0500257
Michael Bueschefccb642006-03-11 13:39:14 +0100258 bcm43xx_lock(bcm, flags);
Michael Buesche9357c02006-03-13 19:27:34 +0100259 phy = bcm43xx_current_phy(bcm);
John W. Linvillef2223132006-01-23 16:59:58 -0500260
261 range->num_bitrates = 0;
262 i = 0;
Michael Buesche9357c02006-03-13 19:27:34 +0100263 if (phy->type == BCM43xx_PHYTYPE_A ||
264 phy->type == BCM43xx_PHYTYPE_G) {
John W. Linvillef2223132006-01-23 16:59:58 -0500265 range->num_bitrates = 8;
266 range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB;
267 range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;
268 range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;
269 range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;
270 range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;
271 range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;
272 range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;
273 range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;
274 }
Michael Buesche9357c02006-03-13 19:27:34 +0100275 if (phy->type == BCM43xx_PHYTYPE_B ||
276 phy->type == BCM43xx_PHYTYPE_G) {
John W. Linvillef2223132006-01-23 16:59:58 -0500277 range->num_bitrates += 4;
278 range->bitrate[i++] = IEEE80211_CCK_RATE_1MB;
279 range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;
280 range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;
281 range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;
282 }
283
284 geo = ieee80211_get_geo(bcm->ieee);
285 range->num_channels = geo->a_channels + geo->bg_channels;
286 j = 0;
287 for (i = 0; i < geo->a_channels; i++) {
288 if (j == IW_MAX_FREQUENCIES)
289 break;
290 range->freq[j].i = j + 1;
291 range->freq[j].m = geo->a[i].freq;//FIXME?
292 range->freq[j].e = 1;
293 j++;
294 }
295 for (i = 0; i < geo->bg_channels; i++) {
296 if (j == IW_MAX_FREQUENCIES)
297 break;
298 range->freq[j].i = j + 1;
299 range->freq[j].m = geo->bg[i].freq;//FIXME?
300 range->freq[j].e = 1;
301 j++;
302 }
303 range->num_frequency = j;
304
Michael Bueschefccb642006-03-11 13:39:14 +0100305 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500306
307 return 0;
308}
309
310static int bcm43xx_wx_set_nick(struct net_device *net_dev,
311 struct iw_request_info *info,
312 union iwreq_data *data,
313 char *extra)
314{
315 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
316 unsigned long flags;
317 size_t len;
318
Michael Bueschefccb642006-03-11 13:39:14 +0100319 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500320 len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
321 memcpy(bcm->nick, extra, len);
322 bcm->nick[len] = '\0';
Michael Bueschefccb642006-03-11 13:39:14 +0100323 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500324
325 return 0;
326}
327
328static int bcm43xx_wx_get_nick(struct net_device *net_dev,
329 struct iw_request_info *info,
330 union iwreq_data *data,
331 char *extra)
332{
333 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
334 unsigned long flags;
335 size_t len;
336
Michael Bueschefccb642006-03-11 13:39:14 +0100337 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500338 len = strlen(bcm->nick) + 1;
339 memcpy(extra, bcm->nick, len);
340 data->data.length = (__u16)len;
341 data->data.flags = 1;
Michael Bueschefccb642006-03-11 13:39:14 +0100342 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500343
344 return 0;
345}
346
347static int bcm43xx_wx_set_rts(struct net_device *net_dev,
348 struct iw_request_info *info,
349 union iwreq_data *data,
350 char *extra)
351{
352 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
353 unsigned long flags;
354 int err = -EINVAL;
355
Michael Bueschefccb642006-03-11 13:39:14 +0100356 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500357 if (data->rts.disabled) {
358 bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
359 err = 0;
360 } else {
361 if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
362 data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
363 bcm->rts_threshold = data->rts.value;
364 err = 0;
365 }
366 }
Michael Bueschefccb642006-03-11 13:39:14 +0100367 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500368
369 return err;
370}
371
372static int bcm43xx_wx_get_rts(struct net_device *net_dev,
373 struct iw_request_info *info,
374 union iwreq_data *data,
375 char *extra)
376{
377 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
378 unsigned long flags;
379
Michael Bueschefccb642006-03-11 13:39:14 +0100380 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500381 data->rts.value = bcm->rts_threshold;
382 data->rts.fixed = 0;
383 data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
Michael Bueschefccb642006-03-11 13:39:14 +0100384 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500385
386 return 0;
387}
388
389static int bcm43xx_wx_set_frag(struct net_device *net_dev,
390 struct iw_request_info *info,
391 union iwreq_data *data,
392 char *extra)
393{
394 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
395 unsigned long flags;
396 int err = -EINVAL;
397
Michael Bueschefccb642006-03-11 13:39:14 +0100398 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500399 if (data->frag.disabled) {
400 bcm->ieee->fts = MAX_FRAG_THRESHOLD;
401 err = 0;
402 } else {
403 if (data->frag.value >= MIN_FRAG_THRESHOLD &&
404 data->frag.value <= MAX_FRAG_THRESHOLD) {
405 bcm->ieee->fts = data->frag.value & ~0x1;
406 err = 0;
407 }
408 }
Michael Bueschefccb642006-03-11 13:39:14 +0100409 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500410
411 return err;
412}
413
414static int bcm43xx_wx_get_frag(struct net_device *net_dev,
415 struct iw_request_info *info,
416 union iwreq_data *data,
417 char *extra)
418{
419 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
420 unsigned long flags;
421
Michael Bueschefccb642006-03-11 13:39:14 +0100422 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500423 data->frag.value = bcm->ieee->fts;
424 data->frag.fixed = 0;
425 data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
Michael Bueschefccb642006-03-11 13:39:14 +0100426 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500427
428 return 0;
429}
430
431static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
432 struct iw_request_info *info,
433 union iwreq_data *data,
434 char *extra)
435{
436 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
Michael Buesch393344f2006-02-05 15:28:20 +0100437 struct bcm43xx_radioinfo *radio;
438 struct bcm43xx_phyinfo *phy;
John W. Linvillef2223132006-01-23 16:59:58 -0500439 unsigned long flags;
440 int err = -ENODEV;
Michael Buesch393344f2006-02-05 15:28:20 +0100441 u16 maxpower;
John W. Linvillef2223132006-01-23 16:59:58 -0500442
Michael Buesch393344f2006-02-05 15:28:20 +0100443 if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
444 printk(PFX KERN_ERR "TX power not in dBm.\n");
445 return -EOPNOTSUPP;
446 }
447
Michael Bueschefccb642006-03-11 13:39:14 +0100448 bcm43xx_lock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500449 if (!bcm->initialized)
450 goto out_unlock;
Michael Buesche9357c02006-03-13 19:27:34 +0100451 radio = bcm43xx_current_radio(bcm);
452 phy = bcm43xx_current_phy(bcm);
Michael Buesch393344f2006-02-05 15:28:20 +0100453 if (data->txpower.disabled != (!(radio->enabled))) {
454 if (data->txpower.disabled)
John W. Linvillef2223132006-01-23 16:59:58 -0500455 bcm43xx_radio_turn_off(bcm);
456 else
457 bcm43xx_radio_turn_on(bcm);
458 }
Michael Buesch393344f2006-02-05 15:28:20 +0100459 if (data->txpower.value > 0) {
460 /* desired and maxpower dBm values are in Q5.2 */
461 if (phy->type == BCM43xx_PHYTYPE_A)
462 maxpower = bcm->sprom.maxpower_aphy;
463 else
464 maxpower = bcm->sprom.maxpower_bgphy;
465 radio->txpower_desired = limit_value(data->txpower.value << 2,
466 0, maxpower);
467 bcm43xx_phy_xmitpower(bcm);
468 }
John W. Linvillef2223132006-01-23 16:59:58 -0500469 err = 0;
470
471out_unlock:
Michael Bueschefccb642006-03-11 13:39:14 +0100472 bcm43xx_unlock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500473
474 return err;
475}
476
477static 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);
Michael Buesch393344f2006-02-05 15:28:20 +0100483 struct bcm43xx_radioinfo *radio;
John W. Linvillef2223132006-01-23 16:59:58 -0500484 unsigned long flags;
Michael Buesch393344f2006-02-05 15:28:20 +0100485 int err = -ENODEV;
John W. Linvillef2223132006-01-23 16:59:58 -0500486
Michael Bueschefccb642006-03-11 13:39:14 +0100487 bcm43xx_lock(bcm, flags);
Michael Buesch393344f2006-02-05 15:28:20 +0100488 if (!bcm->initialized)
489 goto out_unlock;
Michael Buesche9357c02006-03-13 19:27:34 +0100490 radio = bcm43xx_current_radio(bcm);
Michael Buesch393344f2006-02-05 15:28:20 +0100491 /* desired dBm value is in Q5.2 */
492 data->txpower.value = radio->txpower_desired >> 2;
493 data->txpower.fixed = 1;
494 data->txpower.flags = IW_TXPOW_DBM;
495 data->txpower.disabled = !(radio->enabled);
496
497 err = 0;
498out_unlock:
Michael Bueschefccb642006-03-11 13:39:14 +0100499 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500500
Michael Buesch393344f2006-02-05 15:28:20 +0100501 return err;
John W. Linvillef2223132006-01-23 16:59:58 -0500502}
503
John W. Linvillef2223132006-01-23 16:59:58 -0500504static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
505 struct iw_request_info *info,
506 union iwreq_data *data,
507 char *extra)
508{
509 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
510 int err;
511
John W. Linvillef2223132006-01-23 16:59:58 -0500512 err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
513
514 return err;
515}
516
517static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
518 struct iw_request_info *info,
519 union iwreq_data *data,
520 char *extra)
521{
522 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
523 int err;
524
John W. Linvillef2223132006-01-23 16:59:58 -0500525 err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
526
527 return err;
528}
529
530static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
531 struct iw_request_info *info,
532 union iwreq_data *data,
533 char *extra)
534{
535 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
536 int err;
537
John W. Linvillef2223132006-01-23 16:59:58 -0500538 err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
539
540 return err;
541}
542
543static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
544 struct iw_request_info *info,
545 union iwreq_data *data,
546 char *extra)
547{
548 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
549 int err;
550
John W. Linvillef2223132006-01-23 16:59:58 -0500551 err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
552
553 return err;
554}
555
John W. Linvillef2223132006-01-23 16:59:58 -0500556static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
557 struct iw_request_info *info,
558 union iwreq_data *data,
559 char *extra)
560{
561 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
562 unsigned long flags;
563 int mode, err = 0;
564
John W. Linvillef2223132006-01-23 16:59:58 -0500565 mode = *((int *)extra);
566 switch (mode) {
567 case 0:
568 mode = BCM43xx_RADIO_INTERFMODE_NONE;
569 break;
570 case 1:
571 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
572 break;
573 case 2:
574 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
575 break;
576 case 3:
577 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
578 break;
579 default:
580 printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
581 "0 => None, 1 => Non-WLAN, 2 => WLAN, "
582 "3 => Auto-WLAN\n");
583 return -EINVAL;
584 }
585
Michael Bueschefccb642006-03-11 13:39:14 +0100586 bcm43xx_lock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500587 if (bcm->initialized) {
588 err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
589 if (err) {
590 printk(KERN_ERR PFX "Interference Mitigation not "
591 "supported by device\n");
592 }
593 } else {
594 if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
595 printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
596 "not supported while the interface is down.\n");
597 err = -ENODEV;
598 } else
Michael Buesche9357c02006-03-13 19:27:34 +0100599 bcm43xx_current_radio(bcm)->interfmode = mode;
John W. Linvillef2223132006-01-23 16:59:58 -0500600 }
Michael Bueschefccb642006-03-11 13:39:14 +0100601 bcm43xx_unlock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500602
603 return err;
604}
605
606static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
607 struct iw_request_info *info,
608 union iwreq_data *data,
609 char *extra)
610{
611 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
612 unsigned long flags;
613 int mode;
614
Michael Bueschefccb642006-03-11 13:39:14 +0100615 bcm43xx_lock(bcm, flags);
Michael Buesche9357c02006-03-13 19:27:34 +0100616 mode = bcm43xx_current_radio(bcm)->interfmode;
Michael Bueschefccb642006-03-11 13:39:14 +0100617 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500618
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
637static 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
John W. Linvillef2223132006-01-23 16:59:58 -0500646 on = *((int *)extra);
Michael Bueschefccb642006-03-11 13:39:14 +0100647 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500648 bcm->short_preamble = !!on;
Michael Bueschefccb642006-03-11 13:39:14 +0100649 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500650
651 return 0;
652}
653
654static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
655 struct iw_request_info *info,
656 union iwreq_data *data,
657 char *extra)
658{
659 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
660 unsigned long flags;
661 int on;
662
Michael Bueschefccb642006-03-11 13:39:14 +0100663 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500664 on = bcm->short_preamble;
Michael Bueschefccb642006-03-11 13:39:14 +0100665 bcm43xx_unlock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500666
667 if (on)
668 strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
669 else
670 strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
671 data->data.length = strlen(extra) + 1;
672
673 return 0;
674}
675
676static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
677 struct iw_request_info *info,
678 union iwreq_data *data,
679 char *extra)
680{
681 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
682 unsigned long flags;
683 int on;
684
John W. Linvillef2223132006-01-23 16:59:58 -0500685 on = *((int *)extra);
Michael Bueschefccb642006-03-11 13:39:14 +0100686
687 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500688 bcm->ieee->host_encrypt = !!on;
689 bcm->ieee->host_decrypt = !!on;
690 bcm->ieee->host_build_iv = !on;
Michael Bueschefccb642006-03-11 13:39:14 +0100691 bcm43xx_unlock(bcm, flags);
692
John W. Linvillef2223132006-01-23 16:59:58 -0500693 return 0;
694}
695
696static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
697 struct iw_request_info *info,
698 union iwreq_data *data,
699 char *extra)
700{
701 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
702 unsigned long flags;
703 int on;
Michael Bueschefccb642006-03-11 13:39:14 +0100704
705 bcm43xx_lock(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500706 on = bcm->ieee->host_encrypt;
Michael Bueschefccb642006-03-11 13:39:14 +0100707 bcm43xx_unlock(bcm, flags);
708
John W. Linvillef2223132006-01-23 16:59:58 -0500709 if (on)
710 strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
711 else
712 strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
713 data->data.length = strlen(extra + 1);
Michael Bueschefccb642006-03-11 13:39:14 +0100714
John W. Linvillef2223132006-01-23 16:59:58 -0500715 return 0;
716}
717
718/* Enough buffer to hold a hexdump of the sprom data. */
719#define SPROM_BUFFERSIZE 512
720
721static int sprom2hex(const u16 *sprom, char *dump)
722{
723 int i, pos = 0;
724
725 for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
726 pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
727 "%04X", swab16(sprom[i]) & 0xFFFF);
728 }
729
730 return pos + 1;
731}
732
733static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
734{
735 char tmp[5] = { 0 };
736 int cnt = 0;
737 unsigned long parsed;
John W. Linvillef2223132006-01-23 16:59:58 -0500738
739 if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
740 return -EINVAL;
741 while (cnt < BCM43xx_SPROM_SIZE) {
742 memcpy(tmp, dump, 4);
743 dump += 4;
744 parsed = simple_strtoul(tmp, NULL, 16);
745 sprom[cnt++] = swab16((u16)parsed);
746 }
747
John W. Linvillef2223132006-01-23 16:59:58 -0500748 return 0;
749}
750
751static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
752 struct iw_request_info *info,
753 union iwreq_data *data,
754 char *extra)
755{
756 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
Michael Bueschea0922b2006-02-19 14:09:20 +0100757 int err = -EPERM;
John W. Linvillef2223132006-01-23 16:59:58 -0500758 u16 *sprom;
759 unsigned long flags;
760
761 if (!capable(CAP_SYS_RAWIO))
762 goto out;
763
764 err = -ENOMEM;
765 sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
766 GFP_KERNEL);
767 if (!sprom)
768 goto out;
769
Michael Bueschefccb642006-03-11 13:39:14 +0100770 bcm43xx_lock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500771 err = -ENODEV;
Michael Bueschefccb642006-03-11 13:39:14 +0100772 if (bcm->initialized)
773 err = bcm43xx_sprom_read(bcm, sprom);
774 bcm43xx_unlock_mmio(bcm, flags);
Michael Bueschea0922b2006-02-19 14:09:20 +0100775 if (!err)
776 data->data.length = sprom2hex(sprom, extra);
John W. Linvillef2223132006-01-23 16:59:58 -0500777 kfree(sprom);
778out:
779 return err;
780}
781
782static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
783 struct iw_request_info *info,
784 union iwreq_data *data,
785 char *extra)
786{
787 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
788 int err = -EPERM;
789 u16 *sprom;
790 unsigned long flags;
791 char *input;
792 unsigned int len;
John W. Linvillef2223132006-01-23 16:59:58 -0500793
794 if (!capable(CAP_SYS_RAWIO))
795 goto out;
796
797 err = -ENOMEM;
798 sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
799 GFP_KERNEL);
800 if (!sprom)
801 goto out;
802
803 len = data->data.length;
804 extra[len - 1] = '\0';
805 input = strchr(extra, ':');
806 if (input) {
807 input++;
808 len -= input - extra;
809 } else
810 input = extra;
811 err = hex2sprom(sprom, input, len);
812 if (err)
813 goto out_kfree;
814
Michael Bueschefccb642006-03-11 13:39:14 +0100815 bcm43xx_lock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500816 err = -ENODEV;
Michael Bueschefccb642006-03-11 13:39:14 +0100817 if (bcm->initialized)
818 err = bcm43xx_sprom_write(bcm, sprom);
819 bcm43xx_unlock_mmio(bcm, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500820out_kfree:
821 kfree(sprom);
822out:
823 return err;
824}
825
Michael Buesch72fb8512006-03-22 18:10:19 +0100826/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
827
828static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
829{
830 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
831 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
832 struct iw_statistics *wstats;
833
834 wstats = &bcm->stats.wstats;
835 if (!mac->associated) {
836 wstats->miss.beacon = 0;
837// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
838 wstats->discard.retries = 0;
839// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
840 wstats->discard.nwid = 0;
841// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
842 wstats->discard.code = 0;
843// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here
844 wstats->discard.fragment = 0;
845 wstats->discard.misc = 0;
846 wstats->qual.qual = 0;
847 wstats->qual.level = 0;
848 wstats->qual.noise = 0;
849 wstats->qual.updated = 7;
850 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
851 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
852 return wstats;
853 }
854 /* fill in the real statistics when iface associated */
855 wstats->qual.qual = 100; // TODO: get the real signal quality
856 wstats->qual.level = 3 - bcm->stats.link_quality;
857 wstats->qual.noise = bcm->stats.noise;
858 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
859 IW_QUAL_NOISE_UPDATED;
860 wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
861 wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
862 wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
863 wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
864 wstats->discard.misc = 0; // FIXME
865 wstats->miss.beacon = 0; // FIXME
866 return wstats;
867}
868
John W. Linvillef2223132006-01-23 16:59:58 -0500869
870#ifdef WX
871# undef WX
872#endif
873#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
874static const iw_handler bcm43xx_wx_handlers[] = {
875 /* Wireless Identification */
876 WX(SIOCGIWNAME) = bcm43xx_wx_get_name,
877 /* Basic operations */
878 WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,
879 WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,
880 WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,
881 WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,
882 /* Informative stuff */
883 WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,
884 /* Access Point manipulation */
885 WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
886 WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
887 WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
888 WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
889 /* 802.11 specific support */
890 WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
891 WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
892 WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,
893 WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,
894 /* Other parameters */
895 WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
896 WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
897 WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,
898 WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,
899 WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,
900 WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,
901 WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,
902 WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,
903//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,
904//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,
905 /* Encoding */
906 WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,
907 WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,
908 WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,
909 WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,
910 /* Power saving */
911//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,
912//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,
913 WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
914 WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
915 WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
916 WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
917};
918#undef WX
919
920static const iw_handler bcm43xx_priv_wx_handlers[] = {
921 /* Set Interference Mitigation Mode. */
922 bcm43xx_wx_set_interfmode,
923 /* Get Interference Mitigation Mode. */
924 bcm43xx_wx_get_interfmode,
925 /* Enable/Disable Short Preamble mode. */
926 bcm43xx_wx_set_shortpreamble,
927 /* Get Short Preamble mode. */
928 bcm43xx_wx_get_shortpreamble,
929 /* Enable/Disable Software Encryption mode */
930 bcm43xx_wx_set_swencryption,
931 /* Get Software Encryption mode */
932 bcm43xx_wx_get_swencryption,
933 /* Write SRPROM data. */
934 bcm43xx_wx_sprom_write,
935 /* Read SPROM data. */
936 bcm43xx_wx_sprom_read,
937};
938
939#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)
940#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)
941#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)
942#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)
943#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)
944#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)
945#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)
946#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)
947
948#define PRIV_WX_DUMMY(ioctl) \
949 { \
950 .cmd = (ioctl), \
951 .name = "__unused" \
952 }
953
954static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
955 {
956 .cmd = PRIV_WX_SET_INTERFMODE,
957 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
958 .name = "set_interfmode",
959 },
960 {
961 .cmd = PRIV_WX_GET_INTERFMODE,
962 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
963 .name = "get_interfmode",
964 },
965 {
966 .cmd = PRIV_WX_SET_SHORTPREAMBLE,
967 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
Erik Mouwa3921492006-04-13 15:02:27 +0200968 .name = "set_shortpreamb",
John W. Linvillef2223132006-01-23 16:59:58 -0500969 },
970 {
971 .cmd = PRIV_WX_GET_SHORTPREAMBLE,
972 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
Erik Mouwa3921492006-04-13 15:02:27 +0200973 .name = "get_shortpreamb",
John W. Linvillef2223132006-01-23 16:59:58 -0500974 },
975 {
976 .cmd = PRIV_WX_SET_SWENCRYPTION,
977 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
Erik Mouwa3921492006-04-13 15:02:27 +0200978 .name = "set_swencrypt",
John W. Linvillef2223132006-01-23 16:59:58 -0500979 },
980 {
981 .cmd = PRIV_WX_GET_SWENCRYPTION,
982 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
Erik Mouwa3921492006-04-13 15:02:27 +0200983 .name = "get_swencrypt",
John W. Linvillef2223132006-01-23 16:59:58 -0500984 },
985 {
986 .cmd = PRIV_WX_SPROM_WRITE,
987 .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
988 .name = "write_sprom",
989 },
990 {
991 .cmd = PRIV_WX_SPROM_READ,
992 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
993 .name = "read_sprom",
994 },
995};
996
997const struct iw_handler_def bcm43xx_wx_handlers_def = {
998 .standard = bcm43xx_wx_handlers,
999 .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),
1000 .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
1001 .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),
1002 .private = bcm43xx_priv_wx_handlers,
1003 .private_args = bcm43xx_priv_wx_args,
Michael Buesch72fb8512006-03-22 18:10:19 +01001004 .get_wireless_stats = bcm43xx_get_wireless_stats,
John W. Linvillef2223132006-01-23 16:59:58 -05001005};