diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/wext.c')
-rw-r--r-- | drivers/net/wireless/orinoco/wext.c | 2325 |
1 files changed, 2325 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c new file mode 100644 index 000000000000..3f0814234392 --- /dev/null +++ b/drivers/net/wireless/orinoco/wext.c | |||
@@ -0,0 +1,2325 @@ | |||
1 | /* Wireless extensions support. | ||
2 | * | ||
3 | * See copyright notice in main.c | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/if_arp.h> | ||
7 | #include <linux/wireless.h> | ||
8 | #include <linux/ieee80211.h> | ||
9 | #include <net/iw_handler.h> | ||
10 | |||
11 | #include "hermes.h" | ||
12 | #include "hermes_rid.h" | ||
13 | #include "orinoco.h" | ||
14 | |||
15 | #include "hw.h" | ||
16 | #include "mic.h" | ||
17 | #include "scan.h" | ||
18 | #include "main.h" | ||
19 | |||
20 | #include "wext.h" | ||
21 | |||
22 | #define MAX_RID_LEN 1024 | ||
23 | |||
24 | static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) | ||
25 | { | ||
26 | struct orinoco_private *priv = netdev_priv(dev); | ||
27 | hermes_t *hw = &priv->hw; | ||
28 | struct iw_statistics *wstats = &priv->wstats; | ||
29 | int err; | ||
30 | unsigned long flags; | ||
31 | |||
32 | if (!netif_device_present(dev)) { | ||
33 | printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", | ||
34 | dev->name); | ||
35 | return NULL; /* FIXME: Can we do better than this? */ | ||
36 | } | ||
37 | |||
38 | /* If busy, return the old stats. Returning NULL may cause | ||
39 | * the interface to disappear from /proc/net/wireless */ | ||
40 | if (orinoco_lock(priv, &flags) != 0) | ||
41 | return wstats; | ||
42 | |||
43 | /* We can't really wait for the tallies inquiry command to | ||
44 | * complete, so we just use the previous results and trigger | ||
45 | * a new tallies inquiry command for next time - Jean II */ | ||
46 | /* FIXME: Really we should wait for the inquiry to come back - | ||
47 | * as it is the stats we give don't make a whole lot of sense. | ||
48 | * Unfortunately, it's not clear how to do that within the | ||
49 | * wireless extensions framework: I think we're in user | ||
50 | * context, but a lock seems to be held by the time we get in | ||
51 | * here so we're not safe to sleep here. */ | ||
52 | hermes_inquire(hw, HERMES_INQ_TALLIES); | ||
53 | |||
54 | if (priv->iw_mode == IW_MODE_ADHOC) { | ||
55 | memset(&wstats->qual, 0, sizeof(wstats->qual)); | ||
56 | /* If a spy address is defined, we report stats of the | ||
57 | * first spy address - Jean II */ | ||
58 | if (SPY_NUMBER(priv)) { | ||
59 | wstats->qual.qual = priv->spy_data.spy_stat[0].qual; | ||
60 | wstats->qual.level = priv->spy_data.spy_stat[0].level; | ||
61 | wstats->qual.noise = priv->spy_data.spy_stat[0].noise; | ||
62 | wstats->qual.updated = | ||
63 | priv->spy_data.spy_stat[0].updated; | ||
64 | } | ||
65 | } else { | ||
66 | struct { | ||
67 | __le16 qual, signal, noise, unused; | ||
68 | } __attribute__ ((packed)) cq; | ||
69 | |||
70 | err = HERMES_READ_RECORD(hw, USER_BAP, | ||
71 | HERMES_RID_COMMSQUALITY, &cq); | ||
72 | |||
73 | if (!err) { | ||
74 | wstats->qual.qual = (int)le16_to_cpu(cq.qual); | ||
75 | wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; | ||
76 | wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; | ||
77 | wstats->qual.updated = | ||
78 | IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | orinoco_unlock(priv, &flags); | ||
83 | return wstats; | ||
84 | } | ||
85 | |||
86 | /********************************************************************/ | ||
87 | /* Wireless extensions */ | ||
88 | /********************************************************************/ | ||
89 | |||
90 | static int orinoco_ioctl_getname(struct net_device *dev, | ||
91 | struct iw_request_info *info, | ||
92 | char *name, | ||
93 | char *extra) | ||
94 | { | ||
95 | struct orinoco_private *priv = netdev_priv(dev); | ||
96 | int numrates; | ||
97 | int err; | ||
98 | |||
99 | err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); | ||
100 | |||
101 | if (!err && (numrates > 2)) | ||
102 | strcpy(name, "IEEE 802.11b"); | ||
103 | else | ||
104 | strcpy(name, "IEEE 802.11-DS"); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int orinoco_ioctl_setwap(struct net_device *dev, | ||
110 | struct iw_request_info *info, | ||
111 | struct sockaddr *ap_addr, | ||
112 | char *extra) | ||
113 | { | ||
114 | struct orinoco_private *priv = netdev_priv(dev); | ||
115 | int err = -EINPROGRESS; /* Call commit handler */ | ||
116 | unsigned long flags; | ||
117 | static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
118 | static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
119 | |||
120 | if (orinoco_lock(priv, &flags) != 0) | ||
121 | return -EBUSY; | ||
122 | |||
123 | /* Enable automatic roaming - no sanity checks are needed */ | ||
124 | if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || | ||
125 | memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { | ||
126 | priv->bssid_fixed = 0; | ||
127 | memset(priv->desired_bssid, 0, ETH_ALEN); | ||
128 | |||
129 | /* "off" means keep existing connection */ | ||
130 | if (ap_addr->sa_data[0] == 0) { | ||
131 | __orinoco_hw_set_wap(priv); | ||
132 | err = 0; | ||
133 | } | ||
134 | goto out; | ||
135 | } | ||
136 | |||
137 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { | ||
138 | printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " | ||
139 | "support manual roaming\n", | ||
140 | dev->name); | ||
141 | err = -EOPNOTSUPP; | ||
142 | goto out; | ||
143 | } | ||
144 | |||
145 | if (priv->iw_mode != IW_MODE_INFRA) { | ||
146 | printk(KERN_WARNING "%s: Manual roaming supported only in " | ||
147 | "managed mode\n", dev->name); | ||
148 | err = -EOPNOTSUPP; | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | /* Intersil firmware hangs without Desired ESSID */ | ||
153 | if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && | ||
154 | strlen(priv->desired_essid) == 0) { | ||
155 | printk(KERN_WARNING "%s: Desired ESSID must be set for " | ||
156 | "manual roaming\n", dev->name); | ||
157 | err = -EOPNOTSUPP; | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | /* Finally, enable manual roaming */ | ||
162 | priv->bssid_fixed = 1; | ||
163 | memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); | ||
164 | |||
165 | out: | ||
166 | orinoco_unlock(priv, &flags); | ||
167 | return err; | ||
168 | } | ||
169 | |||
170 | static int orinoco_ioctl_getwap(struct net_device *dev, | ||
171 | struct iw_request_info *info, | ||
172 | struct sockaddr *ap_addr, | ||
173 | char *extra) | ||
174 | { | ||
175 | struct orinoco_private *priv = netdev_priv(dev); | ||
176 | |||
177 | hermes_t *hw = &priv->hw; | ||
178 | int err = 0; | ||
179 | unsigned long flags; | ||
180 | |||
181 | if (orinoco_lock(priv, &flags) != 0) | ||
182 | return -EBUSY; | ||
183 | |||
184 | ap_addr->sa_family = ARPHRD_ETHER; | ||
185 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, | ||
186 | ETH_ALEN, NULL, ap_addr->sa_data); | ||
187 | |||
188 | orinoco_unlock(priv, &flags); | ||
189 | |||
190 | return err; | ||
191 | } | ||
192 | |||
193 | static int orinoco_ioctl_setmode(struct net_device *dev, | ||
194 | struct iw_request_info *info, | ||
195 | u32 *mode, | ||
196 | char *extra) | ||
197 | { | ||
198 | struct orinoco_private *priv = netdev_priv(dev); | ||
199 | int err = -EINPROGRESS; /* Call commit handler */ | ||
200 | unsigned long flags; | ||
201 | |||
202 | if (priv->iw_mode == *mode) | ||
203 | return 0; | ||
204 | |||
205 | if (orinoco_lock(priv, &flags) != 0) | ||
206 | return -EBUSY; | ||
207 | |||
208 | switch (*mode) { | ||
209 | case IW_MODE_ADHOC: | ||
210 | if (!priv->has_ibss && !priv->has_port3) | ||
211 | err = -EOPNOTSUPP; | ||
212 | break; | ||
213 | |||
214 | case IW_MODE_INFRA: | ||
215 | break; | ||
216 | |||
217 | case IW_MODE_MONITOR: | ||
218 | if (priv->broken_monitor && !force_monitor) { | ||
219 | printk(KERN_WARNING "%s: Monitor mode support is " | ||
220 | "buggy in this firmware, not enabling\n", | ||
221 | dev->name); | ||
222 | err = -EOPNOTSUPP; | ||
223 | } | ||
224 | break; | ||
225 | |||
226 | default: | ||
227 | err = -EOPNOTSUPP; | ||
228 | break; | ||
229 | } | ||
230 | |||
231 | if (err == -EINPROGRESS) { | ||
232 | priv->iw_mode = *mode; | ||
233 | set_port_type(priv); | ||
234 | } | ||
235 | |||
236 | orinoco_unlock(priv, &flags); | ||
237 | |||
238 | return err; | ||
239 | } | ||
240 | |||
241 | static int orinoco_ioctl_getmode(struct net_device *dev, | ||
242 | struct iw_request_info *info, | ||
243 | u32 *mode, | ||
244 | char *extra) | ||
245 | { | ||
246 | struct orinoco_private *priv = netdev_priv(dev); | ||
247 | |||
248 | *mode = priv->iw_mode; | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static int orinoco_ioctl_getiwrange(struct net_device *dev, | ||
253 | struct iw_request_info *info, | ||
254 | struct iw_point *rrq, | ||
255 | char *extra) | ||
256 | { | ||
257 | struct orinoco_private *priv = netdev_priv(dev); | ||
258 | int err = 0; | ||
259 | struct iw_range *range = (struct iw_range *) extra; | ||
260 | int numrates; | ||
261 | int i, k; | ||
262 | |||
263 | rrq->length = sizeof(struct iw_range); | ||
264 | memset(range, 0, sizeof(struct iw_range)); | ||
265 | |||
266 | range->we_version_compiled = WIRELESS_EXT; | ||
267 | range->we_version_source = 22; | ||
268 | |||
269 | /* Set available channels/frequencies */ | ||
270 | range->num_channels = NUM_CHANNELS; | ||
271 | k = 0; | ||
272 | for (i = 0; i < NUM_CHANNELS; i++) { | ||
273 | if (priv->channel_mask & (1 << i)) { | ||
274 | range->freq[k].i = i + 1; | ||
275 | range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) * | ||
276 | 100000); | ||
277 | range->freq[k].e = 1; | ||
278 | k++; | ||
279 | } | ||
280 | |||
281 | if (k >= IW_MAX_FREQUENCIES) | ||
282 | break; | ||
283 | } | ||
284 | range->num_frequency = k; | ||
285 | range->sensitivity = 3; | ||
286 | |||
287 | if (priv->has_wep) { | ||
288 | range->max_encoding_tokens = ORINOCO_MAX_KEYS; | ||
289 | range->encoding_size[0] = SMALL_KEY_SIZE; | ||
290 | range->num_encoding_sizes = 1; | ||
291 | |||
292 | if (priv->has_big_wep) { | ||
293 | range->encoding_size[1] = LARGE_KEY_SIZE; | ||
294 | range->num_encoding_sizes = 2; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | if (priv->has_wpa) | ||
299 | range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; | ||
300 | |||
301 | if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) { | ||
302 | /* Quality stats meaningless in ad-hoc mode */ | ||
303 | } else { | ||
304 | range->max_qual.qual = 0x8b - 0x2f; | ||
305 | range->max_qual.level = 0x2f - 0x95 - 1; | ||
306 | range->max_qual.noise = 0x2f - 0x95 - 1; | ||
307 | /* Need to get better values */ | ||
308 | range->avg_qual.qual = 0x24; | ||
309 | range->avg_qual.level = 0xC2; | ||
310 | range->avg_qual.noise = 0x9E; | ||
311 | } | ||
312 | |||
313 | err = orinoco_hw_get_bitratelist(priv, &numrates, | ||
314 | range->bitrate, IW_MAX_BITRATES); | ||
315 | if (err) | ||
316 | return err; | ||
317 | range->num_bitrates = numrates; | ||
318 | |||
319 | /* Set an indication of the max TCP throughput in bit/s that we can | ||
320 | * expect using this interface. May be use for QoS stuff... | ||
321 | * Jean II */ | ||
322 | if (numrates > 2) | ||
323 | range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ | ||
324 | else | ||
325 | range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ | ||
326 | |||
327 | range->min_rts = 0; | ||
328 | range->max_rts = 2347; | ||
329 | range->min_frag = 256; | ||
330 | range->max_frag = 2346; | ||
331 | |||
332 | range->min_pmp = 0; | ||
333 | range->max_pmp = 65535000; | ||
334 | range->min_pmt = 0; | ||
335 | range->max_pmt = 65535 * 1000; /* ??? */ | ||
336 | range->pmp_flags = IW_POWER_PERIOD; | ||
337 | range->pmt_flags = IW_POWER_TIMEOUT; | ||
338 | range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT | | ||
339 | IW_POWER_UNICAST_R); | ||
340 | |||
341 | range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; | ||
342 | range->retry_flags = IW_RETRY_LIMIT; | ||
343 | range->r_time_flags = IW_RETRY_LIFETIME; | ||
344 | range->min_retry = 0; | ||
345 | range->max_retry = 65535; /* ??? */ | ||
346 | range->min_r_time = 0; | ||
347 | range->max_r_time = 65535 * 1000; /* ??? */ | ||
348 | |||
349 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) | ||
350 | range->scan_capa = IW_SCAN_CAPA_ESSID; | ||
351 | else | ||
352 | range->scan_capa = IW_SCAN_CAPA_NONE; | ||
353 | |||
354 | /* Event capability (kernel) */ | ||
355 | IW_EVENT_CAPA_SET_KERNEL(range->event_capa); | ||
356 | /* Event capability (driver) */ | ||
357 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); | ||
358 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); | ||
359 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); | ||
360 | IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int orinoco_ioctl_setiwencode(struct net_device *dev, | ||
366 | struct iw_request_info *info, | ||
367 | struct iw_point *erq, | ||
368 | char *keybuf) | ||
369 | { | ||
370 | struct orinoco_private *priv = netdev_priv(dev); | ||
371 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
372 | int setindex = priv->tx_key; | ||
373 | int encode_alg = priv->encode_alg; | ||
374 | int restricted = priv->wep_restrict; | ||
375 | u16 xlen = 0; | ||
376 | int err = -EINPROGRESS; /* Call commit handler */ | ||
377 | unsigned long flags; | ||
378 | |||
379 | if (!priv->has_wep) | ||
380 | return -EOPNOTSUPP; | ||
381 | |||
382 | if (erq->pointer) { | ||
383 | /* We actually have a key to set - check its length */ | ||
384 | if (erq->length > LARGE_KEY_SIZE) | ||
385 | return -E2BIG; | ||
386 | |||
387 | if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) | ||
388 | return -E2BIG; | ||
389 | } | ||
390 | |||
391 | if (orinoco_lock(priv, &flags) != 0) | ||
392 | return -EBUSY; | ||
393 | |||
394 | /* Clear any TKIP key we have */ | ||
395 | if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP)) | ||
396 | (void) orinoco_clear_tkip_key(priv, setindex); | ||
397 | |||
398 | if (erq->length > 0) { | ||
399 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
400 | index = priv->tx_key; | ||
401 | |||
402 | /* Adjust key length to a supported value */ | ||
403 | if (erq->length > SMALL_KEY_SIZE) | ||
404 | xlen = LARGE_KEY_SIZE; | ||
405 | else if (erq->length > 0) | ||
406 | xlen = SMALL_KEY_SIZE; | ||
407 | else | ||
408 | xlen = 0; | ||
409 | |||
410 | /* Switch on WEP if off */ | ||
411 | if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) { | ||
412 | setindex = index; | ||
413 | encode_alg = IW_ENCODE_ALG_WEP; | ||
414 | } | ||
415 | } else { | ||
416 | /* Important note : if the user do "iwconfig eth0 enc off", | ||
417 | * we will arrive there with an index of -1. This is valid | ||
418 | * but need to be taken care off... Jean II */ | ||
419 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { | ||
420 | if ((index != -1) || (erq->flags == 0)) { | ||
421 | err = -EINVAL; | ||
422 | goto out; | ||
423 | } | ||
424 | } else { | ||
425 | /* Set the index : Check that the key is valid */ | ||
426 | if (priv->keys[index].len == 0) { | ||
427 | err = -EINVAL; | ||
428 | goto out; | ||
429 | } | ||
430 | setindex = index; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | if (erq->flags & IW_ENCODE_DISABLED) | ||
435 | encode_alg = IW_ENCODE_ALG_NONE; | ||
436 | if (erq->flags & IW_ENCODE_OPEN) | ||
437 | restricted = 0; | ||
438 | if (erq->flags & IW_ENCODE_RESTRICTED) | ||
439 | restricted = 1; | ||
440 | |||
441 | if (erq->pointer && erq->length > 0) { | ||
442 | priv->keys[index].len = cpu_to_le16(xlen); | ||
443 | memset(priv->keys[index].data, 0, | ||
444 | sizeof(priv->keys[index].data)); | ||
445 | memcpy(priv->keys[index].data, keybuf, erq->length); | ||
446 | } | ||
447 | priv->tx_key = setindex; | ||
448 | |||
449 | /* Try fast key change if connected and only keys are changed */ | ||
450 | if ((priv->encode_alg == encode_alg) && | ||
451 | (priv->wep_restrict == restricted) && | ||
452 | netif_carrier_ok(dev)) { | ||
453 | err = __orinoco_hw_setup_wepkeys(priv); | ||
454 | /* No need to commit if successful */ | ||
455 | goto out; | ||
456 | } | ||
457 | |||
458 | priv->encode_alg = encode_alg; | ||
459 | priv->wep_restrict = restricted; | ||
460 | |||
461 | out: | ||
462 | orinoco_unlock(priv, &flags); | ||
463 | |||
464 | return err; | ||
465 | } | ||
466 | |||
467 | static int orinoco_ioctl_getiwencode(struct net_device *dev, | ||
468 | struct iw_request_info *info, | ||
469 | struct iw_point *erq, | ||
470 | char *keybuf) | ||
471 | { | ||
472 | struct orinoco_private *priv = netdev_priv(dev); | ||
473 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
474 | u16 xlen = 0; | ||
475 | unsigned long flags; | ||
476 | |||
477 | if (!priv->has_wep) | ||
478 | return -EOPNOTSUPP; | ||
479 | |||
480 | if (orinoco_lock(priv, &flags) != 0) | ||
481 | return -EBUSY; | ||
482 | |||
483 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
484 | index = priv->tx_key; | ||
485 | |||
486 | erq->flags = 0; | ||
487 | if (!priv->encode_alg) | ||
488 | erq->flags |= IW_ENCODE_DISABLED; | ||
489 | erq->flags |= index + 1; | ||
490 | |||
491 | if (priv->wep_restrict) | ||
492 | erq->flags |= IW_ENCODE_RESTRICTED; | ||
493 | else | ||
494 | erq->flags |= IW_ENCODE_OPEN; | ||
495 | |||
496 | xlen = le16_to_cpu(priv->keys[index].len); | ||
497 | |||
498 | erq->length = xlen; | ||
499 | |||
500 | memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); | ||
501 | |||
502 | orinoco_unlock(priv, &flags); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int orinoco_ioctl_setessid(struct net_device *dev, | ||
507 | struct iw_request_info *info, | ||
508 | struct iw_point *erq, | ||
509 | char *essidbuf) | ||
510 | { | ||
511 | struct orinoco_private *priv = netdev_priv(dev); | ||
512 | unsigned long flags; | ||
513 | |||
514 | /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it | ||
515 | * anyway... - Jean II */ | ||
516 | |||
517 | /* Hum... Should not use Wireless Extension constant (may change), | ||
518 | * should use our own... - Jean II */ | ||
519 | if (erq->length > IW_ESSID_MAX_SIZE) | ||
520 | return -E2BIG; | ||
521 | |||
522 | if (orinoco_lock(priv, &flags) != 0) | ||
523 | return -EBUSY; | ||
524 | |||
525 | /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ | ||
526 | memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); | ||
527 | |||
528 | /* If not ANY, get the new ESSID */ | ||
529 | if (erq->flags) | ||
530 | memcpy(priv->desired_essid, essidbuf, erq->length); | ||
531 | |||
532 | orinoco_unlock(priv, &flags); | ||
533 | |||
534 | return -EINPROGRESS; /* Call commit handler */ | ||
535 | } | ||
536 | |||
537 | static int orinoco_ioctl_getessid(struct net_device *dev, | ||
538 | struct iw_request_info *info, | ||
539 | struct iw_point *erq, | ||
540 | char *essidbuf) | ||
541 | { | ||
542 | struct orinoco_private *priv = netdev_priv(dev); | ||
543 | int active; | ||
544 | int err = 0; | ||
545 | unsigned long flags; | ||
546 | |||
547 | if (netif_running(dev)) { | ||
548 | err = orinoco_hw_get_essid(priv, &active, essidbuf); | ||
549 | if (err < 0) | ||
550 | return err; | ||
551 | erq->length = err; | ||
552 | } else { | ||
553 | if (orinoco_lock(priv, &flags) != 0) | ||
554 | return -EBUSY; | ||
555 | memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); | ||
556 | erq->length = strlen(priv->desired_essid); | ||
557 | orinoco_unlock(priv, &flags); | ||
558 | } | ||
559 | |||
560 | erq->flags = 1; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static int orinoco_ioctl_setnick(struct net_device *dev, | ||
566 | struct iw_request_info *info, | ||
567 | struct iw_point *nrq, | ||
568 | char *nickbuf) | ||
569 | { | ||
570 | struct orinoco_private *priv = netdev_priv(dev); | ||
571 | unsigned long flags; | ||
572 | |||
573 | if (nrq->length > IW_ESSID_MAX_SIZE) | ||
574 | return -E2BIG; | ||
575 | |||
576 | if (orinoco_lock(priv, &flags) != 0) | ||
577 | return -EBUSY; | ||
578 | |||
579 | memset(priv->nick, 0, sizeof(priv->nick)); | ||
580 | memcpy(priv->nick, nickbuf, nrq->length); | ||
581 | |||
582 | orinoco_unlock(priv, &flags); | ||
583 | |||
584 | return -EINPROGRESS; /* Call commit handler */ | ||
585 | } | ||
586 | |||
587 | static int orinoco_ioctl_getnick(struct net_device *dev, | ||
588 | struct iw_request_info *info, | ||
589 | struct iw_point *nrq, | ||
590 | char *nickbuf) | ||
591 | { | ||
592 | struct orinoco_private *priv = netdev_priv(dev); | ||
593 | unsigned long flags; | ||
594 | |||
595 | if (orinoco_lock(priv, &flags) != 0) | ||
596 | return -EBUSY; | ||
597 | |||
598 | memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE); | ||
599 | orinoco_unlock(priv, &flags); | ||
600 | |||
601 | nrq->length = strlen(priv->nick); | ||
602 | |||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | static int orinoco_ioctl_setfreq(struct net_device *dev, | ||
607 | struct iw_request_info *info, | ||
608 | struct iw_freq *frq, | ||
609 | char *extra) | ||
610 | { | ||
611 | struct orinoco_private *priv = netdev_priv(dev); | ||
612 | int chan = -1; | ||
613 | unsigned long flags; | ||
614 | int err = -EINPROGRESS; /* Call commit handler */ | ||
615 | |||
616 | /* In infrastructure mode the AP sets the channel */ | ||
617 | if (priv->iw_mode == IW_MODE_INFRA) | ||
618 | return -EBUSY; | ||
619 | |||
620 | if ((frq->e == 0) && (frq->m <= 1000)) { | ||
621 | /* Setting by channel number */ | ||
622 | chan = frq->m; | ||
623 | } else { | ||
624 | /* Setting by frequency */ | ||
625 | int denom = 1; | ||
626 | int i; | ||
627 | |||
628 | /* Calculate denominator to rescale to MHz */ | ||
629 | for (i = 0; i < (6 - frq->e); i++) | ||
630 | denom *= 10; | ||
631 | |||
632 | chan = ieee80211_freq_to_dsss_chan(frq->m / denom); | ||
633 | } | ||
634 | |||
635 | if ((chan < 1) || (chan > NUM_CHANNELS) || | ||
636 | !(priv->channel_mask & (1 << (chan-1)))) | ||
637 | return -EINVAL; | ||
638 | |||
639 | if (orinoco_lock(priv, &flags) != 0) | ||
640 | return -EBUSY; | ||
641 | |||
642 | priv->channel = chan; | ||
643 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
644 | /* Fast channel change - no commit if successful */ | ||
645 | hermes_t *hw = &priv->hw; | ||
646 | err = hermes_docmd_wait(hw, HERMES_CMD_TEST | | ||
647 | HERMES_TEST_SET_CHANNEL, | ||
648 | chan, NULL); | ||
649 | } | ||
650 | orinoco_unlock(priv, &flags); | ||
651 | |||
652 | return err; | ||
653 | } | ||
654 | |||
655 | static int orinoco_ioctl_getfreq(struct net_device *dev, | ||
656 | struct iw_request_info *info, | ||
657 | struct iw_freq *frq, | ||
658 | char *extra) | ||
659 | { | ||
660 | struct orinoco_private *priv = netdev_priv(dev); | ||
661 | int tmp; | ||
662 | |||
663 | /* Locking done in there */ | ||
664 | tmp = orinoco_hw_get_freq(priv); | ||
665 | if (tmp < 0) | ||
666 | return tmp; | ||
667 | |||
668 | frq->m = tmp * 100000; | ||
669 | frq->e = 1; | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int orinoco_ioctl_getsens(struct net_device *dev, | ||
675 | struct iw_request_info *info, | ||
676 | struct iw_param *srq, | ||
677 | char *extra) | ||
678 | { | ||
679 | struct orinoco_private *priv = netdev_priv(dev); | ||
680 | hermes_t *hw = &priv->hw; | ||
681 | u16 val; | ||
682 | int err; | ||
683 | unsigned long flags; | ||
684 | |||
685 | if (!priv->has_sensitivity) | ||
686 | return -EOPNOTSUPP; | ||
687 | |||
688 | if (orinoco_lock(priv, &flags) != 0) | ||
689 | return -EBUSY; | ||
690 | err = hermes_read_wordrec(hw, USER_BAP, | ||
691 | HERMES_RID_CNFSYSTEMSCALE, &val); | ||
692 | orinoco_unlock(priv, &flags); | ||
693 | |||
694 | if (err) | ||
695 | return err; | ||
696 | |||
697 | srq->value = val; | ||
698 | srq->fixed = 0; /* auto */ | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int orinoco_ioctl_setsens(struct net_device *dev, | ||
704 | struct iw_request_info *info, | ||
705 | struct iw_param *srq, | ||
706 | char *extra) | ||
707 | { | ||
708 | struct orinoco_private *priv = netdev_priv(dev); | ||
709 | int val = srq->value; | ||
710 | unsigned long flags; | ||
711 | |||
712 | if (!priv->has_sensitivity) | ||
713 | return -EOPNOTSUPP; | ||
714 | |||
715 | if ((val < 1) || (val > 3)) | ||
716 | return -EINVAL; | ||
717 | |||
718 | if (orinoco_lock(priv, &flags) != 0) | ||
719 | return -EBUSY; | ||
720 | priv->ap_density = val; | ||
721 | orinoco_unlock(priv, &flags); | ||
722 | |||
723 | return -EINPROGRESS; /* Call commit handler */ | ||
724 | } | ||
725 | |||
726 | static int orinoco_ioctl_setrts(struct net_device *dev, | ||
727 | struct iw_request_info *info, | ||
728 | struct iw_param *rrq, | ||
729 | char *extra) | ||
730 | { | ||
731 | struct orinoco_private *priv = netdev_priv(dev); | ||
732 | int val = rrq->value; | ||
733 | unsigned long flags; | ||
734 | |||
735 | if (rrq->disabled) | ||
736 | val = 2347; | ||
737 | |||
738 | if ((val < 0) || (val > 2347)) | ||
739 | return -EINVAL; | ||
740 | |||
741 | if (orinoco_lock(priv, &flags) != 0) | ||
742 | return -EBUSY; | ||
743 | |||
744 | priv->rts_thresh = val; | ||
745 | orinoco_unlock(priv, &flags); | ||
746 | |||
747 | return -EINPROGRESS; /* Call commit handler */ | ||
748 | } | ||
749 | |||
750 | static int orinoco_ioctl_getrts(struct net_device *dev, | ||
751 | struct iw_request_info *info, | ||
752 | struct iw_param *rrq, | ||
753 | char *extra) | ||
754 | { | ||
755 | struct orinoco_private *priv = netdev_priv(dev); | ||
756 | |||
757 | rrq->value = priv->rts_thresh; | ||
758 | rrq->disabled = (rrq->value == 2347); | ||
759 | rrq->fixed = 1; | ||
760 | |||
761 | return 0; | ||
762 | } | ||
763 | |||
764 | static int orinoco_ioctl_setfrag(struct net_device *dev, | ||
765 | struct iw_request_info *info, | ||
766 | struct iw_param *frq, | ||
767 | char *extra) | ||
768 | { | ||
769 | struct orinoco_private *priv = netdev_priv(dev); | ||
770 | int err = -EINPROGRESS; /* Call commit handler */ | ||
771 | unsigned long flags; | ||
772 | |||
773 | if (orinoco_lock(priv, &flags) != 0) | ||
774 | return -EBUSY; | ||
775 | |||
776 | if (priv->has_mwo) { | ||
777 | if (frq->disabled) | ||
778 | priv->mwo_robust = 0; | ||
779 | else { | ||
780 | if (frq->fixed) | ||
781 | printk(KERN_WARNING "%s: Fixed fragmentation " | ||
782 | "is not supported on this firmware. " | ||
783 | "Using MWO robust instead.\n", | ||
784 | dev->name); | ||
785 | priv->mwo_robust = 1; | ||
786 | } | ||
787 | } else { | ||
788 | if (frq->disabled) | ||
789 | priv->frag_thresh = 2346; | ||
790 | else { | ||
791 | if ((frq->value < 256) || (frq->value > 2346)) | ||
792 | err = -EINVAL; | ||
793 | else | ||
794 | /* must be even */ | ||
795 | priv->frag_thresh = frq->value & ~0x1; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | orinoco_unlock(priv, &flags); | ||
800 | |||
801 | return err; | ||
802 | } | ||
803 | |||
804 | static int orinoco_ioctl_getfrag(struct net_device *dev, | ||
805 | struct iw_request_info *info, | ||
806 | struct iw_param *frq, | ||
807 | char *extra) | ||
808 | { | ||
809 | struct orinoco_private *priv = netdev_priv(dev); | ||
810 | hermes_t *hw = &priv->hw; | ||
811 | int err; | ||
812 | u16 val; | ||
813 | unsigned long flags; | ||
814 | |||
815 | if (orinoco_lock(priv, &flags) != 0) | ||
816 | return -EBUSY; | ||
817 | |||
818 | if (priv->has_mwo) { | ||
819 | err = hermes_read_wordrec(hw, USER_BAP, | ||
820 | HERMES_RID_CNFMWOROBUST_AGERE, | ||
821 | &val); | ||
822 | if (err) | ||
823 | val = 0; | ||
824 | |||
825 | frq->value = val ? 2347 : 0; | ||
826 | frq->disabled = !val; | ||
827 | frq->fixed = 0; | ||
828 | } else { | ||
829 | err = hermes_read_wordrec(hw, USER_BAP, | ||
830 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | ||
831 | &val); | ||
832 | if (err) | ||
833 | val = 0; | ||
834 | |||
835 | frq->value = val; | ||
836 | frq->disabled = (val >= 2346); | ||
837 | frq->fixed = 1; | ||
838 | } | ||
839 | |||
840 | orinoco_unlock(priv, &flags); | ||
841 | |||
842 | return err; | ||
843 | } | ||
844 | |||
845 | static int orinoco_ioctl_setrate(struct net_device *dev, | ||
846 | struct iw_request_info *info, | ||
847 | struct iw_param *rrq, | ||
848 | char *extra) | ||
849 | { | ||
850 | struct orinoco_private *priv = netdev_priv(dev); | ||
851 | int ratemode; | ||
852 | int bitrate; /* 100s of kilobits */ | ||
853 | unsigned long flags; | ||
854 | |||
855 | /* As the user space doesn't know our highest rate, it uses -1 | ||
856 | * to ask us to set the highest rate. Test it using "iwconfig | ||
857 | * ethX rate auto" - Jean II */ | ||
858 | if (rrq->value == -1) | ||
859 | bitrate = 110; | ||
860 | else { | ||
861 | if (rrq->value % 100000) | ||
862 | return -EINVAL; | ||
863 | bitrate = rrq->value / 100000; | ||
864 | } | ||
865 | |||
866 | ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); | ||
867 | |||
868 | if (ratemode == -1) | ||
869 | return -EINVAL; | ||
870 | |||
871 | if (orinoco_lock(priv, &flags) != 0) | ||
872 | return -EBUSY; | ||
873 | priv->bitratemode = ratemode; | ||
874 | orinoco_unlock(priv, &flags); | ||
875 | |||
876 | return -EINPROGRESS; | ||
877 | } | ||
878 | |||
879 | static int orinoco_ioctl_getrate(struct net_device *dev, | ||
880 | struct iw_request_info *info, | ||
881 | struct iw_param *rrq, | ||
882 | char *extra) | ||
883 | { | ||
884 | struct orinoco_private *priv = netdev_priv(dev); | ||
885 | int err = 0; | ||
886 | int bitrate, automatic; | ||
887 | unsigned long flags; | ||
888 | |||
889 | if (orinoco_lock(priv, &flags) != 0) | ||
890 | return -EBUSY; | ||
891 | |||
892 | orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); | ||
893 | |||
894 | /* If the interface is running we try to find more about the | ||
895 | current mode */ | ||
896 | if (netif_running(dev)) | ||
897 | err = orinoco_hw_get_act_bitrate(priv, &bitrate); | ||
898 | |||
899 | orinoco_unlock(priv, &flags); | ||
900 | |||
901 | rrq->value = bitrate; | ||
902 | rrq->fixed = !automatic; | ||
903 | rrq->disabled = 0; | ||
904 | |||
905 | return err; | ||
906 | } | ||
907 | |||
908 | static int orinoco_ioctl_setpower(struct net_device *dev, | ||
909 | struct iw_request_info *info, | ||
910 | struct iw_param *prq, | ||
911 | char *extra) | ||
912 | { | ||
913 | struct orinoco_private *priv = netdev_priv(dev); | ||
914 | int err = -EINPROGRESS; /* Call commit handler */ | ||
915 | unsigned long flags; | ||
916 | |||
917 | if (orinoco_lock(priv, &flags) != 0) | ||
918 | return -EBUSY; | ||
919 | |||
920 | if (prq->disabled) { | ||
921 | priv->pm_on = 0; | ||
922 | } else { | ||
923 | switch (prq->flags & IW_POWER_MODE) { | ||
924 | case IW_POWER_UNICAST_R: | ||
925 | priv->pm_mcast = 0; | ||
926 | priv->pm_on = 1; | ||
927 | break; | ||
928 | case IW_POWER_ALL_R: | ||
929 | priv->pm_mcast = 1; | ||
930 | priv->pm_on = 1; | ||
931 | break; | ||
932 | case IW_POWER_ON: | ||
933 | /* No flags : but we may have a value - Jean II */ | ||
934 | break; | ||
935 | default: | ||
936 | err = -EINVAL; | ||
937 | goto out; | ||
938 | } | ||
939 | |||
940 | if (prq->flags & IW_POWER_TIMEOUT) { | ||
941 | priv->pm_on = 1; | ||
942 | priv->pm_timeout = prq->value / 1000; | ||
943 | } | ||
944 | if (prq->flags & IW_POWER_PERIOD) { | ||
945 | priv->pm_on = 1; | ||
946 | priv->pm_period = prq->value / 1000; | ||
947 | } | ||
948 | /* It's valid to not have a value if we are just toggling | ||
949 | * the flags... Jean II */ | ||
950 | if (!priv->pm_on) { | ||
951 | err = -EINVAL; | ||
952 | goto out; | ||
953 | } | ||
954 | } | ||
955 | |||
956 | out: | ||
957 | orinoco_unlock(priv, &flags); | ||
958 | |||
959 | return err; | ||
960 | } | ||
961 | |||
962 | static int orinoco_ioctl_getpower(struct net_device *dev, | ||
963 | struct iw_request_info *info, | ||
964 | struct iw_param *prq, | ||
965 | char *extra) | ||
966 | { | ||
967 | struct orinoco_private *priv = netdev_priv(dev); | ||
968 | hermes_t *hw = &priv->hw; | ||
969 | int err = 0; | ||
970 | u16 enable, period, timeout, mcast; | ||
971 | unsigned long flags; | ||
972 | |||
973 | if (orinoco_lock(priv, &flags) != 0) | ||
974 | return -EBUSY; | ||
975 | |||
976 | err = hermes_read_wordrec(hw, USER_BAP, | ||
977 | HERMES_RID_CNFPMENABLED, &enable); | ||
978 | if (err) | ||
979 | goto out; | ||
980 | |||
981 | err = hermes_read_wordrec(hw, USER_BAP, | ||
982 | HERMES_RID_CNFMAXSLEEPDURATION, &period); | ||
983 | if (err) | ||
984 | goto out; | ||
985 | |||
986 | err = hermes_read_wordrec(hw, USER_BAP, | ||
987 | HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); | ||
988 | if (err) | ||
989 | goto out; | ||
990 | |||
991 | err = hermes_read_wordrec(hw, USER_BAP, | ||
992 | HERMES_RID_CNFMULTICASTRECEIVE, &mcast); | ||
993 | if (err) | ||
994 | goto out; | ||
995 | |||
996 | prq->disabled = !enable; | ||
997 | /* Note : by default, display the period */ | ||
998 | if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
999 | prq->flags = IW_POWER_TIMEOUT; | ||
1000 | prq->value = timeout * 1000; | ||
1001 | } else { | ||
1002 | prq->flags = IW_POWER_PERIOD; | ||
1003 | prq->value = period * 1000; | ||
1004 | } | ||
1005 | if (mcast) | ||
1006 | prq->flags |= IW_POWER_ALL_R; | ||
1007 | else | ||
1008 | prq->flags |= IW_POWER_UNICAST_R; | ||
1009 | |||
1010 | out: | ||
1011 | orinoco_unlock(priv, &flags); | ||
1012 | |||
1013 | return err; | ||
1014 | } | ||
1015 | |||
1016 | static int orinoco_ioctl_set_encodeext(struct net_device *dev, | ||
1017 | struct iw_request_info *info, | ||
1018 | union iwreq_data *wrqu, | ||
1019 | char *extra) | ||
1020 | { | ||
1021 | struct orinoco_private *priv = netdev_priv(dev); | ||
1022 | struct iw_point *encoding = &wrqu->encoding; | ||
1023 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
1024 | int idx, alg = ext->alg, set_key = 1; | ||
1025 | unsigned long flags; | ||
1026 | int err = -EINVAL; | ||
1027 | u16 key_len; | ||
1028 | |||
1029 | if (orinoco_lock(priv, &flags) != 0) | ||
1030 | return -EBUSY; | ||
1031 | |||
1032 | /* Determine and validate the key index */ | ||
1033 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
1034 | if (idx) { | ||
1035 | if ((idx < 1) || (idx > 4)) | ||
1036 | goto out; | ||
1037 | idx--; | ||
1038 | } else | ||
1039 | idx = priv->tx_key; | ||
1040 | |||
1041 | if (encoding->flags & IW_ENCODE_DISABLED) | ||
1042 | alg = IW_ENCODE_ALG_NONE; | ||
1043 | |||
1044 | if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { | ||
1045 | /* Clear any TKIP TX key we had */ | ||
1046 | (void) orinoco_clear_tkip_key(priv, priv->tx_key); | ||
1047 | } | ||
1048 | |||
1049 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
1050 | priv->tx_key = idx; | ||
1051 | set_key = ((alg == IW_ENCODE_ALG_TKIP) || | ||
1052 | (ext->key_len > 0)) ? 1 : 0; | ||
1053 | } | ||
1054 | |||
1055 | if (set_key) { | ||
1056 | /* Set the requested key first */ | ||
1057 | switch (alg) { | ||
1058 | case IW_ENCODE_ALG_NONE: | ||
1059 | priv->encode_alg = alg; | ||
1060 | priv->keys[idx].len = 0; | ||
1061 | break; | ||
1062 | |||
1063 | case IW_ENCODE_ALG_WEP: | ||
1064 | if (ext->key_len > SMALL_KEY_SIZE) | ||
1065 | key_len = LARGE_KEY_SIZE; | ||
1066 | else if (ext->key_len > 0) | ||
1067 | key_len = SMALL_KEY_SIZE; | ||
1068 | else | ||
1069 | goto out; | ||
1070 | |||
1071 | priv->encode_alg = alg; | ||
1072 | priv->keys[idx].len = cpu_to_le16(key_len); | ||
1073 | |||
1074 | key_len = min(ext->key_len, key_len); | ||
1075 | |||
1076 | memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE); | ||
1077 | memcpy(priv->keys[idx].data, ext->key, key_len); | ||
1078 | break; | ||
1079 | |||
1080 | case IW_ENCODE_ALG_TKIP: | ||
1081 | { | ||
1082 | hermes_t *hw = &priv->hw; | ||
1083 | u8 *tkip_iv = NULL; | ||
1084 | |||
1085 | if (!priv->has_wpa || | ||
1086 | (ext->key_len > sizeof(priv->tkip_key[0]))) | ||
1087 | goto out; | ||
1088 | |||
1089 | priv->encode_alg = alg; | ||
1090 | memset(&priv->tkip_key[idx], 0, | ||
1091 | sizeof(priv->tkip_key[idx])); | ||
1092 | memcpy(&priv->tkip_key[idx], ext->key, ext->key_len); | ||
1093 | |||
1094 | if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) | ||
1095 | tkip_iv = &ext->rx_seq[0]; | ||
1096 | |||
1097 | err = __orinoco_hw_set_tkip_key(hw, idx, | ||
1098 | ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, | ||
1099 | (u8 *) &priv->tkip_key[idx], | ||
1100 | tkip_iv, NULL); | ||
1101 | if (err) | ||
1102 | printk(KERN_ERR "%s: Error %d setting TKIP key" | ||
1103 | "\n", dev->name, err); | ||
1104 | |||
1105 | goto out; | ||
1106 | } | ||
1107 | default: | ||
1108 | goto out; | ||
1109 | } | ||
1110 | } | ||
1111 | err = -EINPROGRESS; | ||
1112 | out: | ||
1113 | orinoco_unlock(priv, &flags); | ||
1114 | |||
1115 | return err; | ||
1116 | } | ||
1117 | |||
1118 | static int orinoco_ioctl_get_encodeext(struct net_device *dev, | ||
1119 | struct iw_request_info *info, | ||
1120 | union iwreq_data *wrqu, | ||
1121 | char *extra) | ||
1122 | { | ||
1123 | struct orinoco_private *priv = netdev_priv(dev); | ||
1124 | struct iw_point *encoding = &wrqu->encoding; | ||
1125 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
1126 | int idx, max_key_len; | ||
1127 | unsigned long flags; | ||
1128 | int err; | ||
1129 | |||
1130 | if (orinoco_lock(priv, &flags) != 0) | ||
1131 | return -EBUSY; | ||
1132 | |||
1133 | err = -EINVAL; | ||
1134 | max_key_len = encoding->length - sizeof(*ext); | ||
1135 | if (max_key_len < 0) | ||
1136 | goto out; | ||
1137 | |||
1138 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
1139 | if (idx) { | ||
1140 | if ((idx < 1) || (idx > 4)) | ||
1141 | goto out; | ||
1142 | idx--; | ||
1143 | } else | ||
1144 | idx = priv->tx_key; | ||
1145 | |||
1146 | encoding->flags = idx + 1; | ||
1147 | memset(ext, 0, sizeof(*ext)); | ||
1148 | |||
1149 | ext->alg = priv->encode_alg; | ||
1150 | switch (priv->encode_alg) { | ||
1151 | case IW_ENCODE_ALG_NONE: | ||
1152 | ext->key_len = 0; | ||
1153 | encoding->flags |= IW_ENCODE_DISABLED; | ||
1154 | break; | ||
1155 | case IW_ENCODE_ALG_WEP: | ||
1156 | ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len), | ||
1157 | max_key_len); | ||
1158 | memcpy(ext->key, priv->keys[idx].data, ext->key_len); | ||
1159 | encoding->flags |= IW_ENCODE_ENABLED; | ||
1160 | break; | ||
1161 | case IW_ENCODE_ALG_TKIP: | ||
1162 | ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key), | ||
1163 | max_key_len); | ||
1164 | memcpy(ext->key, &priv->tkip_key[idx], ext->key_len); | ||
1165 | encoding->flags |= IW_ENCODE_ENABLED; | ||
1166 | break; | ||
1167 | } | ||
1168 | |||
1169 | err = 0; | ||
1170 | out: | ||
1171 | orinoco_unlock(priv, &flags); | ||
1172 | |||
1173 | return err; | ||
1174 | } | ||
1175 | |||
1176 | static int orinoco_ioctl_set_auth(struct net_device *dev, | ||
1177 | struct iw_request_info *info, | ||
1178 | union iwreq_data *wrqu, char *extra) | ||
1179 | { | ||
1180 | struct orinoco_private *priv = netdev_priv(dev); | ||
1181 | hermes_t *hw = &priv->hw; | ||
1182 | struct iw_param *param = &wrqu->param; | ||
1183 | unsigned long flags; | ||
1184 | int ret = -EINPROGRESS; | ||
1185 | |||
1186 | if (orinoco_lock(priv, &flags) != 0) | ||
1187 | return -EBUSY; | ||
1188 | |||
1189 | switch (param->flags & IW_AUTH_INDEX) { | ||
1190 | case IW_AUTH_WPA_VERSION: | ||
1191 | case IW_AUTH_CIPHER_PAIRWISE: | ||
1192 | case IW_AUTH_CIPHER_GROUP: | ||
1193 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | ||
1194 | case IW_AUTH_PRIVACY_INVOKED: | ||
1195 | case IW_AUTH_DROP_UNENCRYPTED: | ||
1196 | /* | ||
1197 | * orinoco does not use these parameters | ||
1198 | */ | ||
1199 | break; | ||
1200 | |||
1201 | case IW_AUTH_KEY_MGMT: | ||
1202 | /* wl_lkm implies value 2 == PSK for Hermes I | ||
1203 | * which ties in with WEXT | ||
1204 | * no other hints tho :( | ||
1205 | */ | ||
1206 | priv->key_mgmt = param->value; | ||
1207 | break; | ||
1208 | |||
1209 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
1210 | /* When countermeasures are enabled, shut down the | ||
1211 | * card; when disabled, re-enable the card. This must | ||
1212 | * take effect immediately. | ||
1213 | * | ||
1214 | * TODO: Make sure that the EAPOL message is getting | ||
1215 | * out before card disabled | ||
1216 | */ | ||
1217 | if (param->value) { | ||
1218 | priv->tkip_cm_active = 1; | ||
1219 | ret = hermes_enable_port(hw, 0); | ||
1220 | } else { | ||
1221 | priv->tkip_cm_active = 0; | ||
1222 | ret = hermes_disable_port(hw, 0); | ||
1223 | } | ||
1224 | break; | ||
1225 | |||
1226 | case IW_AUTH_80211_AUTH_ALG: | ||
1227 | if (param->value & IW_AUTH_ALG_SHARED_KEY) | ||
1228 | priv->wep_restrict = 1; | ||
1229 | else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) | ||
1230 | priv->wep_restrict = 0; | ||
1231 | else | ||
1232 | ret = -EINVAL; | ||
1233 | break; | ||
1234 | |||
1235 | case IW_AUTH_WPA_ENABLED: | ||
1236 | if (priv->has_wpa) { | ||
1237 | priv->wpa_enabled = param->value ? 1 : 0; | ||
1238 | } else { | ||
1239 | if (param->value) | ||
1240 | ret = -EOPNOTSUPP; | ||
1241 | /* else silently accept disable of WPA */ | ||
1242 | priv->wpa_enabled = 0; | ||
1243 | } | ||
1244 | break; | ||
1245 | |||
1246 | default: | ||
1247 | ret = -EOPNOTSUPP; | ||
1248 | } | ||
1249 | |||
1250 | orinoco_unlock(priv, &flags); | ||
1251 | return ret; | ||
1252 | } | ||
1253 | |||
1254 | static int orinoco_ioctl_get_auth(struct net_device *dev, | ||
1255 | struct iw_request_info *info, | ||
1256 | union iwreq_data *wrqu, char *extra) | ||
1257 | { | ||
1258 | struct orinoco_private *priv = netdev_priv(dev); | ||
1259 | struct iw_param *param = &wrqu->param; | ||
1260 | unsigned long flags; | ||
1261 | int ret = 0; | ||
1262 | |||
1263 | if (orinoco_lock(priv, &flags) != 0) | ||
1264 | return -EBUSY; | ||
1265 | |||
1266 | switch (param->flags & IW_AUTH_INDEX) { | ||
1267 | case IW_AUTH_KEY_MGMT: | ||
1268 | param->value = priv->key_mgmt; | ||
1269 | break; | ||
1270 | |||
1271 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
1272 | param->value = priv->tkip_cm_active; | ||
1273 | break; | ||
1274 | |||
1275 | case IW_AUTH_80211_AUTH_ALG: | ||
1276 | if (priv->wep_restrict) | ||
1277 | param->value = IW_AUTH_ALG_SHARED_KEY; | ||
1278 | else | ||
1279 | param->value = IW_AUTH_ALG_OPEN_SYSTEM; | ||
1280 | break; | ||
1281 | |||
1282 | case IW_AUTH_WPA_ENABLED: | ||
1283 | param->value = priv->wpa_enabled; | ||
1284 | break; | ||
1285 | |||
1286 | default: | ||
1287 | ret = -EOPNOTSUPP; | ||
1288 | } | ||
1289 | |||
1290 | orinoco_unlock(priv, &flags); | ||
1291 | return ret; | ||
1292 | } | ||
1293 | |||
1294 | static int orinoco_ioctl_set_genie(struct net_device *dev, | ||
1295 | struct iw_request_info *info, | ||
1296 | union iwreq_data *wrqu, char *extra) | ||
1297 | { | ||
1298 | struct orinoco_private *priv = netdev_priv(dev); | ||
1299 | u8 *buf; | ||
1300 | unsigned long flags; | ||
1301 | |||
1302 | /* cut off at IEEE80211_MAX_DATA_LEN */ | ||
1303 | if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || | ||
1304 | (wrqu->data.length && (extra == NULL))) | ||
1305 | return -EINVAL; | ||
1306 | |||
1307 | if (wrqu->data.length) { | ||
1308 | buf = kmalloc(wrqu->data.length, GFP_KERNEL); | ||
1309 | if (buf == NULL) | ||
1310 | return -ENOMEM; | ||
1311 | |||
1312 | memcpy(buf, extra, wrqu->data.length); | ||
1313 | } else | ||
1314 | buf = NULL; | ||
1315 | |||
1316 | if (orinoco_lock(priv, &flags) != 0) { | ||
1317 | kfree(buf); | ||
1318 | return -EBUSY; | ||
1319 | } | ||
1320 | |||
1321 | kfree(priv->wpa_ie); | ||
1322 | priv->wpa_ie = buf; | ||
1323 | priv->wpa_ie_len = wrqu->data.length; | ||
1324 | |||
1325 | if (priv->wpa_ie) { | ||
1326 | /* Looks like wl_lkm wants to check the auth alg, and | ||
1327 | * somehow pass it to the firmware. | ||
1328 | * Instead it just calls the key mgmt rid | ||
1329 | * - we do this in set auth. | ||
1330 | */ | ||
1331 | } | ||
1332 | |||
1333 | orinoco_unlock(priv, &flags); | ||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | static int orinoco_ioctl_get_genie(struct net_device *dev, | ||
1338 | struct iw_request_info *info, | ||
1339 | union iwreq_data *wrqu, char *extra) | ||
1340 | { | ||
1341 | struct orinoco_private *priv = netdev_priv(dev); | ||
1342 | unsigned long flags; | ||
1343 | int err = 0; | ||
1344 | |||
1345 | if (orinoco_lock(priv, &flags) != 0) | ||
1346 | return -EBUSY; | ||
1347 | |||
1348 | if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { | ||
1349 | wrqu->data.length = 0; | ||
1350 | goto out; | ||
1351 | } | ||
1352 | |||
1353 | if (wrqu->data.length < priv->wpa_ie_len) { | ||
1354 | err = -E2BIG; | ||
1355 | goto out; | ||
1356 | } | ||
1357 | |||
1358 | wrqu->data.length = priv->wpa_ie_len; | ||
1359 | memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); | ||
1360 | |||
1361 | out: | ||
1362 | orinoco_unlock(priv, &flags); | ||
1363 | return err; | ||
1364 | } | ||
1365 | |||
1366 | static int orinoco_ioctl_set_mlme(struct net_device *dev, | ||
1367 | struct iw_request_info *info, | ||
1368 | union iwreq_data *wrqu, char *extra) | ||
1369 | { | ||
1370 | struct orinoco_private *priv = netdev_priv(dev); | ||
1371 | hermes_t *hw = &priv->hw; | ||
1372 | struct iw_mlme *mlme = (struct iw_mlme *)extra; | ||
1373 | unsigned long flags; | ||
1374 | int ret = 0; | ||
1375 | |||
1376 | if (orinoco_lock(priv, &flags) != 0) | ||
1377 | return -EBUSY; | ||
1378 | |||
1379 | switch (mlme->cmd) { | ||
1380 | case IW_MLME_DEAUTH: | ||
1381 | /* silently ignore */ | ||
1382 | break; | ||
1383 | |||
1384 | case IW_MLME_DISASSOC: | ||
1385 | { | ||
1386 | struct { | ||
1387 | u8 addr[ETH_ALEN]; | ||
1388 | __le16 reason_code; | ||
1389 | } __attribute__ ((packed)) buf; | ||
1390 | |||
1391 | memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN); | ||
1392 | buf.reason_code = cpu_to_le16(mlme->reason_code); | ||
1393 | ret = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
1394 | HERMES_RID_CNFDISASSOCIATE, | ||
1395 | &buf); | ||
1396 | break; | ||
1397 | } | ||
1398 | default: | ||
1399 | ret = -EOPNOTSUPP; | ||
1400 | } | ||
1401 | |||
1402 | orinoco_unlock(priv, &flags); | ||
1403 | return ret; | ||
1404 | } | ||
1405 | |||
1406 | static int orinoco_ioctl_getretry(struct net_device *dev, | ||
1407 | struct iw_request_info *info, | ||
1408 | struct iw_param *rrq, | ||
1409 | char *extra) | ||
1410 | { | ||
1411 | struct orinoco_private *priv = netdev_priv(dev); | ||
1412 | hermes_t *hw = &priv->hw; | ||
1413 | int err = 0; | ||
1414 | u16 short_limit, long_limit, lifetime; | ||
1415 | unsigned long flags; | ||
1416 | |||
1417 | if (orinoco_lock(priv, &flags) != 0) | ||
1418 | return -EBUSY; | ||
1419 | |||
1420 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, | ||
1421 | &short_limit); | ||
1422 | if (err) | ||
1423 | goto out; | ||
1424 | |||
1425 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, | ||
1426 | &long_limit); | ||
1427 | if (err) | ||
1428 | goto out; | ||
1429 | |||
1430 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, | ||
1431 | &lifetime); | ||
1432 | if (err) | ||
1433 | goto out; | ||
1434 | |||
1435 | rrq->disabled = 0; /* Can't be disabled */ | ||
1436 | |||
1437 | /* Note : by default, display the retry number */ | ||
1438 | if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { | ||
1439 | rrq->flags = IW_RETRY_LIFETIME; | ||
1440 | rrq->value = lifetime * 1000; /* ??? */ | ||
1441 | } else { | ||
1442 | /* By default, display the min number */ | ||
1443 | if ((rrq->flags & IW_RETRY_LONG)) { | ||
1444 | rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; | ||
1445 | rrq->value = long_limit; | ||
1446 | } else { | ||
1447 | rrq->flags = IW_RETRY_LIMIT; | ||
1448 | rrq->value = short_limit; | ||
1449 | if (short_limit != long_limit) | ||
1450 | rrq->flags |= IW_RETRY_SHORT; | ||
1451 | } | ||
1452 | } | ||
1453 | |||
1454 | out: | ||
1455 | orinoco_unlock(priv, &flags); | ||
1456 | |||
1457 | return err; | ||
1458 | } | ||
1459 | |||
1460 | static int orinoco_ioctl_reset(struct net_device *dev, | ||
1461 | struct iw_request_info *info, | ||
1462 | void *wrqu, | ||
1463 | char *extra) | ||
1464 | { | ||
1465 | struct orinoco_private *priv = netdev_priv(dev); | ||
1466 | |||
1467 | if (!capable(CAP_NET_ADMIN)) | ||
1468 | return -EPERM; | ||
1469 | |||
1470 | if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { | ||
1471 | printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); | ||
1472 | |||
1473 | /* Firmware reset */ | ||
1474 | orinoco_reset(&priv->reset_work); | ||
1475 | } else { | ||
1476 | printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); | ||
1477 | |||
1478 | schedule_work(&priv->reset_work); | ||
1479 | } | ||
1480 | |||
1481 | return 0; | ||
1482 | } | ||
1483 | |||
1484 | static int orinoco_ioctl_setibssport(struct net_device *dev, | ||
1485 | struct iw_request_info *info, | ||
1486 | void *wrqu, | ||
1487 | char *extra) | ||
1488 | |||
1489 | { | ||
1490 | struct orinoco_private *priv = netdev_priv(dev); | ||
1491 | int val = *((int *) extra); | ||
1492 | unsigned long flags; | ||
1493 | |||
1494 | if (orinoco_lock(priv, &flags) != 0) | ||
1495 | return -EBUSY; | ||
1496 | |||
1497 | priv->ibss_port = val ; | ||
1498 | |||
1499 | /* Actually update the mode we are using */ | ||
1500 | set_port_type(priv); | ||
1501 | |||
1502 | orinoco_unlock(priv, &flags); | ||
1503 | return -EINPROGRESS; /* Call commit handler */ | ||
1504 | } | ||
1505 | |||
1506 | static int orinoco_ioctl_getibssport(struct net_device *dev, | ||
1507 | struct iw_request_info *info, | ||
1508 | void *wrqu, | ||
1509 | char *extra) | ||
1510 | { | ||
1511 | struct orinoco_private *priv = netdev_priv(dev); | ||
1512 | int *val = (int *) extra; | ||
1513 | |||
1514 | *val = priv->ibss_port; | ||
1515 | return 0; | ||
1516 | } | ||
1517 | |||
1518 | static int orinoco_ioctl_setport3(struct net_device *dev, | ||
1519 | struct iw_request_info *info, | ||
1520 | void *wrqu, | ||
1521 | char *extra) | ||
1522 | { | ||
1523 | struct orinoco_private *priv = netdev_priv(dev); | ||
1524 | int val = *((int *) extra); | ||
1525 | int err = 0; | ||
1526 | unsigned long flags; | ||
1527 | |||
1528 | if (orinoco_lock(priv, &flags) != 0) | ||
1529 | return -EBUSY; | ||
1530 | |||
1531 | switch (val) { | ||
1532 | case 0: /* Try to do IEEE ad-hoc mode */ | ||
1533 | if (!priv->has_ibss) { | ||
1534 | err = -EINVAL; | ||
1535 | break; | ||
1536 | } | ||
1537 | priv->prefer_port3 = 0; | ||
1538 | |||
1539 | break; | ||
1540 | |||
1541 | case 1: /* Try to do Lucent proprietary ad-hoc mode */ | ||
1542 | if (!priv->has_port3) { | ||
1543 | err = -EINVAL; | ||
1544 | break; | ||
1545 | } | ||
1546 | priv->prefer_port3 = 1; | ||
1547 | break; | ||
1548 | |||
1549 | default: | ||
1550 | err = -EINVAL; | ||
1551 | } | ||
1552 | |||
1553 | if (!err) { | ||
1554 | /* Actually update the mode we are using */ | ||
1555 | set_port_type(priv); | ||
1556 | err = -EINPROGRESS; | ||
1557 | } | ||
1558 | |||
1559 | orinoco_unlock(priv, &flags); | ||
1560 | |||
1561 | return err; | ||
1562 | } | ||
1563 | |||
1564 | static int orinoco_ioctl_getport3(struct net_device *dev, | ||
1565 | struct iw_request_info *info, | ||
1566 | void *wrqu, | ||
1567 | char *extra) | ||
1568 | { | ||
1569 | struct orinoco_private *priv = netdev_priv(dev); | ||
1570 | int *val = (int *) extra; | ||
1571 | |||
1572 | *val = priv->prefer_port3; | ||
1573 | return 0; | ||
1574 | } | ||
1575 | |||
1576 | static int orinoco_ioctl_setpreamble(struct net_device *dev, | ||
1577 | struct iw_request_info *info, | ||
1578 | void *wrqu, | ||
1579 | char *extra) | ||
1580 | { | ||
1581 | struct orinoco_private *priv = netdev_priv(dev); | ||
1582 | unsigned long flags; | ||
1583 | int val; | ||
1584 | |||
1585 | if (!priv->has_preamble) | ||
1586 | return -EOPNOTSUPP; | ||
1587 | |||
1588 | /* 802.11b has recently defined some short preamble. | ||
1589 | * Basically, the Phy header has been reduced in size. | ||
1590 | * This increase performance, especially at high rates | ||
1591 | * (the preamble is transmitted at 1Mb/s), unfortunately | ||
1592 | * this give compatibility troubles... - Jean II */ | ||
1593 | val = *((int *) extra); | ||
1594 | |||
1595 | if (orinoco_lock(priv, &flags) != 0) | ||
1596 | return -EBUSY; | ||
1597 | |||
1598 | if (val) | ||
1599 | priv->preamble = 1; | ||
1600 | else | ||
1601 | priv->preamble = 0; | ||
1602 | |||
1603 | orinoco_unlock(priv, &flags); | ||
1604 | |||
1605 | return -EINPROGRESS; /* Call commit handler */ | ||
1606 | } | ||
1607 | |||
1608 | static int orinoco_ioctl_getpreamble(struct net_device *dev, | ||
1609 | struct iw_request_info *info, | ||
1610 | void *wrqu, | ||
1611 | char *extra) | ||
1612 | { | ||
1613 | struct orinoco_private *priv = netdev_priv(dev); | ||
1614 | int *val = (int *) extra; | ||
1615 | |||
1616 | if (!priv->has_preamble) | ||
1617 | return -EOPNOTSUPP; | ||
1618 | |||
1619 | *val = priv->preamble; | ||
1620 | return 0; | ||
1621 | } | ||
1622 | |||
1623 | /* ioctl interface to hermes_read_ltv() | ||
1624 | * To use with iwpriv, pass the RID as the token argument, e.g. | ||
1625 | * iwpriv get_rid [0xfc00] | ||
1626 | * At least Wireless Tools 25 is required to use iwpriv. | ||
1627 | * For Wireless Tools 25 and 26 append "dummy" are the end. */ | ||
1628 | static int orinoco_ioctl_getrid(struct net_device *dev, | ||
1629 | struct iw_request_info *info, | ||
1630 | struct iw_point *data, | ||
1631 | char *extra) | ||
1632 | { | ||
1633 | struct orinoco_private *priv = netdev_priv(dev); | ||
1634 | hermes_t *hw = &priv->hw; | ||
1635 | int rid = data->flags; | ||
1636 | u16 length; | ||
1637 | int err; | ||
1638 | unsigned long flags; | ||
1639 | |||
1640 | /* It's a "get" function, but we don't want users to access the | ||
1641 | * WEP key and other raw firmware data */ | ||
1642 | if (!capable(CAP_NET_ADMIN)) | ||
1643 | return -EPERM; | ||
1644 | |||
1645 | if (rid < 0xfc00 || rid > 0xffff) | ||
1646 | return -EINVAL; | ||
1647 | |||
1648 | if (orinoco_lock(priv, &flags) != 0) | ||
1649 | return -EBUSY; | ||
1650 | |||
1651 | err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, | ||
1652 | extra); | ||
1653 | if (err) | ||
1654 | goto out; | ||
1655 | |||
1656 | data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), | ||
1657 | MAX_RID_LEN); | ||
1658 | |||
1659 | out: | ||
1660 | orinoco_unlock(priv, &flags); | ||
1661 | return err; | ||
1662 | } | ||
1663 | |||
1664 | /* Trigger a scan (look for other cells in the vicinity) */ | ||
1665 | static int orinoco_ioctl_setscan(struct net_device *dev, | ||
1666 | struct iw_request_info *info, | ||
1667 | struct iw_point *srq, | ||
1668 | char *extra) | ||
1669 | { | ||
1670 | struct orinoco_private *priv = netdev_priv(dev); | ||
1671 | hermes_t *hw = &priv->hw; | ||
1672 | struct iw_scan_req *si = (struct iw_scan_req *) extra; | ||
1673 | int err = 0; | ||
1674 | unsigned long flags; | ||
1675 | |||
1676 | /* Note : you may have realised that, as this is a SET operation, | ||
1677 | * this is privileged and therefore a normal user can't | ||
1678 | * perform scanning. | ||
1679 | * This is not an error, while the device perform scanning, | ||
1680 | * traffic doesn't flow, so it's a perfect DoS... | ||
1681 | * Jean II */ | ||
1682 | |||
1683 | if (orinoco_lock(priv, &flags) != 0) | ||
1684 | return -EBUSY; | ||
1685 | |||
1686 | /* Scanning with port 0 disabled would fail */ | ||
1687 | if (!netif_running(dev)) { | ||
1688 | err = -ENETDOWN; | ||
1689 | goto out; | ||
1690 | } | ||
1691 | |||
1692 | /* In monitor mode, the scan results are always empty. | ||
1693 | * Probe responses are passed to the driver as received | ||
1694 | * frames and could be processed in software. */ | ||
1695 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
1696 | err = -EOPNOTSUPP; | ||
1697 | goto out; | ||
1698 | } | ||
1699 | |||
1700 | /* Note : because we don't lock out the irq handler, the way | ||
1701 | * we access scan variables in priv is critical. | ||
1702 | * o scan_inprogress : not touched by irq handler | ||
1703 | * o scan_mode : not touched by irq handler | ||
1704 | * Before modifying anything on those variables, please think hard ! | ||
1705 | * Jean II */ | ||
1706 | |||
1707 | /* Save flags */ | ||
1708 | priv->scan_mode = srq->flags; | ||
1709 | |||
1710 | /* Always trigger scanning, even if it's in progress. | ||
1711 | * This way, if the info frame get lost, we will recover somewhat | ||
1712 | * gracefully - Jean II */ | ||
1713 | |||
1714 | if (priv->has_hostscan) { | ||
1715 | switch (priv->firmware_type) { | ||
1716 | case FIRMWARE_TYPE_SYMBOL: | ||
1717 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1718 | HERMES_RID_CNFHOSTSCAN_SYMBOL, | ||
1719 | HERMES_HOSTSCAN_SYMBOL_ONCE | | ||
1720 | HERMES_HOSTSCAN_SYMBOL_BCAST); | ||
1721 | break; | ||
1722 | case FIRMWARE_TYPE_INTERSIL: { | ||
1723 | __le16 req[3]; | ||
1724 | |||
1725 | req[0] = cpu_to_le16(0x3fff); /* All channels */ | ||
1726 | req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ | ||
1727 | req[2] = 0; /* Any ESSID */ | ||
1728 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
1729 | HERMES_RID_CNFHOSTSCAN, &req); | ||
1730 | } | ||
1731 | break; | ||
1732 | case FIRMWARE_TYPE_AGERE: | ||
1733 | if (priv->scan_mode & IW_SCAN_THIS_ESSID) { | ||
1734 | struct hermes_idstring idbuf; | ||
1735 | size_t len = min(sizeof(idbuf.val), | ||
1736 | (size_t) si->essid_len); | ||
1737 | idbuf.len = cpu_to_le16(len); | ||
1738 | memcpy(idbuf.val, si->essid, len); | ||
1739 | |||
1740 | err = hermes_write_ltv(hw, USER_BAP, | ||
1741 | HERMES_RID_CNFSCANSSID_AGERE, | ||
1742 | HERMES_BYTES_TO_RECLEN(len + 2), | ||
1743 | &idbuf); | ||
1744 | } else | ||
1745 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1746 | HERMES_RID_CNFSCANSSID_AGERE, | ||
1747 | 0); /* Any ESSID */ | ||
1748 | if (err) | ||
1749 | break; | ||
1750 | |||
1751 | if (priv->has_ext_scan) { | ||
1752 | /* Clear scan results at the start of | ||
1753 | * an extended scan */ | ||
1754 | orinoco_clear_scan_results(priv, | ||
1755 | msecs_to_jiffies(15000)); | ||
1756 | |||
1757 | /* TODO: Is this available on older firmware? | ||
1758 | * Can we use it to scan specific channels | ||
1759 | * for IW_SCAN_THIS_FREQ? */ | ||
1760 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1761 | HERMES_RID_CNFSCANCHANNELS2GHZ, | ||
1762 | 0x7FFF); | ||
1763 | if (err) | ||
1764 | goto out; | ||
1765 | |||
1766 | err = hermes_inquire(hw, | ||
1767 | HERMES_INQ_CHANNELINFO); | ||
1768 | } else | ||
1769 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
1770 | break; | ||
1771 | } | ||
1772 | } else | ||
1773 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
1774 | |||
1775 | /* One more client */ | ||
1776 | if (!err) | ||
1777 | priv->scan_inprogress = 1; | ||
1778 | |||
1779 | out: | ||
1780 | orinoco_unlock(priv, &flags); | ||
1781 | return err; | ||
1782 | } | ||
1783 | |||
1784 | #define MAX_CUSTOM_LEN 64 | ||
1785 | |||
1786 | /* Translate scan data returned from the card to a card independant | ||
1787 | * format that the Wireless Tools will understand - Jean II */ | ||
1788 | static inline char *orinoco_translate_scan(struct net_device *dev, | ||
1789 | struct iw_request_info *info, | ||
1790 | char *current_ev, | ||
1791 | char *end_buf, | ||
1792 | union hermes_scan_info *bss, | ||
1793 | unsigned long last_scanned) | ||
1794 | { | ||
1795 | struct orinoco_private *priv = netdev_priv(dev); | ||
1796 | u16 capabilities; | ||
1797 | u16 channel; | ||
1798 | struct iw_event iwe; /* Temporary buffer */ | ||
1799 | char custom[MAX_CUSTOM_LEN]; | ||
1800 | |||
1801 | memset(&iwe, 0, sizeof(iwe)); | ||
1802 | |||
1803 | /* First entry *MUST* be the AP MAC address */ | ||
1804 | iwe.cmd = SIOCGIWAP; | ||
1805 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
1806 | memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); | ||
1807 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1808 | &iwe, IW_EV_ADDR_LEN); | ||
1809 | |||
1810 | /* Other entries will be displayed in the order we give them */ | ||
1811 | |||
1812 | /* Add the ESSID */ | ||
1813 | iwe.u.data.length = le16_to_cpu(bss->a.essid_len); | ||
1814 | if (iwe.u.data.length > 32) | ||
1815 | iwe.u.data.length = 32; | ||
1816 | iwe.cmd = SIOCGIWESSID; | ||
1817 | iwe.u.data.flags = 1; | ||
1818 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1819 | &iwe, bss->a.essid); | ||
1820 | |||
1821 | /* Add mode */ | ||
1822 | iwe.cmd = SIOCGIWMODE; | ||
1823 | capabilities = le16_to_cpu(bss->a.capabilities); | ||
1824 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
1825 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
1826 | iwe.u.mode = IW_MODE_MASTER; | ||
1827 | else | ||
1828 | iwe.u.mode = IW_MODE_ADHOC; | ||
1829 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1830 | &iwe, IW_EV_UINT_LEN); | ||
1831 | } | ||
1832 | |||
1833 | channel = bss->s.channel; | ||
1834 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
1835 | /* Add channel and frequency */ | ||
1836 | iwe.cmd = SIOCGIWFREQ; | ||
1837 | iwe.u.freq.m = channel; | ||
1838 | iwe.u.freq.e = 0; | ||
1839 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1840 | &iwe, IW_EV_FREQ_LEN); | ||
1841 | |||
1842 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
1843 | iwe.u.freq.e = 1; | ||
1844 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1845 | &iwe, IW_EV_FREQ_LEN); | ||
1846 | } | ||
1847 | |||
1848 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
1849 | iwe.cmd = IWEVQUAL; | ||
1850 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
1851 | iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; | ||
1852 | iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; | ||
1853 | /* Wireless tools prior to 27.pre22 will show link quality | ||
1854 | * anyway, so we provide a reasonable value. */ | ||
1855 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
1856 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
1857 | else | ||
1858 | iwe.u.qual.qual = 0; | ||
1859 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1860 | &iwe, IW_EV_QUAL_LEN); | ||
1861 | |||
1862 | /* Add encryption capability */ | ||
1863 | iwe.cmd = SIOCGIWENCODE; | ||
1864 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
1865 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
1866 | else | ||
1867 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
1868 | iwe.u.data.length = 0; | ||
1869 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1870 | &iwe, NULL); | ||
1871 | |||
1872 | /* Bit rate is not available in Lucent/Agere firmwares */ | ||
1873 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | ||
1874 | char *current_val = current_ev + iwe_stream_lcp_len(info); | ||
1875 | int i; | ||
1876 | int step; | ||
1877 | |||
1878 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) | ||
1879 | step = 2; | ||
1880 | else | ||
1881 | step = 1; | ||
1882 | |||
1883 | iwe.cmd = SIOCGIWRATE; | ||
1884 | /* Those two flags are ignored... */ | ||
1885 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
1886 | /* Max 10 values */ | ||
1887 | for (i = 0; i < 10; i += step) { | ||
1888 | /* NULL terminated */ | ||
1889 | if (bss->p.rates[i] == 0x0) | ||
1890 | break; | ||
1891 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
1892 | iwe.u.bitrate.value = | ||
1893 | ((bss->p.rates[i] & 0x7f) * 500000); | ||
1894 | current_val = iwe_stream_add_value(info, current_ev, | ||
1895 | current_val, | ||
1896 | end_buf, &iwe, | ||
1897 | IW_EV_PARAM_LEN); | ||
1898 | } | ||
1899 | /* Check if we added any event */ | ||
1900 | if ((current_val - current_ev) > iwe_stream_lcp_len(info)) | ||
1901 | current_ev = current_val; | ||
1902 | } | ||
1903 | |||
1904 | /* Beacon interval */ | ||
1905 | iwe.cmd = IWEVCUSTOM; | ||
1906 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1907 | "bcn_int=%d", | ||
1908 | le16_to_cpu(bss->a.beacon_interv)); | ||
1909 | if (iwe.u.data.length) | ||
1910 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1911 | &iwe, custom); | ||
1912 | |||
1913 | /* Capabilites */ | ||
1914 | iwe.cmd = IWEVCUSTOM; | ||
1915 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1916 | "capab=0x%04x", | ||
1917 | capabilities); | ||
1918 | if (iwe.u.data.length) | ||
1919 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1920 | &iwe, custom); | ||
1921 | |||
1922 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
1923 | * for given network. */ | ||
1924 | iwe.cmd = IWEVCUSTOM; | ||
1925 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1926 | " Last beacon: %dms ago", | ||
1927 | jiffies_to_msecs(jiffies - last_scanned)); | ||
1928 | if (iwe.u.data.length) | ||
1929 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1930 | &iwe, custom); | ||
1931 | |||
1932 | return current_ev; | ||
1933 | } | ||
1934 | |||
1935 | static inline char *orinoco_translate_ext_scan(struct net_device *dev, | ||
1936 | struct iw_request_info *info, | ||
1937 | char *current_ev, | ||
1938 | char *end_buf, | ||
1939 | struct agere_ext_scan_info *bss, | ||
1940 | unsigned long last_scanned) | ||
1941 | { | ||
1942 | u16 capabilities; | ||
1943 | u16 channel; | ||
1944 | struct iw_event iwe; /* Temporary buffer */ | ||
1945 | char custom[MAX_CUSTOM_LEN]; | ||
1946 | u8 *ie; | ||
1947 | |||
1948 | memset(&iwe, 0, sizeof(iwe)); | ||
1949 | |||
1950 | /* First entry *MUST* be the AP MAC address */ | ||
1951 | iwe.cmd = SIOCGIWAP; | ||
1952 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
1953 | memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); | ||
1954 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1955 | &iwe, IW_EV_ADDR_LEN); | ||
1956 | |||
1957 | /* Other entries will be displayed in the order we give them */ | ||
1958 | |||
1959 | /* Add the ESSID */ | ||
1960 | ie = bss->data; | ||
1961 | iwe.u.data.length = ie[1]; | ||
1962 | if (iwe.u.data.length) { | ||
1963 | if (iwe.u.data.length > 32) | ||
1964 | iwe.u.data.length = 32; | ||
1965 | iwe.cmd = SIOCGIWESSID; | ||
1966 | iwe.u.data.flags = 1; | ||
1967 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1968 | &iwe, &ie[2]); | ||
1969 | } | ||
1970 | |||
1971 | /* Add mode */ | ||
1972 | capabilities = le16_to_cpu(bss->capabilities); | ||
1973 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
1974 | iwe.cmd = SIOCGIWMODE; | ||
1975 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
1976 | iwe.u.mode = IW_MODE_MASTER; | ||
1977 | else | ||
1978 | iwe.u.mode = IW_MODE_ADHOC; | ||
1979 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1980 | &iwe, IW_EV_UINT_LEN); | ||
1981 | } | ||
1982 | |||
1983 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS); | ||
1984 | channel = ie ? ie[2] : 0; | ||
1985 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
1986 | /* Add channel and frequency */ | ||
1987 | iwe.cmd = SIOCGIWFREQ; | ||
1988 | iwe.u.freq.m = channel; | ||
1989 | iwe.u.freq.e = 0; | ||
1990 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1991 | &iwe, IW_EV_FREQ_LEN); | ||
1992 | |||
1993 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
1994 | iwe.u.freq.e = 1; | ||
1995 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1996 | &iwe, IW_EV_FREQ_LEN); | ||
1997 | } | ||
1998 | |||
1999 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
2000 | iwe.cmd = IWEVQUAL; | ||
2001 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
2002 | iwe.u.qual.level = bss->level - 0x95; | ||
2003 | iwe.u.qual.noise = bss->noise - 0x95; | ||
2004 | /* Wireless tools prior to 27.pre22 will show link quality | ||
2005 | * anyway, so we provide a reasonable value. */ | ||
2006 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
2007 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
2008 | else | ||
2009 | iwe.u.qual.qual = 0; | ||
2010 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
2011 | &iwe, IW_EV_QUAL_LEN); | ||
2012 | |||
2013 | /* Add encryption capability */ | ||
2014 | iwe.cmd = SIOCGIWENCODE; | ||
2015 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
2016 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
2017 | else | ||
2018 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
2019 | iwe.u.data.length = 0; | ||
2020 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2021 | &iwe, NULL); | ||
2022 | |||
2023 | /* WPA IE */ | ||
2024 | ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); | ||
2025 | if (ie) { | ||
2026 | iwe.cmd = IWEVGENIE; | ||
2027 | iwe.u.data.length = ie[1] + 2; | ||
2028 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2029 | &iwe, ie); | ||
2030 | } | ||
2031 | |||
2032 | /* RSN IE */ | ||
2033 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN); | ||
2034 | if (ie) { | ||
2035 | iwe.cmd = IWEVGENIE; | ||
2036 | iwe.u.data.length = ie[1] + 2; | ||
2037 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2038 | &iwe, ie); | ||
2039 | } | ||
2040 | |||
2041 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES); | ||
2042 | if (ie) { | ||
2043 | char *p = current_ev + iwe_stream_lcp_len(info); | ||
2044 | int i; | ||
2045 | |||
2046 | iwe.cmd = SIOCGIWRATE; | ||
2047 | /* Those two flags are ignored... */ | ||
2048 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
2049 | |||
2050 | for (i = 2; i < (ie[1] + 2); i++) { | ||
2051 | iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); | ||
2052 | p = iwe_stream_add_value(info, current_ev, p, end_buf, | ||
2053 | &iwe, IW_EV_PARAM_LEN); | ||
2054 | } | ||
2055 | /* Check if we added any event */ | ||
2056 | if (p > (current_ev + iwe_stream_lcp_len(info))) | ||
2057 | current_ev = p; | ||
2058 | } | ||
2059 | |||
2060 | /* Timestamp */ | ||
2061 | iwe.cmd = IWEVCUSTOM; | ||
2062 | iwe.u.data.length = | ||
2063 | snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx", | ||
2064 | (unsigned long long) le64_to_cpu(bss->timestamp)); | ||
2065 | if (iwe.u.data.length) | ||
2066 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2067 | &iwe, custom); | ||
2068 | |||
2069 | /* Beacon interval */ | ||
2070 | iwe.cmd = IWEVCUSTOM; | ||
2071 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2072 | "bcn_int=%d", | ||
2073 | le16_to_cpu(bss->beacon_interval)); | ||
2074 | if (iwe.u.data.length) | ||
2075 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2076 | &iwe, custom); | ||
2077 | |||
2078 | /* Capabilites */ | ||
2079 | iwe.cmd = IWEVCUSTOM; | ||
2080 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2081 | "capab=0x%04x", | ||
2082 | capabilities); | ||
2083 | if (iwe.u.data.length) | ||
2084 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2085 | &iwe, custom); | ||
2086 | |||
2087 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
2088 | * for given network. */ | ||
2089 | iwe.cmd = IWEVCUSTOM; | ||
2090 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2091 | " Last beacon: %dms ago", | ||
2092 | jiffies_to_msecs(jiffies - last_scanned)); | ||
2093 | if (iwe.u.data.length) | ||
2094 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2095 | &iwe, custom); | ||
2096 | |||
2097 | return current_ev; | ||
2098 | } | ||
2099 | |||
2100 | /* Return results of a scan */ | ||
2101 | static int orinoco_ioctl_getscan(struct net_device *dev, | ||
2102 | struct iw_request_info *info, | ||
2103 | struct iw_point *srq, | ||
2104 | char *extra) | ||
2105 | { | ||
2106 | struct orinoco_private *priv = netdev_priv(dev); | ||
2107 | int err = 0; | ||
2108 | unsigned long flags; | ||
2109 | char *current_ev = extra; | ||
2110 | |||
2111 | if (orinoco_lock(priv, &flags) != 0) | ||
2112 | return -EBUSY; | ||
2113 | |||
2114 | if (priv->scan_inprogress) { | ||
2115 | /* Important note : we don't want to block the caller | ||
2116 | * until results are ready for various reasons. | ||
2117 | * First, managing wait queues is complex and racy. | ||
2118 | * Second, we grab some rtnetlink lock before comming | ||
2119 | * here (in dev_ioctl()). | ||
2120 | * Third, we generate an Wireless Event, so the | ||
2121 | * caller can wait itself on that - Jean II */ | ||
2122 | err = -EAGAIN; | ||
2123 | goto out; | ||
2124 | } | ||
2125 | |||
2126 | if (priv->has_ext_scan) { | ||
2127 | struct xbss_element *bss; | ||
2128 | |||
2129 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
2130 | /* Translate this entry to WE format */ | ||
2131 | current_ev = | ||
2132 | orinoco_translate_ext_scan(dev, info, | ||
2133 | current_ev, | ||
2134 | extra + srq->length, | ||
2135 | &bss->bss, | ||
2136 | bss->last_scanned); | ||
2137 | |||
2138 | /* Check if there is space for one more entry */ | ||
2139 | if ((extra + srq->length - current_ev) | ||
2140 | <= IW_EV_ADDR_LEN) { | ||
2141 | /* Ask user space to try again with a | ||
2142 | * bigger buffer */ | ||
2143 | err = -E2BIG; | ||
2144 | goto out; | ||
2145 | } | ||
2146 | } | ||
2147 | |||
2148 | } else { | ||
2149 | struct bss_element *bss; | ||
2150 | |||
2151 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
2152 | /* Translate this entry to WE format */ | ||
2153 | current_ev = orinoco_translate_scan(dev, info, | ||
2154 | current_ev, | ||
2155 | extra + srq->length, | ||
2156 | &bss->bss, | ||
2157 | bss->last_scanned); | ||
2158 | |||
2159 | /* Check if there is space for one more entry */ | ||
2160 | if ((extra + srq->length - current_ev) | ||
2161 | <= IW_EV_ADDR_LEN) { | ||
2162 | /* Ask user space to try again with a | ||
2163 | * bigger buffer */ | ||
2164 | err = -E2BIG; | ||
2165 | goto out; | ||
2166 | } | ||
2167 | } | ||
2168 | } | ||
2169 | |||
2170 | srq->length = (current_ev - extra); | ||
2171 | srq->flags = (__u16) priv->scan_mode; | ||
2172 | |||
2173 | out: | ||
2174 | orinoco_unlock(priv, &flags); | ||
2175 | return err; | ||
2176 | } | ||
2177 | |||
2178 | /* Commit handler, called after set operations */ | ||
2179 | static int orinoco_ioctl_commit(struct net_device *dev, | ||
2180 | struct iw_request_info *info, | ||
2181 | void *wrqu, | ||
2182 | char *extra) | ||
2183 | { | ||
2184 | struct orinoco_private *priv = netdev_priv(dev); | ||
2185 | struct hermes *hw = &priv->hw; | ||
2186 | unsigned long flags; | ||
2187 | int err = 0; | ||
2188 | |||
2189 | if (!priv->open) | ||
2190 | return 0; | ||
2191 | |||
2192 | if (priv->broken_disableport) { | ||
2193 | orinoco_reset(&priv->reset_work); | ||
2194 | return 0; | ||
2195 | } | ||
2196 | |||
2197 | if (orinoco_lock(priv, &flags) != 0) | ||
2198 | return err; | ||
2199 | |||
2200 | err = hermes_disable_port(hw, 0); | ||
2201 | if (err) { | ||
2202 | printk(KERN_WARNING "%s: Unable to disable port " | ||
2203 | "while reconfiguring card\n", dev->name); | ||
2204 | priv->broken_disableport = 1; | ||
2205 | goto out; | ||
2206 | } | ||
2207 | |||
2208 | err = __orinoco_program_rids(dev); | ||
2209 | if (err) { | ||
2210 | printk(KERN_WARNING "%s: Unable to reconfigure card\n", | ||
2211 | dev->name); | ||
2212 | goto out; | ||
2213 | } | ||
2214 | |||
2215 | err = hermes_enable_port(hw, 0); | ||
2216 | if (err) { | ||
2217 | printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", | ||
2218 | dev->name); | ||
2219 | goto out; | ||
2220 | } | ||
2221 | |||
2222 | out: | ||
2223 | if (err) { | ||
2224 | printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); | ||
2225 | schedule_work(&priv->reset_work); | ||
2226 | err = 0; | ||
2227 | } | ||
2228 | |||
2229 | orinoco_unlock(priv, &flags); | ||
2230 | return err; | ||
2231 | } | ||
2232 | |||
2233 | static const struct iw_priv_args orinoco_privtab[] = { | ||
2234 | { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, | ||
2235 | { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, | ||
2236 | { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2237 | 0, "set_port3" }, | ||
2238 | { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2239 | "get_port3" }, | ||
2240 | { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2241 | 0, "set_preamble" }, | ||
2242 | { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2243 | "get_preamble" }, | ||
2244 | { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2245 | 0, "set_ibssport" }, | ||
2246 | { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2247 | "get_ibssport" }, | ||
2248 | { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, | ||
2249 | "get_rid" }, | ||
2250 | }; | ||
2251 | |||
2252 | |||
2253 | /* | ||
2254 | * Structures to export the Wireless Handlers | ||
2255 | */ | ||
2256 | |||
2257 | #define STD_IW_HANDLER(id, func) \ | ||
2258 | [IW_IOCTL_IDX(id)] = (iw_handler) func | ||
2259 | static const iw_handler orinoco_handler[] = { | ||
2260 | STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), | ||
2261 | STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname), | ||
2262 | STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), | ||
2263 | STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), | ||
2264 | STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode), | ||
2265 | STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode), | ||
2266 | STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), | ||
2267 | STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), | ||
2268 | STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange), | ||
2269 | STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), | ||
2270 | STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), | ||
2271 | STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), | ||
2272 | STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), | ||
2273 | STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), | ||
2274 | STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), | ||
2275 | STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan), | ||
2276 | STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan), | ||
2277 | STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), | ||
2278 | STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), | ||
2279 | STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick), | ||
2280 | STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick), | ||
2281 | STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), | ||
2282 | STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), | ||
2283 | STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), | ||
2284 | STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), | ||
2285 | STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), | ||
2286 | STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), | ||
2287 | STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), | ||
2288 | STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), | ||
2289 | STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), | ||
2290 | STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), | ||
2291 | STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), | ||
2292 | STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), | ||
2293 | STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), | ||
2294 | STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), | ||
2295 | STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), | ||
2296 | STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), | ||
2297 | STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), | ||
2298 | STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), | ||
2299 | }; | ||
2300 | |||
2301 | |||
2302 | /* | ||
2303 | Added typecasting since we no longer use iwreq_data -- Moustafa | ||
2304 | */ | ||
2305 | static const iw_handler orinoco_private_handler[] = { | ||
2306 | [0] = (iw_handler) orinoco_ioctl_reset, | ||
2307 | [1] = (iw_handler) orinoco_ioctl_reset, | ||
2308 | [2] = (iw_handler) orinoco_ioctl_setport3, | ||
2309 | [3] = (iw_handler) orinoco_ioctl_getport3, | ||
2310 | [4] = (iw_handler) orinoco_ioctl_setpreamble, | ||
2311 | [5] = (iw_handler) orinoco_ioctl_getpreamble, | ||
2312 | [6] = (iw_handler) orinoco_ioctl_setibssport, | ||
2313 | [7] = (iw_handler) orinoco_ioctl_getibssport, | ||
2314 | [9] = (iw_handler) orinoco_ioctl_getrid, | ||
2315 | }; | ||
2316 | |||
2317 | const struct iw_handler_def orinoco_handler_def = { | ||
2318 | .num_standard = ARRAY_SIZE(orinoco_handler), | ||
2319 | .num_private = ARRAY_SIZE(orinoco_private_handler), | ||
2320 | .num_private_args = ARRAY_SIZE(orinoco_privtab), | ||
2321 | .standard = orinoco_handler, | ||
2322 | .private = orinoco_private_handler, | ||
2323 | .private_args = orinoco_privtab, | ||
2324 | .get_wireless_stats = orinoco_get_wireless_stats, | ||
2325 | }; | ||