]>
Commit | Line | Data |
---|---|---|
f222313a JL |
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 | ||
8fa252d0 MB |
44 | |
45 | /* The WIRELESS_EXT version, which is implemented by this driver. */ | |
46 | #define BCM43xx_WX_VERSION 18 | |
47 | ||
48 | ||
f222313a JL |
49 | /* Define to enable a printk on each wx handler function invocation */ |
50 | //#define BCM43xx_WX_DEBUG | |
51 | ||
52 | ||
53 | #ifdef BCM43xx_WX_DEBUG | |
54 | # define printk_wx printk | |
55 | #else | |
56 | # define printk_wx(x...) do { /* nothing */ } while (0) | |
57 | #endif | |
58 | #define wx_enter() printk_wx(KERN_INFO PFX "WX handler called: %s\n", __FUNCTION__); | |
59 | ||
60 | #define MAX_WX_STRING 80 | |
61 | ||
62 | ||
63 | static int bcm43xx_wx_get_name(struct net_device *net_dev, | |
64 | struct iw_request_info *info, | |
65 | union iwreq_data *data, | |
66 | char *extra) | |
67 | { | |
68 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
69 | unsigned long flags; | |
70 | int i, nr_80211; | |
71 | struct bcm43xx_phyinfo *phy; | |
72 | char suffix[7] = { 0 }; | |
73 | int have_a = 0, have_b = 0, have_g = 0; | |
74 | ||
75 | wx_enter(); | |
76 | ||
77 | spin_lock_irqsave(&bcm->lock, flags); | |
78 | nr_80211 = bcm43xx_num_80211_cores(bcm); | |
79 | for (i = 0; i < nr_80211; i++) { | |
80 | phy = bcm->phy + i; | |
81 | switch (phy->type) { | |
82 | case BCM43xx_PHYTYPE_A: | |
83 | have_a = 1; | |
84 | break; | |
85 | case BCM43xx_PHYTYPE_G: | |
86 | have_g = 1; | |
87 | case BCM43xx_PHYTYPE_B: | |
88 | have_b = 1; | |
89 | break; | |
90 | default: | |
91 | assert(0); | |
92 | } | |
93 | } | |
94 | spin_unlock_irqrestore(&bcm->lock, flags); | |
95 | ||
96 | i = 0; | |
97 | if (have_a) { | |
98 | suffix[i++] = 'a'; | |
99 | suffix[i++] = '/'; | |
100 | } | |
101 | if (have_b) { | |
102 | suffix[i++] = 'b'; | |
103 | suffix[i++] = '/'; | |
104 | } | |
105 | if (have_g) { | |
106 | suffix[i++] = 'g'; | |
107 | suffix[i++] = '/'; | |
108 | } | |
109 | if (i != 0) | |
110 | suffix[i - 1] = '\0'; | |
111 | ||
112 | snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, | |
118 | struct iw_request_info *info, | |
119 | union iwreq_data *data, | |
120 | char *extra) | |
121 | { | |
122 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
123 | struct ieee80211softmac_device *softmac = bcm->softmac; | |
124 | unsigned long flags; | |
125 | u8 channel; | |
126 | int freq; | |
127 | int err = 0; | |
128 | ||
129 | wx_enter(); | |
130 | ||
131 | if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { | |
132 | channel = data->freq.m; | |
133 | freq = bcm43xx_channel_to_freq(bcm, channel); | |
134 | } else { | |
135 | channel = bcm43xx_freq_to_channel(bcm, data->freq.m); | |
136 | freq = data->freq.m; | |
137 | } | |
138 | if (!bcm43xx_is_valid_channel(bcm, channel)) | |
139 | return -EINVAL; | |
140 | ||
141 | spin_lock_irqsave(&bcm->lock, flags); | |
142 | if (bcm->initialized) { | |
143 | //ieee80211softmac_disassoc(softmac, $REASON); | |
144 | bcm43xx_mac_suspend(bcm); | |
145 | err = bcm43xx_radio_selectchannel(bcm, channel, 0); | |
146 | bcm43xx_mac_enable(bcm); | |
147 | } else | |
148 | bcm->current_core->radio->initial_channel = channel; | |
149 | spin_unlock_irqrestore(&bcm->lock, flags); | |
150 | if (!err) | |
151 | printk_wx(KERN_INFO PFX "Selected channel: %d\n", channel); | |
152 | ||
153 | return err; | |
154 | } | |
155 | ||
156 | static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, | |
157 | struct iw_request_info *info, | |
158 | union iwreq_data *data, | |
159 | char *extra) | |
160 | { | |
161 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
162 | unsigned long flags; | |
163 | int err = -ENODEV; | |
164 | u16 channel; | |
165 | ||
166 | wx_enter(); | |
167 | ||
168 | spin_lock_irqsave(&bcm->lock, flags); | |
169 | channel = bcm->current_core->radio->channel; | |
170 | if (channel == 0xFF) { | |
171 | assert(!bcm->initialized); | |
172 | channel = bcm->current_core->radio->initial_channel; | |
173 | if (channel == 0xFF) | |
174 | goto out_unlock; | |
175 | } | |
176 | assert(channel > 0 && channel <= 1000); | |
177 | data->freq.e = 1; | |
178 | data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000; | |
179 | data->freq.flags = 1; | |
180 | ||
181 | err = 0; | |
182 | out_unlock: | |
183 | spin_unlock_irqrestore(&bcm->lock, flags); | |
184 | ||
185 | return err; | |
186 | } | |
187 | ||
188 | static int bcm43xx_wx_set_mode(struct net_device *net_dev, | |
189 | struct iw_request_info *info, | |
190 | union iwreq_data *data, | |
191 | char *extra) | |
192 | { | |
193 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
194 | unsigned long flags; | |
195 | int mode; | |
196 | ||
197 | wx_enter(); | |
198 | ||
199 | mode = data->mode; | |
200 | if (mode == IW_MODE_AUTO) | |
201 | mode = BCM43xx_INITIAL_IWMODE; | |
202 | ||
203 | spin_lock_irqsave(&bcm->lock, flags); | |
204 | if (bcm->ieee->iw_mode != mode) | |
205 | bcm43xx_set_iwmode(bcm, mode); | |
206 | spin_unlock_irqrestore(&bcm->lock, flags); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static int bcm43xx_wx_get_mode(struct net_device *net_dev, | |
212 | struct iw_request_info *info, | |
213 | union iwreq_data *data, | |
214 | char *extra) | |
215 | { | |
216 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
217 | unsigned long flags; | |
218 | ||
219 | wx_enter(); | |
220 | ||
221 | spin_lock_irqsave(&bcm->lock, flags); | |
222 | data->mode = bcm->ieee->iw_mode; | |
223 | spin_unlock_irqrestore(&bcm->lock, flags); | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
228 | static int bcm43xx_wx_set_sensitivity(struct net_device *net_dev, | |
229 | struct iw_request_info *info, | |
230 | union iwreq_data *data, | |
231 | char *extra) | |
232 | { | |
233 | wx_enter(); | |
234 | /*TODO*/ | |
235 | return 0; | |
236 | } | |
237 | ||
238 | static int bcm43xx_wx_get_sensitivity(struct net_device *net_dev, | |
239 | struct iw_request_info *info, | |
240 | union iwreq_data *data, | |
241 | char *extra) | |
242 | { | |
243 | wx_enter(); | |
244 | /*TODO*/ | |
245 | return 0; | |
246 | } | |
247 | ||
248 | static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, | |
249 | struct iw_request_info *info, | |
250 | union iwreq_data *data, | |
251 | char *extra) | |
252 | { | |
253 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
254 | struct iw_range *range = (struct iw_range *)extra; | |
255 | const struct ieee80211_geo *geo; | |
256 | unsigned long flags; | |
257 | int i, j; | |
258 | ||
259 | wx_enter(); | |
260 | ||
261 | data->data.length = sizeof(*range); | |
262 | memset(range, 0, sizeof(*range)); | |
263 | ||
264 | //TODO: What about 802.11b? | |
265 | /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */ | |
266 | range->throughput = 27 * 1000 * 1000; | |
267 | ||
268 | range->max_qual.qual = 100; | |
269 | /* TODO: Real max RSSI */ | |
270 | range->max_qual.level = 0; | |
271 | range->max_qual.noise = 0; | |
272 | range->max_qual.updated = 7; | |
273 | ||
274 | range->avg_qual.qual = 70; | |
275 | range->avg_qual.level = 0; | |
276 | range->avg_qual.noise = 0; | |
277 | range->avg_qual.updated = 7; | |
278 | ||
279 | range->min_rts = BCM43xx_MIN_RTS_THRESHOLD; | |
280 | range->max_rts = BCM43xx_MAX_RTS_THRESHOLD; | |
281 | range->min_frag = MIN_FRAG_THRESHOLD; | |
282 | range->max_frag = MAX_FRAG_THRESHOLD; | |
283 | ||
284 | range->encoding_size[0] = 5; | |
285 | range->encoding_size[1] = 13; | |
286 | range->num_encoding_sizes = 2; | |
287 | range->max_encoding_tokens = WEP_KEYS; | |
288 | ||
289 | range->we_version_compiled = WIRELESS_EXT; | |
8fa252d0 MB |
290 | range->we_version_source = BCM43xx_WX_VERSION; |
291 | ||
292 | range->enc_capa = IW_ENC_CAPA_WPA | | |
293 | IW_ENC_CAPA_WPA2 | | |
294 | IW_ENC_CAPA_CIPHER_TKIP | | |
295 | IW_ENC_CAPA_CIPHER_CCMP; | |
f222313a JL |
296 | |
297 | spin_lock_irqsave(&bcm->lock, flags); | |
298 | ||
299 | range->num_bitrates = 0; | |
300 | i = 0; | |
301 | if (bcm->current_core->phy->type == BCM43xx_PHYTYPE_A || | |
302 | bcm->current_core->phy->type == BCM43xx_PHYTYPE_G) { | |
303 | range->num_bitrates = 8; | |
304 | range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB; | |
305 | range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB; | |
306 | range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB; | |
307 | range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB; | |
308 | range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB; | |
309 | range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB; | |
310 | range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB; | |
311 | range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB; | |
312 | } | |
313 | if (bcm->current_core->phy->type == BCM43xx_PHYTYPE_B || | |
314 | bcm->current_core->phy->type == BCM43xx_PHYTYPE_G) { | |
315 | range->num_bitrates += 4; | |
316 | range->bitrate[i++] = IEEE80211_CCK_RATE_1MB; | |
317 | range->bitrate[i++] = IEEE80211_CCK_RATE_2MB; | |
318 | range->bitrate[i++] = IEEE80211_CCK_RATE_5MB; | |
319 | range->bitrate[i++] = IEEE80211_CCK_RATE_11MB; | |
320 | } | |
321 | ||
322 | geo = ieee80211_get_geo(bcm->ieee); | |
323 | range->num_channels = geo->a_channels + geo->bg_channels; | |
324 | j = 0; | |
325 | for (i = 0; i < geo->a_channels; i++) { | |
326 | if (j == IW_MAX_FREQUENCIES) | |
327 | break; | |
328 | range->freq[j].i = j + 1; | |
329 | range->freq[j].m = geo->a[i].freq;//FIXME? | |
330 | range->freq[j].e = 1; | |
331 | j++; | |
332 | } | |
333 | for (i = 0; i < geo->bg_channels; i++) { | |
334 | if (j == IW_MAX_FREQUENCIES) | |
335 | break; | |
336 | range->freq[j].i = j + 1; | |
337 | range->freq[j].m = geo->bg[i].freq;//FIXME? | |
338 | range->freq[j].e = 1; | |
339 | j++; | |
340 | } | |
341 | range->num_frequency = j; | |
342 | ||
343 | spin_unlock_irqrestore(&bcm->lock, flags); | |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | static int bcm43xx_wx_set_nick(struct net_device *net_dev, | |
349 | struct iw_request_info *info, | |
350 | union iwreq_data *data, | |
351 | char *extra) | |
352 | { | |
353 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
354 | unsigned long flags; | |
355 | size_t len; | |
356 | ||
357 | wx_enter(); | |
358 | ||
359 | spin_lock_irqsave(&bcm->lock, flags); | |
360 | len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE); | |
361 | memcpy(bcm->nick, extra, len); | |
362 | bcm->nick[len] = '\0'; | |
363 | spin_unlock_irqrestore(&bcm->lock, flags); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | static int bcm43xx_wx_get_nick(struct net_device *net_dev, | |
369 | struct iw_request_info *info, | |
370 | union iwreq_data *data, | |
371 | char *extra) | |
372 | { | |
373 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
374 | unsigned long flags; | |
375 | size_t len; | |
376 | ||
377 | wx_enter(); | |
378 | ||
379 | spin_lock_irqsave(&bcm->lock, flags); | |
380 | len = strlen(bcm->nick) + 1; | |
381 | memcpy(extra, bcm->nick, len); | |
382 | data->data.length = (__u16)len; | |
383 | data->data.flags = 1; | |
384 | spin_unlock_irqrestore(&bcm->lock, flags); | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
389 | static int bcm43xx_wx_set_rts(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 | ||
398 | wx_enter(); | |
399 | ||
400 | spin_lock_irqsave(&bcm->lock, flags); | |
401 | if (data->rts.disabled) { | |
402 | bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD; | |
403 | err = 0; | |
404 | } else { | |
405 | if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD && | |
406 | data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) { | |
407 | bcm->rts_threshold = data->rts.value; | |
408 | err = 0; | |
409 | } | |
410 | } | |
411 | spin_unlock_irqrestore(&bcm->lock, flags); | |
412 | ||
413 | return err; | |
414 | } | |
415 | ||
416 | static int bcm43xx_wx_get_rts(struct net_device *net_dev, | |
417 | struct iw_request_info *info, | |
418 | union iwreq_data *data, | |
419 | char *extra) | |
420 | { | |
421 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
422 | unsigned long flags; | |
423 | ||
424 | wx_enter(); | |
425 | ||
426 | spin_lock_irqsave(&bcm->lock, flags); | |
427 | data->rts.value = bcm->rts_threshold; | |
428 | data->rts.fixed = 0; | |
429 | data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD); | |
430 | spin_unlock_irqrestore(&bcm->lock, flags); | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | static int bcm43xx_wx_set_frag(struct net_device *net_dev, | |
436 | struct iw_request_info *info, | |
437 | union iwreq_data *data, | |
438 | char *extra) | |
439 | { | |
440 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
441 | unsigned long flags; | |
442 | int err = -EINVAL; | |
443 | ||
444 | wx_enter(); | |
445 | ||
446 | spin_lock_irqsave(&bcm->lock, flags); | |
447 | if (data->frag.disabled) { | |
448 | bcm->ieee->fts = MAX_FRAG_THRESHOLD; | |
449 | err = 0; | |
450 | } else { | |
451 | if (data->frag.value >= MIN_FRAG_THRESHOLD && | |
452 | data->frag.value <= MAX_FRAG_THRESHOLD) { | |
453 | bcm->ieee->fts = data->frag.value & ~0x1; | |
454 | err = 0; | |
455 | } | |
456 | } | |
457 | spin_unlock_irqrestore(&bcm->lock, flags); | |
458 | ||
459 | return err; | |
460 | } | |
461 | ||
462 | static int bcm43xx_wx_get_frag(struct net_device *net_dev, | |
463 | struct iw_request_info *info, | |
464 | union iwreq_data *data, | |
465 | char *extra) | |
466 | { | |
467 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
468 | unsigned long flags; | |
469 | ||
470 | wx_enter(); | |
471 | ||
472 | spin_lock_irqsave(&bcm->lock, flags); | |
473 | data->frag.value = bcm->ieee->fts; | |
474 | data->frag.fixed = 0; | |
475 | data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD); | |
476 | spin_unlock_irqrestore(&bcm->lock, flags); | |
477 | ||
478 | return 0; | |
479 | } | |
480 | ||
481 | static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, | |
482 | struct iw_request_info *info, | |
483 | union iwreq_data *data, | |
484 | char *extra) | |
485 | { | |
486 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
487 | unsigned long flags; | |
488 | int err = -ENODEV; | |
489 | ||
490 | wx_enter(); | |
491 | ||
492 | spin_lock_irqsave(&bcm->lock, flags); | |
493 | if (!bcm->initialized) | |
494 | goto out_unlock; | |
495 | if (data->power.disabled != (!(bcm->current_core->radio->enabled))) { | |
496 | if (data->power.disabled) | |
497 | bcm43xx_radio_turn_off(bcm); | |
498 | else | |
499 | bcm43xx_radio_turn_on(bcm); | |
500 | } | |
501 | //TODO: set txpower. | |
502 | err = 0; | |
503 | ||
504 | out_unlock: | |
505 | spin_unlock_irqrestore(&bcm->lock, flags); | |
506 | ||
507 | return err; | |
508 | } | |
509 | ||
510 | static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, | |
511 | struct iw_request_info *info, | |
512 | union iwreq_data *data, | |
513 | char *extra) | |
514 | { | |
515 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
516 | unsigned long flags; | |
517 | ||
518 | wx_enter(); | |
519 | ||
520 | spin_lock_irqsave(&bcm->lock, flags); | |
521 | //TODO data->power.value = ??? | |
522 | data->power.fixed = 1; | |
523 | data->power.flags = IW_TXPOW_DBM; | |
524 | data->power.disabled = !(bcm->current_core->radio->enabled); | |
525 | spin_unlock_irqrestore(&bcm->lock, flags); | |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | static int bcm43xx_wx_set_retry(struct net_device *net_dev, | |
531 | struct iw_request_info *info, | |
532 | union iwreq_data *data, | |
533 | char *extra) | |
534 | { | |
535 | wx_enter(); | |
536 | /*TODO*/ | |
537 | return 0; | |
538 | } | |
539 | ||
540 | static int bcm43xx_wx_get_retry(struct net_device *net_dev, | |
541 | struct iw_request_info *info, | |
542 | union iwreq_data *data, | |
543 | char *extra) | |
544 | { | |
545 | wx_enter(); | |
546 | /*TODO*/ | |
547 | return 0; | |
548 | } | |
549 | ||
550 | static int bcm43xx_wx_set_encoding(struct net_device *net_dev, | |
551 | struct iw_request_info *info, | |
552 | union iwreq_data *data, | |
553 | char *extra) | |
554 | { | |
555 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
556 | int err; | |
557 | ||
558 | wx_enter(); | |
559 | ||
560 | err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra); | |
561 | ||
562 | return err; | |
563 | } | |
564 | ||
565 | static int bcm43xx_wx_set_encodingext(struct net_device *net_dev, | |
566 | struct iw_request_info *info, | |
567 | union iwreq_data *data, | |
568 | char *extra) | |
569 | { | |
570 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
571 | int err; | |
572 | ||
573 | wx_enter(); | |
574 | ||
575 | err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra); | |
576 | ||
577 | return err; | |
578 | } | |
579 | ||
580 | static int bcm43xx_wx_get_encoding(struct net_device *net_dev, | |
581 | struct iw_request_info *info, | |
582 | union iwreq_data *data, | |
583 | char *extra) | |
584 | { | |
585 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
586 | int err; | |
587 | ||
588 | wx_enter(); | |
589 | ||
590 | err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra); | |
591 | ||
592 | return err; | |
593 | } | |
594 | ||
595 | static int bcm43xx_wx_get_encodingext(struct net_device *net_dev, | |
596 | struct iw_request_info *info, | |
597 | union iwreq_data *data, | |
598 | char *extra) | |
599 | { | |
600 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
601 | int err; | |
602 | ||
603 | wx_enter(); | |
604 | ||
605 | err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra); | |
606 | ||
607 | return err; | |
608 | } | |
609 | ||
610 | static int bcm43xx_wx_set_power(struct net_device *net_dev, | |
611 | struct iw_request_info *info, | |
612 | union iwreq_data *data, | |
613 | char *extra) | |
614 | { | |
615 | wx_enter(); | |
616 | /*TODO*/ | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static int bcm43xx_wx_get_power(struct net_device *net_dev, | |
621 | struct iw_request_info *info, | |
622 | union iwreq_data *data, | |
623 | char *extra) | |
624 | { | |
625 | wx_enter(); | |
626 | /*TODO*/ | |
627 | return 0; | |
628 | } | |
629 | ||
630 | static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, | |
631 | struct iw_request_info *info, | |
632 | union iwreq_data *data, | |
633 | char *extra) | |
634 | { | |
635 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
636 | unsigned long flags; | |
637 | int mode, err = 0; | |
638 | ||
639 | wx_enter(); | |
640 | ||
641 | mode = *((int *)extra); | |
642 | switch (mode) { | |
643 | case 0: | |
644 | mode = BCM43xx_RADIO_INTERFMODE_NONE; | |
645 | break; | |
646 | case 1: | |
647 | mode = BCM43xx_RADIO_INTERFMODE_NONWLAN; | |
648 | break; | |
649 | case 2: | |
650 | mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; | |
651 | break; | |
652 | case 3: | |
653 | mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN; | |
654 | break; | |
655 | default: | |
656 | printk(KERN_ERR PFX "set_interfmode allowed parameters are: " | |
657 | "0 => None, 1 => Non-WLAN, 2 => WLAN, " | |
658 | "3 => Auto-WLAN\n"); | |
659 | return -EINVAL; | |
660 | } | |
661 | ||
662 | spin_lock_irqsave(&bcm->lock, flags); | |
663 | if (bcm->initialized) { | |
664 | err = bcm43xx_radio_set_interference_mitigation(bcm, mode); | |
665 | if (err) { | |
666 | printk(KERN_ERR PFX "Interference Mitigation not " | |
667 | "supported by device\n"); | |
668 | } | |
669 | } else { | |
670 | if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) { | |
671 | printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN " | |
672 | "not supported while the interface is down.\n"); | |
673 | err = -ENODEV; | |
674 | } else | |
675 | bcm->current_core->radio->interfmode = mode; | |
676 | } | |
677 | spin_unlock_irqrestore(&bcm->lock, flags); | |
678 | ||
679 | return err; | |
680 | } | |
681 | ||
682 | static int bcm43xx_wx_get_interfmode(struct net_device *net_dev, | |
683 | struct iw_request_info *info, | |
684 | union iwreq_data *data, | |
685 | char *extra) | |
686 | { | |
687 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
688 | unsigned long flags; | |
689 | int mode; | |
690 | ||
691 | wx_enter(); | |
692 | ||
693 | spin_lock_irqsave(&bcm->lock, flags); | |
694 | mode = bcm->current_core->radio->interfmode; | |
695 | spin_unlock_irqrestore(&bcm->lock, flags); | |
696 | ||
697 | switch (mode) { | |
698 | case BCM43xx_RADIO_INTERFMODE_NONE: | |
699 | strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING); | |
700 | break; | |
701 | case BCM43xx_RADIO_INTERFMODE_NONWLAN: | |
702 | strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING); | |
703 | break; | |
704 | case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: | |
705 | strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING); | |
706 | break; | |
707 | default: | |
708 | assert(0); | |
709 | } | |
710 | data->data.length = strlen(extra) + 1; | |
711 | ||
712 | return 0; | |
713 | } | |
714 | ||
715 | static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev, | |
716 | struct iw_request_info *info, | |
717 | union iwreq_data *data, | |
718 | char *extra) | |
719 | { | |
720 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
721 | unsigned long flags; | |
722 | int on; | |
723 | ||
724 | wx_enter(); | |
725 | ||
726 | on = *((int *)extra); | |
727 | spin_lock_irqsave(&bcm->lock, flags); | |
728 | bcm->short_preamble = !!on; | |
729 | spin_unlock_irqrestore(&bcm->lock, flags); | |
730 | ||
731 | return 0; | |
732 | } | |
733 | ||
734 | static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev, | |
735 | struct iw_request_info *info, | |
736 | union iwreq_data *data, | |
737 | char *extra) | |
738 | { | |
739 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
740 | unsigned long flags; | |
741 | int on; | |
742 | ||
743 | wx_enter(); | |
744 | ||
745 | spin_lock_irqsave(&bcm->lock, flags); | |
746 | on = bcm->short_preamble; | |
747 | spin_unlock_irqrestore(&bcm->lock, flags); | |
748 | ||
749 | if (on) | |
750 | strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING); | |
751 | else | |
752 | strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING); | |
753 | data->data.length = strlen(extra) + 1; | |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
758 | static int bcm43xx_wx_set_swencryption(struct net_device *net_dev, | |
759 | struct iw_request_info *info, | |
760 | union iwreq_data *data, | |
761 | char *extra) | |
762 | { | |
763 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
764 | unsigned long flags; | |
765 | int on; | |
766 | ||
767 | wx_enter(); | |
768 | ||
769 | on = *((int *)extra); | |
770 | spin_lock_irqsave(&bcm->lock, flags); | |
771 | bcm->ieee->host_encrypt = !!on; | |
772 | bcm->ieee->host_decrypt = !!on; | |
773 | bcm->ieee->host_build_iv = !on; | |
774 | ||
775 | spin_unlock_irqrestore(&bcm->lock, flags); | |
776 | ||
777 | return 0; | |
778 | } | |
779 | ||
780 | static int bcm43xx_wx_get_swencryption(struct net_device *net_dev, | |
781 | struct iw_request_info *info, | |
782 | union iwreq_data *data, | |
783 | char *extra) | |
784 | { | |
785 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
786 | unsigned long flags; | |
787 | int on; | |
788 | ||
789 | wx_enter(); | |
790 | ||
791 | spin_lock_irqsave(&bcm->lock, flags); | |
792 | on = bcm->ieee->host_encrypt; | |
793 | spin_unlock_irqrestore(&bcm->lock, flags); | |
794 | ||
795 | if (on) | |
796 | strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING); | |
797 | else | |
798 | strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING); | |
799 | data->data.length = strlen(extra + 1); | |
800 | ||
801 | return 0; | |
802 | } | |
803 | ||
804 | /* Enough buffer to hold a hexdump of the sprom data. */ | |
805 | #define SPROM_BUFFERSIZE 512 | |
806 | ||
807 | static int sprom2hex(const u16 *sprom, char *dump) | |
808 | { | |
809 | int i, pos = 0; | |
810 | ||
811 | for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { | |
812 | pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1, | |
813 | "%04X", swab16(sprom[i]) & 0xFFFF); | |
814 | } | |
815 | ||
816 | return pos + 1; | |
817 | } | |
818 | ||
819 | static int hex2sprom(u16 *sprom, const char *dump, unsigned int len) | |
820 | { | |
821 | char tmp[5] = { 0 }; | |
822 | int cnt = 0; | |
823 | unsigned long parsed; | |
824 | u8 crc, expected_crc; | |
825 | ||
826 | if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2) | |
827 | return -EINVAL; | |
828 | while (cnt < BCM43xx_SPROM_SIZE) { | |
829 | memcpy(tmp, dump, 4); | |
830 | dump += 4; | |
831 | parsed = simple_strtoul(tmp, NULL, 16); | |
832 | sprom[cnt++] = swab16((u16)parsed); | |
833 | } | |
834 | ||
835 | crc = bcm43xx_sprom_crc(sprom); | |
836 | expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8; | |
837 | if (crc != expected_crc) { | |
838 | printk(KERN_ERR PFX "SPROM input data: Invalid CRC\n"); | |
839 | return -EINVAL; | |
840 | } | |
841 | ||
842 | return 0; | |
843 | } | |
844 | ||
845 | static int bcm43xx_wx_sprom_read(struct net_device *net_dev, | |
846 | struct iw_request_info *info, | |
847 | union iwreq_data *data, | |
848 | char *extra) | |
849 | { | |
850 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
851 | int err = -EPERM, i; | |
852 | u16 *sprom; | |
853 | unsigned long flags; | |
854 | ||
855 | if (!capable(CAP_SYS_RAWIO)) | |
856 | goto out; | |
857 | ||
858 | err = -ENOMEM; | |
859 | sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), | |
860 | GFP_KERNEL); | |
861 | if (!sprom) | |
862 | goto out; | |
863 | ||
864 | spin_lock_irqsave(&bcm->lock, flags); | |
865 | err = -ENODEV; | |
866 | if (!bcm->initialized) { | |
867 | spin_unlock_irqrestore(&bcm->lock, flags); | |
868 | goto out_kfree; | |
869 | } | |
870 | for (i = 0; i < BCM43xx_SPROM_SIZE; i++) | |
871 | sprom[i] = bcm43xx_read16(bcm, BCM43xx_SPROM_BASE + (i * 2)); | |
872 | spin_unlock_irqrestore(&bcm->lock, flags); | |
873 | ||
874 | data->data.length = sprom2hex(sprom, extra); | |
875 | ||
876 | err = 0; | |
877 | out_kfree: | |
878 | kfree(sprom); | |
879 | out: | |
880 | return err; | |
881 | } | |
882 | ||
883 | static int bcm43xx_wx_sprom_write(struct net_device *net_dev, | |
884 | struct iw_request_info *info, | |
885 | union iwreq_data *data, | |
886 | char *extra) | |
887 | { | |
888 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | |
889 | int err = -EPERM; | |
890 | u16 *sprom; | |
891 | unsigned long flags; | |
892 | char *input; | |
893 | unsigned int len; | |
894 | u32 spromctl; | |
895 | int i; | |
896 | ||
897 | if (!capable(CAP_SYS_RAWIO)) | |
898 | goto out; | |
899 | ||
900 | err = -ENOMEM; | |
901 | sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), | |
902 | GFP_KERNEL); | |
903 | if (!sprom) | |
904 | goto out; | |
905 | ||
906 | len = data->data.length; | |
907 | extra[len - 1] = '\0'; | |
908 | input = strchr(extra, ':'); | |
909 | if (input) { | |
910 | input++; | |
911 | len -= input - extra; | |
912 | } else | |
913 | input = extra; | |
914 | err = hex2sprom(sprom, input, len); | |
915 | if (err) | |
916 | goto out_kfree; | |
917 | ||
918 | spin_lock_irqsave(&bcm->lock, flags); | |
919 | err = -ENODEV; | |
920 | if (!bcm->initialized) { | |
921 | spin_unlock_irqrestore(&bcm->lock, flags); | |
922 | goto out_kfree; | |
923 | } | |
924 | ||
925 | printk(KERN_INFO PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); | |
926 | err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_SPROMCTL, &spromctl); | |
927 | if (err) { | |
928 | printk(KERN_ERR PFX "Could not access SPROM control register.\n"); | |
929 | goto out_unlock; | |
930 | } | |
931 | spromctl |= 0x10; /* SPROM WRITE enable. */ | |
932 | bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); | |
933 | if (err) { | |
934 | printk(KERN_ERR PFX "Could not access SPROM control register.\n"); | |
935 | goto out_unlock; | |
936 | } | |
937 | /* We must burn lots of CPU cycles here, but that does not | |
938 | * really matter as one does not write the SPROM every other minute... | |
939 | */ | |
940 | printk(KERN_INFO PFX "[ 0%%"); | |
941 | mdelay(500); | |
942 | for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { | |
943 | if (i == 16) | |
944 | printk("25%%"); | |
945 | else if (i == 32) | |
946 | printk("50%%"); | |
947 | else if (i == 48) | |
948 | printk("75%%"); | |
949 | else if (i % 2) | |
950 | printk("."); | |
951 | //TODO bcm43xx_write16(bcm, BCM43xx_SPROM_BASE + (i * 2), sprom[i]); | |
952 | mdelay(20); | |
953 | } | |
954 | spromctl &= ~0x10; /* SPROM WRITE enable. */ | |
955 | bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); | |
956 | if (err) { | |
957 | printk(KERN_ERR PFX "Could not access SPROM control register.\n"); | |
958 | goto out_unlock; | |
959 | } | |
960 | mdelay(500); | |
961 | printk("100%% ]\n"); | |
962 | printk(KERN_INFO PFX "SPROM written.\n"); | |
963 | err = 0; | |
964 | out_unlock: | |
965 | spin_unlock_irqrestore(&bcm->lock, flags); | |
966 | out_kfree: | |
967 | kfree(sprom); | |
968 | out: | |
969 | return err; | |
970 | } | |
971 | ||
972 | ||
973 | #ifdef WX | |
974 | # undef WX | |
975 | #endif | |
976 | #define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT] | |
977 | static const iw_handler bcm43xx_wx_handlers[] = { | |
978 | /* Wireless Identification */ | |
979 | WX(SIOCGIWNAME) = bcm43xx_wx_get_name, | |
980 | /* Basic operations */ | |
981 | WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq, | |
982 | WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq, | |
983 | WX(SIOCSIWMODE) = bcm43xx_wx_set_mode, | |
984 | WX(SIOCGIWMODE) = bcm43xx_wx_get_mode, | |
985 | /* Informative stuff */ | |
986 | WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams, | |
987 | /* Access Point manipulation */ | |
988 | WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, | |
989 | WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, | |
990 | WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, | |
991 | WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, | |
992 | /* 802.11 specific support */ | |
993 | WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, | |
994 | WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, | |
995 | WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick, | |
996 | WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick, | |
997 | /* Other parameters */ | |
998 | WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, | |
999 | WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, | |
1000 | WX(SIOCSIWRTS) = bcm43xx_wx_set_rts, | |
1001 | WX(SIOCGIWRTS) = bcm43xx_wx_get_rts, | |
1002 | WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag, | |
1003 | WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag, | |
1004 | WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower, | |
1005 | WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower, | |
1006 | //TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry, | |
1007 | //TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry, | |
1008 | /* Encoding */ | |
1009 | WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding, | |
1010 | WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding, | |
1011 | WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext, | |
1012 | WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext, | |
1013 | /* Power saving */ | |
1014 | //TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power, | |
1015 | //TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power, | |
1016 | WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, | |
1017 | WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, | |
1018 | WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, | |
1019 | WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, | |
1020 | }; | |
1021 | #undef WX | |
1022 | ||
1023 | static const iw_handler bcm43xx_priv_wx_handlers[] = { | |
1024 | /* Set Interference Mitigation Mode. */ | |
1025 | bcm43xx_wx_set_interfmode, | |
1026 | /* Get Interference Mitigation Mode. */ | |
1027 | bcm43xx_wx_get_interfmode, | |
1028 | /* Enable/Disable Short Preamble mode. */ | |
1029 | bcm43xx_wx_set_shortpreamble, | |
1030 | /* Get Short Preamble mode. */ | |
1031 | bcm43xx_wx_get_shortpreamble, | |
1032 | /* Enable/Disable Software Encryption mode */ | |
1033 | bcm43xx_wx_set_swencryption, | |
1034 | /* Get Software Encryption mode */ | |
1035 | bcm43xx_wx_get_swencryption, | |
1036 | /* Write SRPROM data. */ | |
1037 | bcm43xx_wx_sprom_write, | |
1038 | /* Read SPROM data. */ | |
1039 | bcm43xx_wx_sprom_read, | |
1040 | }; | |
1041 | ||
1042 | #define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0) | |
1043 | #define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1) | |
1044 | #define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2) | |
1045 | #define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3) | |
1046 | #define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4) | |
1047 | #define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5) | |
1048 | #define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6) | |
1049 | #define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7) | |
1050 | ||
1051 | #define PRIV_WX_DUMMY(ioctl) \ | |
1052 | { \ | |
1053 | .cmd = (ioctl), \ | |
1054 | .name = "__unused" \ | |
1055 | } | |
1056 | ||
1057 | static const struct iw_priv_args bcm43xx_priv_wx_args[] = { | |
1058 | { | |
1059 | .cmd = PRIV_WX_SET_INTERFMODE, | |
1060 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | |
1061 | .name = "set_interfmode", | |
1062 | }, | |
1063 | { | |
1064 | .cmd = PRIV_WX_GET_INTERFMODE, | |
1065 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | |
1066 | .name = "get_interfmode", | |
1067 | }, | |
1068 | { | |
1069 | .cmd = PRIV_WX_SET_SHORTPREAMBLE, | |
1070 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | |
1071 | .name = "set_shortpreambl", | |
1072 | }, | |
1073 | { | |
1074 | .cmd = PRIV_WX_GET_SHORTPREAMBLE, | |
1075 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | |
1076 | .name = "get_shortpreambl", | |
1077 | }, | |
1078 | { | |
1079 | .cmd = PRIV_WX_SET_SWENCRYPTION, | |
1080 | .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | |
1081 | .name = "set_swencryption", | |
1082 | }, | |
1083 | { | |
1084 | .cmd = PRIV_WX_GET_SWENCRYPTION, | |
1085 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, | |
1086 | .name = "get_swencryption", | |
1087 | }, | |
1088 | { | |
1089 | .cmd = PRIV_WX_SPROM_WRITE, | |
1090 | .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE, | |
1091 | .name = "write_sprom", | |
1092 | }, | |
1093 | { | |
1094 | .cmd = PRIV_WX_SPROM_READ, | |
1095 | .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE, | |
1096 | .name = "read_sprom", | |
1097 | }, | |
1098 | }; | |
1099 | ||
1100 | const struct iw_handler_def bcm43xx_wx_handlers_def = { | |
1101 | .standard = bcm43xx_wx_handlers, | |
1102 | .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers), | |
1103 | .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers), | |
1104 | .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args), | |
1105 | .private = bcm43xx_priv_wx_handlers, | |
1106 | .private_args = bcm43xx_priv_wx_args, | |
1107 | }; | |
1108 | ||
1109 | /* vim: set ts=8 sw=8 sts=8: */ |