diff options
Diffstat (limited to 'drivers/net/wireless/bcm43xx/bcm43xx_wx.c')
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_wx.c | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c new file mode 100644 index 000000000000..3daee828ef4b --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c | |||
@@ -0,0 +1,1002 @@ | |||
1 | /* | ||
2 | |||
3 | Broadcom BCM43xx wireless driver | ||
4 | |||
5 | Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, | ||
6 | Stefano Brivio <st3@riseup.net> | ||
7 | Michael Buesch <mbuesch@freenet.de> | ||
8 | Danny van Dyk <kugelfang@gentoo.org> | ||
9 | Andreas Jaggi <andreas.jaggi@waterwave.ch> | ||
10 | |||
11 | Some parts of the code in this file are derived from the ipw2200 | ||
12 | driver Copyright(c) 2003 - 2004 Intel Corporation. | ||
13 | |||
14 | This program is free software; you can redistribute it and/or modify | ||
15 | it under the terms of the GNU General Public License as published by | ||
16 | the Free Software Foundation; either version 2 of the License, or | ||
17 | (at your option) any later version. | ||
18 | |||
19 | This program is distributed in the hope that it will be useful, | ||
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | GNU General Public License for more details. | ||
23 | |||
24 | You should have received a copy of the GNU General Public License | ||
25 | along with this program; see the file COPYING. If not, write to | ||
26 | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | ||
27 | Boston, MA 02110-1301, USA. | ||
28 | |||
29 | */ | ||
30 | |||
31 | #include <linux/wireless.h> | ||
32 | #include <net/iw_handler.h> | ||
33 | #include <net/ieee80211softmac.h> | ||
34 | #include <net/ieee80211softmac_wx.h> | ||
35 | #include <linux/capability.h> | ||
36 | #include <linux/sched.h> /* for capable() */ | ||
37 | #include <linux/delay.h> | ||
38 | |||
39 | #include "bcm43xx.h" | ||
40 | #include "bcm43xx_wx.h" | ||
41 | #include "bcm43xx_main.h" | ||
42 | #include "bcm43xx_radio.h" | ||
43 | #include "bcm43xx_phy.h" | ||
44 | |||
45 | |||
46 | /* The WIRELESS_EXT version, which is implemented by this driver. */ | ||
47 | #define BCM43xx_WX_VERSION 18 | ||
48 | |||
49 | #define MAX_WX_STRING 80 | ||
50 | |||
51 | |||
52 | static int bcm43xx_wx_get_name(struct net_device *net_dev, | ||
53 | struct iw_request_info *info, | ||
54 | union iwreq_data *data, | ||
55 | char *extra) | ||
56 | { | ||
57 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
58 | unsigned long flags; | ||
59 | int i; | ||
60 | struct bcm43xx_phyinfo *phy; | ||
61 | char suffix[7] = { 0 }; | ||
62 | int have_a = 0, have_b = 0, have_g = 0; | ||
63 | |||
64 | bcm43xx_lock(bcm, flags); | ||
65 | for (i = 0; i < bcm->nr_80211_available; i++) { | ||
66 | phy = &(bcm->core_80211_ext[i].phy); | ||
67 | switch (phy->type) { | ||
68 | case BCM43xx_PHYTYPE_A: | ||
69 | have_a = 1; | ||
70 | break; | ||
71 | case BCM43xx_PHYTYPE_G: | ||
72 | have_g = 1; | ||
73 | case BCM43xx_PHYTYPE_B: | ||
74 | have_b = 1; | ||
75 | break; | ||
76 | default: | ||
77 | assert(0); | ||
78 | } | ||
79 | } | ||
80 | bcm43xx_unlock(bcm, flags); | ||
81 | |||
82 | i = 0; | ||
83 | if (have_a) { | ||
84 | suffix[i++] = 'a'; | ||
85 | suffix[i++] = '/'; | ||
86 | } | ||
87 | if (have_b) { | ||
88 | suffix[i++] = 'b'; | ||
89 | suffix[i++] = '/'; | ||
90 | } | ||
91 | if (have_g) { | ||
92 | suffix[i++] = 'g'; | ||
93 | suffix[i++] = '/'; | ||
94 | } | ||
95 | if (i != 0) | ||
96 | suffix[i - 1] = '\0'; | ||
97 | |||
98 | snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, | ||
104 | struct iw_request_info *info, | ||
105 | union iwreq_data *data, | ||
106 | char *extra) | ||
107 | { | ||
108 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
109 | unsigned long flags; | ||
110 | u8 channel; | ||
111 | int freq; | ||
112 | int err = -EINVAL; | ||
113 | |||
114 | bcm43xx_lock_mmio(bcm, flags); | ||
115 | if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { | ||
116 | channel = data->freq.m; | ||
117 | freq = bcm43xx_channel_to_freq(bcm, channel); | ||
118 | } else { | ||
119 | channel = bcm43xx_freq_to_channel(bcm, data->freq.m); | ||
120 | freq = data->freq.m; | ||
121 | } | ||
122 | if (!bcm43xx_is_valid_channel(bcm, channel)) | ||
123 | goto out_unlock; | ||
124 | if (bcm->initialized) { | ||
125 | //ieee80211softmac_disassoc(softmac, $REASON); | ||
126 | bcm43xx_mac_suspend(bcm); | ||
127 | err = bcm43xx_radio_selectchannel(bcm, channel, 0); | ||
128 | bcm43xx_mac_enable(bcm); | ||
129 | } else { | ||
130 | bcm43xx_current_radio(bcm)->initial_channel = channel; | ||
131 | err = 0; | ||
132 | } | ||
133 | out_unlock: | ||
134 | bcm43xx_unlock_mmio(bcm, flags); | ||
135 | |||
136 | return err; | ||
137 | } | ||
138 | |||
139 | static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, | ||
140 | struct iw_request_info *info, | ||
141 | union iwreq_data *data, | ||
142 | char *extra) | ||
143 | { | ||
144 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
145 | struct bcm43xx_radioinfo *radio; | ||
146 | unsigned long flags; | ||
147 | int err = -ENODEV; | ||
148 | u16 channel; | ||
149 | |||
150 | bcm43xx_lock(bcm, flags); | ||
151 | radio = bcm43xx_current_radio(bcm); | ||
152 | channel = radio->channel; | ||
153 | if (channel == 0xFF) { | ||
154 | assert(!bcm->initialized); | ||
155 | channel = radio->initial_channel; | ||
156 | if (channel == 0xFF) | ||
157 | goto out_unlock; | ||
158 | } | ||
159 | assert(channel > 0 && channel <= 1000); | ||
160 | data->freq.e = 1; | ||
161 | data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000; | ||
162 | data->freq.flags = 1; | ||
163 | |||
164 | err = 0; | ||
165 | out_unlock: | ||
166 | bcm43xx_unlock(bcm, flags); | ||
167 | |||
168 | return err; | ||
169 | } | ||
170 | |||
171 | static int bcm43xx_wx_set_mode(struct net_device *net_dev, | ||
172 | struct iw_request_info *info, | ||
173 | union iwreq_data *data, | ||
174 | char *extra) | ||
175 | { | ||
176 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
177 | unsigned long flags; | ||
178 | int mode; | ||
179 | |||
180 | mode = data->mode; | ||
181 | if (mode == IW_MODE_AUTO) | ||
182 | mode = BCM43xx_INITIAL_IWMODE; | ||
183 | |||
184 | bcm43xx_lock_mmio(bcm, flags); | ||
185 | if (bcm->ieee->iw_mode != mode) | ||
186 | bcm43xx_set_iwmode(bcm, mode); | ||
187 | bcm43xx_unlock_mmio(bcm, flags); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int bcm43xx_wx_get_mode(struct net_device *net_dev, | ||
193 | struct iw_request_info *info, | ||
194 | union iwreq_data *data, | ||
195 | char *extra) | ||
196 | { | ||
197 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
198 | unsigned long flags; | ||
199 | |||
200 | bcm43xx_lock(bcm, flags); | ||
201 | data->mode = bcm->ieee->iw_mode; | ||
202 | bcm43xx_unlock(bcm, flags); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, | ||
208 | struct iw_request_info *info, | ||
209 | union iwreq_data *data, | ||
210 | char *extra) | ||
211 | { | ||
212 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
213 | struct iw_range *range = (struct iw_range *)extra; | ||
214 | const struct ieee80211_geo *geo; | ||
215 | unsigned long flags; | ||
216 | int i, j; | ||
217 | struct bcm43xx_phyinfo *phy; | ||
218 | |||
219 | data->data.length = sizeof(*range); | ||
220 | memset(range, 0, sizeof(*range)); | ||
221 | |||
222 | //TODO: What about 802.11b? | ||
223 | /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */ | ||
224 | range->throughput = 27 * 1000 * 1000; | ||
225 | |||
226 | range->max_qual.qual = 100; | ||
227 | /* TODO: Real max RSSI */ | ||
228 | range->max_qual.level = 3; | ||
229 | range->max_qual.noise = 100; | ||
230 | range->max_qual.updated = 7; | ||
231 | |||
232 | range->avg_qual.qual = 70; | ||
233 | range->avg_qual.level = 2; | ||
234 | range->avg_qual.noise = 40; | ||
235 | range->avg_qual.updated = 7; | ||
236 | |||
237 | range->min_rts = BCM43xx_MIN_RTS_THRESHOLD; | ||
238 | range->max_rts = BCM43xx_MAX_RTS_THRESHOLD; | ||
239 | range->min_frag = MIN_FRAG_THRESHOLD; | ||
240 | range->max_frag = MAX_FRAG_THRESHOLD; | ||
241 | |||
242 | range->encoding_size[0] = 5; | ||
243 | range->encoding_size[1] = 13; | ||
244 | range->num_encoding_sizes = 2; | ||
245 | range->max_encoding_tokens = WEP_KEYS; | ||
246 | |||
247 | range->we_version_compiled = WIRELESS_EXT; | ||
248 | range->we_version_source = BCM43xx_WX_VERSION; | ||
249 | |||
250 | range->enc_capa = IW_ENC_CAPA_WPA | | ||
251 | IW_ENC_CAPA_WPA2 | | ||
252 | IW_ENC_CAPA_CIPHER_TKIP | | ||
253 | IW_ENC_CAPA_CIPHER_CCMP; | ||
254 | |||
255 | bcm43xx_lock(bcm, flags); | ||
256 | phy = bcm43xx_current_phy(bcm); | ||
257 | |||
258 | range->num_bitrates = 0; | ||
259 | i = 0; | ||
260 | if (phy->type == BCM43xx_PHYTYPE_A || | ||
261 | phy->type == BCM43xx_PHYTYPE_G) { | ||
262 | range->num_bitrates = 8; | ||
263 | range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB; | ||
264 | range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB; | ||
265 | range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB; | ||
266 | range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB; | ||
267 | range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB; | ||
268 | range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB; | ||
269 | range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB; | ||
270 | range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB; | ||
271 | } | ||
272 | if (phy->type == BCM43xx_PHYTYPE_B || | ||
273 | phy->type == BCM43xx_PHYTYPE_G) { | ||
274 | range->num_bitrates += 4; | ||
275 | range->bitrate[i++] = IEEE80211_CCK_RATE_1MB; | ||
276 | range->bitrate[i++] = IEEE80211_CCK_RATE_2MB; | ||
277 | range->bitrate[i++] = IEEE80211_CCK_RATE_5MB; | ||
278 | range->bitrate[i++] = IEEE80211_CCK_RATE_11MB; | ||
279 | } | ||
280 | |||
281 | geo = ieee80211_get_geo(bcm->ieee); | ||
282 | range->num_channels = geo->a_channels + geo->bg_channels; | ||
283 | j = 0; | ||
284 | for (i = 0; i < geo->a_channels; i++) { | ||
285 | if (j == IW_MAX_FREQUENCIES) | ||
286 | break; | ||
287 | range->freq[j].i = j + 1; | ||
288 | range->freq[j].m = geo->a[i].freq;//FIXME? | ||
289 | range->freq[j].e = 1; | ||
290 | j++; | ||
291 | } | ||
292 | for (i = 0; i < geo->bg_channels; i++) { | ||
293 | if (j == IW_MAX_FREQUENCIES) | ||
294 | break; | ||
295 | range->freq[j].i = j + 1; | ||
296 | range->freq[j].m = geo->bg[i].freq;//FIXME? | ||
297 | range->freq[j].e = 1; | ||
298 | j++; | ||
299 | } | ||
300 | range->num_frequency = j; | ||
301 | |||
302 | bcm43xx_unlock(bcm, flags); | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static int bcm43xx_wx_set_nick(struct net_device *net_dev, | ||
308 | struct iw_request_info *info, | ||
309 | union iwreq_data *data, | ||
310 | char *extra) | ||
311 | { | ||
312 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
313 | unsigned long flags; | ||
314 | size_t len; | ||
315 | |||
316 | bcm43xx_lock(bcm, flags); | ||
317 | len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE); | ||
318 | memcpy(bcm->nick, extra, len); | ||
319 | bcm->nick[len] = '\0'; | ||
320 | bcm43xx_unlock(bcm, flags); | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int bcm43xx_wx_get_nick(struct net_device *net_dev, | ||
326 | struct iw_request_info *info, | ||
327 | union iwreq_data *data, | ||
328 | char *extra) | ||
329 | { | ||
330 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
331 | unsigned long flags; | ||
332 | size_t len; | ||
333 | |||
334 | bcm43xx_lock(bcm, flags); | ||
335 | len = strlen(bcm->nick) + 1; | ||
336 | memcpy(extra, bcm->nick, len); | ||
337 | data->data.length = (__u16)len; | ||
338 | data->data.flags = 1; | ||
339 | bcm43xx_unlock(bcm, flags); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int bcm43xx_wx_set_rts(struct net_device *net_dev, | ||
345 | struct iw_request_info *info, | ||
346 | union iwreq_data *data, | ||
347 | char *extra) | ||
348 | { | ||
349 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
350 | unsigned long flags; | ||
351 | int err = -EINVAL; | ||
352 | |||
353 | bcm43xx_lock(bcm, flags); | ||
354 | if (data->rts.disabled) { | ||
355 | bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD; | ||
356 | err = 0; | ||
357 | } else { | ||
358 | if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD && | ||
359 | data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) { | ||
360 | bcm->rts_threshold = data->rts.value; | ||
361 | err = 0; | ||
362 | } | ||
363 | } | ||
364 | bcm43xx_unlock(bcm, flags); | ||
365 | |||
366 | return err; | ||
367 | } | ||
368 | |||
369 | static int bcm43xx_wx_get_rts(struct net_device *net_dev, | ||
370 | struct iw_request_info *info, | ||
371 | union iwreq_data *data, | ||
372 | char *extra) | ||
373 | { | ||
374 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
375 | unsigned long flags; | ||
376 | |||
377 | bcm43xx_lock(bcm, flags); | ||
378 | data->rts.value = bcm->rts_threshold; | ||
379 | data->rts.fixed = 0; | ||
380 | data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD); | ||
381 | bcm43xx_unlock(bcm, flags); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static int bcm43xx_wx_set_frag(struct net_device *net_dev, | ||
387 | struct iw_request_info *info, | ||
388 | union iwreq_data *data, | ||
389 | char *extra) | ||
390 | { | ||
391 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
392 | unsigned long flags; | ||
393 | int err = -EINVAL; | ||
394 | |||
395 | bcm43xx_lock(bcm, flags); | ||
396 | if (data->frag.disabled) { | ||
397 | bcm->ieee->fts = MAX_FRAG_THRESHOLD; | ||
398 | err = 0; | ||
399 | } else { | ||
400 | if (data->frag.value >= MIN_FRAG_THRESHOLD && | ||
401 | data->frag.value <= MAX_FRAG_THRESHOLD) { | ||
402 | bcm->ieee->fts = data->frag.value & ~0x1; | ||
403 | err = 0; | ||
404 | } | ||
405 | } | ||
406 | bcm43xx_unlock(bcm, flags); | ||
407 | |||
408 | return err; | ||
409 | } | ||
410 | |||
411 | static int bcm43xx_wx_get_frag(struct net_device *net_dev, | ||
412 | struct iw_request_info *info, | ||
413 | union iwreq_data *data, | ||
414 | char *extra) | ||
415 | { | ||
416 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
417 | unsigned long flags; | ||
418 | |||
419 | bcm43xx_lock(bcm, flags); | ||
420 | data->frag.value = bcm->ieee->fts; | ||
421 | data->frag.fixed = 0; | ||
422 | data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD); | ||
423 | bcm43xx_unlock(bcm, flags); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, | ||
429 | struct iw_request_info *info, | ||
430 | union iwreq_data *data, | ||
431 | char *extra) | ||
432 | { | ||
433 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
434 | struct bcm43xx_radioinfo *radio; | ||
435 | struct bcm43xx_phyinfo *phy; | ||
436 | unsigned long flags; | ||
437 | int err = -ENODEV; | ||
438 | u16 maxpower; | ||
439 | |||
440 | if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) { | ||
441 | printk(PFX KERN_ERR "TX power not in dBm.\n"); | ||
442 | return -EOPNOTSUPP; | ||
443 | } | ||
444 | |||
445 | bcm43xx_lock_mmio(bcm, flags); | ||
446 | if (!bcm->initialized) | ||
447 | goto out_unlock; | ||
448 | radio = bcm43xx_current_radio(bcm); | ||
449 | phy = bcm43xx_current_phy(bcm); | ||
450 | if (data->txpower.disabled != (!(radio->enabled))) { | ||
451 | if (data->txpower.disabled) | ||
452 | bcm43xx_radio_turn_off(bcm); | ||
453 | else | ||
454 | bcm43xx_radio_turn_on(bcm); | ||
455 | } | ||
456 | if (data->txpower.value > 0) { | ||
457 | /* desired and maxpower dBm values are in Q5.2 */ | ||
458 | if (phy->type == BCM43xx_PHYTYPE_A) | ||
459 | maxpower = bcm->sprom.maxpower_aphy; | ||
460 | else | ||
461 | maxpower = bcm->sprom.maxpower_bgphy; | ||
462 | radio->txpower_desired = limit_value(data->txpower.value << 2, | ||
463 | 0, maxpower); | ||
464 | bcm43xx_phy_xmitpower(bcm); | ||
465 | } | ||
466 | err = 0; | ||
467 | |||
468 | out_unlock: | ||
469 | bcm43xx_unlock_mmio(bcm, flags); | ||
470 | |||
471 | return err; | ||
472 | } | ||
473 | |||
474 | static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, | ||
475 | struct iw_request_info *info, | ||
476 | union iwreq_data *data, | ||
477 | char *extra) | ||
478 | { | ||
479 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
480 | struct bcm43xx_radioinfo *radio; | ||
481 | unsigned long flags; | ||
482 | int err = -ENODEV; | ||
483 | |||
484 | bcm43xx_lock(bcm, flags); | ||
485 | if (!bcm->initialized) | ||
486 | goto out_unlock; | ||
487 | radio = bcm43xx_current_radio(bcm); | ||
488 | /* desired dBm value is in Q5.2 */ | ||
489 | data->txpower.value = radio->txpower_desired >> 2; | ||
490 | data->txpower.fixed = 1; | ||
491 | data->txpower.flags = IW_TXPOW_DBM; | ||
492 | data->txpower.disabled = !(radio->enabled); | ||
493 | |||
494 | err = 0; | ||
495 | out_unlock: | ||
496 | bcm43xx_unlock(bcm, flags); | ||
497 | |||
498 | return err; | ||
499 | } | ||
500 | |||
501 | static int bcm43xx_wx_set_encoding(struct net_device *net_dev, | ||
502 | struct iw_request_info *info, | ||
503 | union iwreq_data *data, | ||
504 | char *extra) | ||
505 | { | ||
506 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
507 | int err; | ||
508 | |||
509 | err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra); | ||
510 | |||
511 | return err; | ||
512 | } | ||
513 | |||
514 | static int bcm43xx_wx_set_encodingext(struct net_device *net_dev, | ||
515 | struct iw_request_info *info, | ||
516 | union iwreq_data *data, | ||
517 | char *extra) | ||
518 | { | ||
519 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
520 | int err; | ||
521 | |||
522 | err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra); | ||
523 | |||
524 | return err; | ||
525 | } | ||
526 | |||
527 | static int bcm43xx_wx_get_encoding(struct net_device *net_dev, | ||
528 | struct iw_request_info *info, | ||
529 | union iwreq_data *data, | ||
530 | char *extra) | ||
531 | { | ||
532 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
533 | int err; | ||
534 | |||
535 | err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra); | ||
536 | |||
537 | return err; | ||
538 | } | ||
539 | |||
540 | static int bcm43xx_wx_get_encodingext(struct net_device *net_dev, | ||
541 | struct iw_request_info *info, | ||
542 | union iwreq_data *data, | ||
543 | char *extra) | ||
544 | { | ||
545 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
546 | int err; | ||
547 | |||
548 | err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra); | ||
549 | |||
550 | return err; | ||
551 | } | ||
552 | |||
553 | static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, | ||
554 | struct iw_request_info *info, | ||
555 | union iwreq_data *data, | ||
556 | char *extra) | ||
557 | { | ||
558 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
559 | unsigned long flags; | ||
560 | int mode, err = 0; | ||
561 | |||
562 | mode = *((int *)extra); | ||
563 | switch (mode) { | ||
564 | case 0: | ||
565 | mode = BCM43xx_RADIO_INTERFMODE_NONE; | ||
566 | break; | ||
567 | case 1: | ||
568 | mode = BCM43xx_RADIO_INTERFMODE_NONWLAN; | ||
569 | break; | ||
570 | case 2: | ||
571 | mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; | ||
572 | break; | ||
573 | case 3: | ||
574 | mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN; | ||
575 | break; | ||
576 | default: | ||
577 | printk(KERN_ERR PFX "set_interfmode allowed parameters are: " | ||
578 | "0 => None, 1 => Non-WLAN, 2 => WLAN, " | ||
579 | "3 => Auto-WLAN\n"); | ||
580 | return -EINVAL; | ||
581 | } | ||
582 | |||
583 | bcm43xx_lock_mmio(bcm, flags); | ||
584 | if (bcm->initialized) { | ||
585 | err = bcm43xx_radio_set_interference_mitigation(bcm, mode); | ||
586 | if (err) { | ||
587 | printk(KERN_ERR PFX "Interference Mitigation not " | ||
588 | "supported by device\n"); | ||
589 | } | ||
590 | } else { | ||
591 | if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) { | ||
592 | printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN " | ||
593 | "not supported while the interface is down.\n"); | ||
594 | err = -ENODEV; | ||
595 | } else | ||
596 | bcm43xx_current_radio(bcm)->interfmode = mode; | ||
597 | } | ||
598 | bcm43xx_unlock_mmio(bcm, flags); | ||
599 | |||
600 | return err; | ||
601 | } | ||
602 | |||
603 | static int bcm43xx_wx_get_interfmode(struct net_device *net_dev, | ||
604 | struct iw_request_info *info, | ||
605 | union iwreq_data *data, | ||
606 | char *extra) | ||
607 | { | ||
608 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
609 | unsigned long flags; | ||
610 | int mode; | ||
611 | |||
612 | bcm43xx_lock(bcm, flags); | ||
613 | mode = bcm43xx_current_radio(bcm)->interfmode; | ||
614 | bcm43xx_unlock(bcm, flags); | ||
615 | |||
616 | switch (mode) { | ||
617 | case BCM43xx_RADIO_INTERFMODE_NONE: | ||
618 | strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING); | ||
619 | break; | ||
620 | case BCM43xx_RADIO_INTERFMODE_NONWLAN: | ||
621 | strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING); | ||
622 | break; | ||
623 | case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: | ||
624 | strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING); | ||
625 | break; | ||
626 | default: | ||
627 | assert(0); | ||
628 | } | ||
629 | data->data.length = strlen(extra) + 1; | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev, | ||
635 | struct iw_request_info *info, | ||
636 | union iwreq_data *data, | ||
637 | char *extra) | ||
638 | { | ||
639 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
640 | unsigned long flags; | ||
641 | int on; | ||
642 | |||
643 | on = *((int *)extra); | ||
644 | bcm43xx_lock(bcm, flags); | ||
645 | bcm->short_preamble = !!on; | ||
646 | bcm43xx_unlock(bcm, flags); | ||
647 | |||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev, | ||
652 | struct iw_request_info *info, | ||
653 | union iwreq_data *data, | ||
654 | char *extra) | ||
655 | { | ||
656 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
657 | unsigned long flags; | ||
658 | int on; | ||
659 | |||
660 | bcm43xx_lock(bcm, flags); | ||
661 | on = bcm->short_preamble; | ||
662 | bcm43xx_unlock(bcm, flags); | ||
663 | |||
664 | if (on) | ||
665 | strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING); | ||
666 | else | ||
667 | strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING); | ||
668 | data->data.length = strlen(extra) + 1; | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int bcm43xx_wx_set_swencryption(struct net_device *net_dev, | ||
674 | struct iw_request_info *info, | ||
675 | union iwreq_data *data, | ||
676 | char *extra) | ||
677 | { | ||
678 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
679 | unsigned long flags; | ||
680 | int on; | ||
681 | |||
682 | on = *((int *)extra); | ||
683 | |||
684 | bcm43xx_lock(bcm, flags); | ||
685 | bcm->ieee->host_encrypt = !!on; | ||
686 | bcm->ieee->host_decrypt = !!on; | ||
687 | bcm->ieee->host_build_iv = !on; | ||
688 | bcm43xx_unlock(bcm, flags); | ||
689 | |||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | static int bcm43xx_wx_get_swencryption(struct net_device *net_dev, | ||
694 | struct iw_request_info *info, | ||
695 | union iwreq_data *data, | ||
696 | char *extra) | ||
697 | { | ||
698 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
699 | unsigned long flags; | ||
700 | int on; | ||
701 | |||
702 | bcm43xx_lock(bcm, flags); | ||
703 | on = bcm->ieee->host_encrypt; | ||
704 | bcm43xx_unlock(bcm, flags); | ||
705 | |||
706 | if (on) | ||
707 | strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING); | ||
708 | else | ||
709 | strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING); | ||
710 | data->data.length = strlen(extra + 1); | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | /* Enough buffer to hold a hexdump of the sprom data. */ | ||
716 | #define SPROM_BUFFERSIZE 512 | ||
717 | |||
718 | static int sprom2hex(const u16 *sprom, char *dump) | ||
719 | { | ||
720 | int i, pos = 0; | ||
721 | |||
722 | for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { | ||
723 | pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1, | ||
724 | "%04X", swab16(sprom[i]) & 0xFFFF); | ||
725 | } | ||
726 | |||
727 | return pos + 1; | ||
728 | } | ||
729 | |||
730 | static int hex2sprom(u16 *sprom, const char *dump, unsigned int len) | ||
731 | { | ||
732 | char tmp[5] = { 0 }; | ||
733 | int cnt = 0; | ||
734 | unsigned long parsed; | ||
735 | |||
736 | if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2) | ||
737 | return -EINVAL; | ||
738 | while (cnt < BCM43xx_SPROM_SIZE) { | ||
739 | memcpy(tmp, dump, 4); | ||
740 | dump += 4; | ||
741 | parsed = simple_strtoul(tmp, NULL, 16); | ||
742 | sprom[cnt++] = swab16((u16)parsed); | ||
743 | } | ||
744 | |||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int bcm43xx_wx_sprom_read(struct net_device *net_dev, | ||
749 | struct iw_request_info *info, | ||
750 | union iwreq_data *data, | ||
751 | char *extra) | ||
752 | { | ||
753 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
754 | int err = -EPERM; | ||
755 | u16 *sprom; | ||
756 | unsigned long flags; | ||
757 | |||
758 | if (!capable(CAP_SYS_RAWIO)) | ||
759 | goto out; | ||
760 | |||
761 | err = -ENOMEM; | ||
762 | sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), | ||
763 | GFP_KERNEL); | ||
764 | if (!sprom) | ||
765 | goto out; | ||
766 | |||
767 | bcm43xx_lock_mmio(bcm, flags); | ||
768 | err = -ENODEV; | ||
769 | if (bcm->initialized) | ||
770 | err = bcm43xx_sprom_read(bcm, sprom); | ||
771 | bcm43xx_unlock_mmio(bcm, flags); | ||
772 | if (!err) | ||
773 | data->data.length = sprom2hex(sprom, extra); | ||
774 | kfree(sprom); | ||
775 | out: | ||
776 | return err; | ||
777 | } | ||
778 | |||
779 | static int bcm43xx_wx_sprom_write(struct net_device *net_dev, | ||
780 | struct iw_request_info *info, | ||
781 | union iwreq_data *data, | ||
782 | char *extra) | ||
783 | { | ||
784 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
785 | int err = -EPERM; | ||
786 | u16 *sprom; | ||
787 | unsigned long flags; | ||
788 | char *input; | ||
789 | unsigned int len; | ||
790 | |||
791 | if (!capable(CAP_SYS_RAWIO)) | ||
792 | goto out; | ||
793 | |||
794 | err = -ENOMEM; | ||
795 | sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), | ||
796 | GFP_KERNEL); | ||
797 | if (!sprom) | ||
798 | goto out; | ||
799 | |||
800 | len = data->data.length; | ||
801 | extra[len - 1] = '\0'; | ||
802 | input = strchr(extra, ':'); | ||
803 | if (input) { | ||
804 | input++; | ||
805 | len -= input - extra; | ||
806 | } else | ||
807 | input = extra; | ||
808 | err = hex2sprom(sprom, input, len); | ||
809 | if (err) | ||
810 | goto out_kfree; | ||
811 | |||
812 | bcm43xx_lock_mmio(bcm, flags); | ||
813 | err = -ENODEV; | ||
814 | if (bcm->initialized) | ||
815 | err = bcm43xx_sprom_write(bcm, sprom); | ||
816 | bcm43xx_unlock_mmio(bcm, flags); | ||
817 | out_kfree: | ||
818 | kfree(sprom); | ||
819 | out: | ||
820 | return err; | ||
821 | } | ||
822 | |||
823 | /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ | ||
824 | |||
825 | static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev) | ||
826 | { | ||
827 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | ||
828 | struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | ||
829 | struct iw_statistics *wstats; | ||
830 | |||
831 | wstats = &bcm->stats.wstats; | ||
832 | if (!mac->associated) { | ||
833 | wstats->miss.beacon = 0; | ||
834 | // bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here? | ||
835 | wstats->discard.retries = 0; | ||
836 | // bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question | ||
837 | wstats->discard.nwid = 0; | ||
838 | // bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto | ||
839 | wstats->discard.code = 0; | ||
840 | // bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here | ||
841 | wstats->discard.fragment = 0; | ||
842 | wstats->discard.misc = 0; | ||
843 | wstats->qual.qual = 0; | ||
844 | wstats->qual.level = 0; | ||
845 | wstats->qual.noise = 0; | ||
846 | wstats->qual.updated = 7; | ||
847 | wstats->qual.updated |= IW_QUAL_NOISE_INVALID | | ||
848 | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; | ||
849 | return wstats; | ||
850 | } | ||
851 | /* fill in the real statistics when iface associated */ | ||
852 | wstats->qual.qual = 100; // TODO: get the real signal quality | ||
853 | wstats->qual.level = 3 - bcm->stats.link_quality; | ||
854 | wstats->qual.noise = bcm->stats.noise; | ||
855 | wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | | ||
856 | IW_QUAL_NOISE_UPDATED; | ||
857 | wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable; | ||
858 | wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded; | ||
859 | wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa; | ||
860 | wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments; | ||
861 | wstats->discard.misc = 0; // FIXME | ||
862 | wstats->miss.beacon = 0; // FIXME | ||
863 | return wstats; | ||
864 | } | ||
865 | |||
866 | |||
867 | #ifdef WX | ||
868 | # undef WX | ||
869 | #endif | ||
870 | #define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT] | ||
871 | static const iw_handler bcm43xx_wx_handlers[] = { | ||
872 | /* Wireless Identification */ | ||
873 | WX(SIOCGIWNAME) = bcm43xx_wx_get_name, | ||
874 | /* Basic operations */ | ||
875 | WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq, | ||
876 | WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq, | ||
877 | WX(SIOCSIWMODE) = bcm43xx_wx_set_mode, | ||
878 | WX(SIOCGIWMODE) = bcm43xx_wx_get_mode, | ||
879 | /* Informative stuff */ | ||
880 | WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams, | ||
881 | /* Access Point manipulation */ | ||
882 | WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, | ||
883 | WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, | ||
884 | WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, | ||
885 | WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, | ||
886 | /* 802.11 specific support */ | ||
887 | WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, | ||
888 | WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, | ||
889 | WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick, | ||
890 | WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick, | ||
891 | /* Other parameters */ | ||
892 | WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, | ||
893 | WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, | ||
894 | WX(SIOCSIWRTS) = bcm43xx_wx_set_rts, | ||
895 | WX(SIOCGIWRTS) = bcm43xx_wx_get_rts, | ||
896 | WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag, | ||
897 | WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag, | ||
898 | WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower, | ||
899 | WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower, | ||
900 | //TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry, | ||
901 | //TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry, | ||
902 | /* Encoding */ | ||
903 | WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding, | ||
904 | WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding, | ||
905 | WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext, | ||
906 | WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext, | ||
907 | /* Power saving */ | ||
908 | //TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power, | ||
909 | //TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power, | ||
910 | WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, | ||
911 | WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, | ||
912 | WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, | ||
913 | WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, | ||
914 | }; | ||
915 | #undef WX | ||
916 | |||
917 | static const iw_handler bcm43xx_priv_wx_handlers[] = { | ||
918 | /* Set Interference Mitigation Mode. */ | ||
919 | bcm43xx_wx_set_interfmode, | ||
920 | /* Get Interference Mitigation Mode. */ | ||
921 | bcm43xx_wx_get_interfmode, | ||
922 | /* Enable/Disable Short Preamble mode. */ | ||
923 | bcm43xx_wx_set_shortpreamble, | ||
924 | /* Get Short Preamble mode. */ | ||
925 | bcm43xx_wx_get_shortpreamble, | ||
926 | /* Enable/Disable Software Encryption mode */ | ||
927 | bcm43xx_wx_set_swencryption, | ||
928 | /* Get Software Encryption mode */ | ||
929 | bcm43xx_wx_get_swencryption, | ||
930 | /* Write SRPROM data. */ | ||
931 | bcm43xx_wx_sprom_write, | ||
932 | /* Read SPROM data. */ | ||
933 | bcm43xx_wx_sprom_read, | ||
934 | }; | ||
935 | |||
936 | #define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0) | ||
937 | #define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1) | ||
938 | #define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2) | ||
939 | #define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3) | ||
940 | #define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4) | ||
941 | #define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5) | ||
942 | #define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6) | ||
943 | #define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7) | ||
944 | |||
945 | #define PRIV_WX_DUMMY(ioctl) \ | ||
946 | { \ | ||
947 | .cmd = (ioctl), \ | ||
948 | .name = "__unused" \ | ||
949 | } | ||
950 | |||
951 | static const struct iw_priv_args bcm43xx_priv_wx_args[] = { | ||
952 | { | ||
953 | .cmd = PRIV_WX_SET_INTERFMODE, | ||
954 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
955 | .name = "set_interfmode", | ||
956 | }, | ||
957 | { | ||
958 | .cmd = PRIV_WX_GET_INTERFMODE, | ||
959 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | ||
960 | .name = "get_interfmode", | ||
961 | }, | ||
962 | { | ||
963 | .cmd = PRIV_WX_SET_SHORTPREAMBLE, | ||
964 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
965 | .name = "set_shortpreambl", | ||
966 | }, | ||
967 | { | ||
968 | .cmd = PRIV_WX_GET_SHORTPREAMBLE, | ||
969 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | ||
970 | .name = "get_shortpreambl", | ||
971 | }, | ||
972 | { | ||
973 | .cmd = PRIV_WX_SET_SWENCRYPTION, | ||
974 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
975 | .name = "set_swencryption", | ||
976 | }, | ||
977 | { | ||
978 | .cmd = PRIV_WX_GET_SWENCRYPTION, | ||
979 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | ||
980 | .name = "get_swencryption", | ||
981 | }, | ||
982 | { | ||
983 | .cmd = PRIV_WX_SPROM_WRITE, | ||
984 | .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE, | ||
985 | .name = "write_sprom", | ||
986 | }, | ||
987 | { | ||
988 | .cmd = PRIV_WX_SPROM_READ, | ||
989 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE, | ||
990 | .name = "read_sprom", | ||
991 | }, | ||
992 | }; | ||
993 | |||
994 | const struct iw_handler_def bcm43xx_wx_handlers_def = { | ||
995 | .standard = bcm43xx_wx_handlers, | ||
996 | .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers), | ||
997 | .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers), | ||
998 | .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args), | ||
999 | .private = bcm43xx_priv_wx_handlers, | ||
1000 | .private_args = bcm43xx_priv_wx_args, | ||
1001 | .get_wireless_stats = bcm43xx_get_wireless_stats, | ||
1002 | }; | ||