x86/amd-iommu: Add per IOMMU reference counting
[linux/fpc-iii.git] / drivers / net / wireless / ipw2x00 / libipw_wx.c
blob4d89f66f53b2b0233eaae018b969d7504c0ef53f
1 /******************************************************************************
3 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
5 Portions of this file are based on the WEP enablement code provided by the
6 Host AP project hostap-drivers v0.1.3
7 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
8 <j@w1.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
11 This program is free software; you can redistribute it and/or modify it
12 under the terms of version 2 of the GNU General Public License as
13 published by the Free Software Foundation.
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 more details.
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 The full GNU General Public License is included in this distribution in the
25 file called LICENSE.
27 Contact Information:
28 Intel Linux Wireless <ilw@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
31 ******************************************************************************/
33 #include <linux/kmod.h>
34 #include <linux/module.h>
35 #include <linux/jiffies.h>
37 #include <net/lib80211.h>
38 #include <linux/wireless.h>
40 #include "libipw.h"
42 static const char *libipw_modes[] = {
43 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
46 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
48 unsigned long end = jiffies;
50 if (end >= start)
51 return jiffies_to_msecs(end - start);
53 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
56 #define MAX_CUSTOM_LEN 64
57 static char *libipw_translate_scan(struct libipw_device *ieee,
58 char *start, char *stop,
59 struct libipw_network *network,
60 struct iw_request_info *info)
62 char custom[MAX_CUSTOM_LEN];
63 char *p;
64 struct iw_event iwe;
65 int i, j;
66 char *current_val; /* For rates */
67 u8 rate;
69 /* First entry *MUST* be the AP MAC address */
70 iwe.cmd = SIOCGIWAP;
71 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
72 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
73 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
75 /* Remaining entries will be displayed in the order we provide them */
77 /* Add the ESSID */
78 iwe.cmd = SIOCGIWESSID;
79 iwe.u.data.flags = 1;
80 iwe.u.data.length = min(network->ssid_len, (u8) 32);
81 start = iwe_stream_add_point(info, start, stop,
82 &iwe, network->ssid);
84 /* Add the protocol name */
85 iwe.cmd = SIOCGIWNAME;
86 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
87 libipw_modes[network->mode]);
88 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
90 /* Add mode */
91 iwe.cmd = SIOCGIWMODE;
92 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
93 if (network->capability & WLAN_CAPABILITY_ESS)
94 iwe.u.mode = IW_MODE_MASTER;
95 else
96 iwe.u.mode = IW_MODE_ADHOC;
98 start = iwe_stream_add_event(info, start, stop,
99 &iwe, IW_EV_UINT_LEN);
102 /* Add channel and frequency */
103 /* Note : userspace automatically computes channel using iwrange */
104 iwe.cmd = SIOCGIWFREQ;
105 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
106 iwe.u.freq.e = 6;
107 iwe.u.freq.i = 0;
108 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
110 /* Add encryption capability */
111 iwe.cmd = SIOCGIWENCODE;
112 if (network->capability & WLAN_CAPABILITY_PRIVACY)
113 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
114 else
115 iwe.u.data.flags = IW_ENCODE_DISABLED;
116 iwe.u.data.length = 0;
117 start = iwe_stream_add_point(info, start, stop,
118 &iwe, network->ssid);
120 /* Add basic and extended rates */
121 /* Rate : stuffing multiple values in a single event require a bit
122 * more of magic - Jean II */
123 current_val = start + iwe_stream_lcp_len(info);
124 iwe.cmd = SIOCGIWRATE;
125 /* Those two flags are ignored... */
126 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
128 for (i = 0, j = 0; i < network->rates_len;) {
129 if (j < network->rates_ex_len &&
130 ((network->rates_ex[j] & 0x7F) <
131 (network->rates[i] & 0x7F)))
132 rate = network->rates_ex[j++] & 0x7F;
133 else
134 rate = network->rates[i++] & 0x7F;
135 /* Bit rate given in 500 kb/s units (+ 0x80) */
136 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
137 /* Add new value to event */
138 current_val = iwe_stream_add_value(info, start, current_val,
139 stop, &iwe, IW_EV_PARAM_LEN);
141 for (; j < network->rates_ex_len; j++) {
142 rate = network->rates_ex[j] & 0x7F;
143 /* Bit rate given in 500 kb/s units (+ 0x80) */
144 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
145 /* Add new value to event */
146 current_val = iwe_stream_add_value(info, start, current_val,
147 stop, &iwe, IW_EV_PARAM_LEN);
149 /* Check if we added any rate */
150 if ((current_val - start) > iwe_stream_lcp_len(info))
151 start = current_val;
153 /* Add quality statistics */
154 iwe.cmd = IWEVQUAL;
155 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
156 IW_QUAL_NOISE_UPDATED;
158 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
159 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
160 IW_QUAL_LEVEL_INVALID;
161 iwe.u.qual.qual = 0;
162 } else {
163 if (ieee->perfect_rssi == ieee->worst_rssi)
164 iwe.u.qual.qual = 100;
165 else
166 iwe.u.qual.qual =
167 (100 *
168 (ieee->perfect_rssi - ieee->worst_rssi) *
169 (ieee->perfect_rssi - ieee->worst_rssi) -
170 (ieee->perfect_rssi - network->stats.rssi) *
171 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
172 62 * (ieee->perfect_rssi -
173 network->stats.rssi))) /
174 ((ieee->perfect_rssi -
175 ieee->worst_rssi) * (ieee->perfect_rssi -
176 ieee->worst_rssi));
177 if (iwe.u.qual.qual > 100)
178 iwe.u.qual.qual = 100;
179 else if (iwe.u.qual.qual < 1)
180 iwe.u.qual.qual = 0;
183 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
184 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
185 iwe.u.qual.noise = 0;
186 } else {
187 iwe.u.qual.noise = network->stats.noise;
190 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
191 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
192 iwe.u.qual.level = 0;
193 } else {
194 iwe.u.qual.level = network->stats.signal;
197 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
199 iwe.cmd = IWEVCUSTOM;
200 p = custom;
202 iwe.u.data.length = p - custom;
203 if (iwe.u.data.length)
204 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
206 memset(&iwe, 0, sizeof(iwe));
207 if (network->wpa_ie_len) {
208 char buf[MAX_WPA_IE_LEN];
209 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
210 iwe.cmd = IWEVGENIE;
211 iwe.u.data.length = network->wpa_ie_len;
212 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
215 memset(&iwe, 0, sizeof(iwe));
216 if (network->rsn_ie_len) {
217 char buf[MAX_WPA_IE_LEN];
218 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
219 iwe.cmd = IWEVGENIE;
220 iwe.u.data.length = network->rsn_ie_len;
221 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
224 /* Add EXTRA: Age to display seconds since last beacon/probe response
225 * for given network. */
226 iwe.cmd = IWEVCUSTOM;
227 p = custom;
228 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
229 " Last beacon: %ums ago",
230 elapsed_jiffies_msecs(network->last_scanned));
231 iwe.u.data.length = p - custom;
232 if (iwe.u.data.length)
233 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
235 /* Add spectrum management information */
236 iwe.cmd = -1;
237 p = custom;
238 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
240 if (libipw_get_channel_flags(ieee, network->channel) &
241 LIBIPW_CH_INVALID) {
242 iwe.cmd = IWEVCUSTOM;
243 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246 if (libipw_get_channel_flags(ieee, network->channel) &
247 LIBIPW_CH_RADAR_DETECT) {
248 iwe.cmd = IWEVCUSTOM;
249 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252 if (iwe.cmd == IWEVCUSTOM) {
253 iwe.u.data.length = p - custom;
254 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
257 return start;
260 #define SCAN_ITEM_SIZE 128
262 int libipw_wx_get_scan(struct libipw_device *ieee,
263 struct iw_request_info *info,
264 union iwreq_data *wrqu, char *extra)
266 struct libipw_network *network;
267 unsigned long flags;
268 int err = 0;
270 char *ev = extra;
271 char *stop = ev + wrqu->data.length;
272 int i = 0;
273 DECLARE_SSID_BUF(ssid);
275 LIBIPW_DEBUG_WX("Getting scan\n");
277 spin_lock_irqsave(&ieee->lock, flags);
279 list_for_each_entry(network, &ieee->network_list, list) {
280 i++;
281 if (stop - ev < SCAN_ITEM_SIZE) {
282 err = -E2BIG;
283 break;
286 if (ieee->scan_age == 0 ||
287 time_after(network->last_scanned + ieee->scan_age, jiffies))
288 ev = libipw_translate_scan(ieee, ev, stop, network,
289 info);
290 else {
291 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
292 "%pM)' due to age (%ums).\n",
293 print_ssid(ssid, network->ssid,
294 network->ssid_len),
295 network->bssid,
296 elapsed_jiffies_msecs(
297 network->last_scanned));
301 spin_unlock_irqrestore(&ieee->lock, flags);
303 wrqu->data.length = ev - extra;
304 wrqu->data.flags = 0;
306 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
308 return err;
311 int libipw_wx_set_encode(struct libipw_device *ieee,
312 struct iw_request_info *info,
313 union iwreq_data *wrqu, char *keybuf)
315 struct iw_point *erq = &(wrqu->encoding);
316 struct net_device *dev = ieee->dev;
317 struct libipw_security sec = {
318 .flags = 0
320 int i, key, key_provided, len;
321 struct lib80211_crypt_data **crypt;
322 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
323 DECLARE_SSID_BUF(ssid);
325 LIBIPW_DEBUG_WX("SET_ENCODE\n");
327 key = erq->flags & IW_ENCODE_INDEX;
328 if (key) {
329 if (key > WEP_KEYS)
330 return -EINVAL;
331 key--;
332 key_provided = 1;
333 } else {
334 key_provided = 0;
335 key = ieee->crypt_info.tx_keyidx;
338 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
339 "provided" : "default");
341 crypt = &ieee->crypt_info.crypt[key];
343 if (erq->flags & IW_ENCODE_DISABLED) {
344 if (key_provided && *crypt) {
345 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
346 key);
347 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
348 } else
349 LIBIPW_DEBUG_WX("Disabling encryption.\n");
351 /* Check all the keys to see if any are still configured,
352 * and if no key index was provided, de-init them all */
353 for (i = 0; i < WEP_KEYS; i++) {
354 if (ieee->crypt_info.crypt[i] != NULL) {
355 if (key_provided)
356 break;
357 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
358 &ieee->crypt_info.crypt[i]);
362 if (i == WEP_KEYS) {
363 sec.enabled = 0;
364 sec.encrypt = 0;
365 sec.level = SEC_LEVEL_0;
366 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
369 goto done;
372 sec.enabled = 1;
373 sec.encrypt = 1;
374 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
376 if (*crypt != NULL && (*crypt)->ops != NULL &&
377 strcmp((*crypt)->ops->name, "WEP") != 0) {
378 /* changing to use WEP; deinit previously used algorithm
379 * on this key */
380 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
383 if (*crypt == NULL && host_crypto) {
384 struct lib80211_crypt_data *new_crypt;
386 /* take WEP into use */
387 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
388 GFP_KERNEL);
389 if (new_crypt == NULL)
390 return -ENOMEM;
391 new_crypt->ops = lib80211_get_crypto_ops("WEP");
392 if (!new_crypt->ops) {
393 request_module("lib80211_crypt_wep");
394 new_crypt->ops = lib80211_get_crypto_ops("WEP");
397 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
398 new_crypt->priv = new_crypt->ops->init(key);
400 if (!new_crypt->ops || !new_crypt->priv) {
401 kfree(new_crypt);
402 new_crypt = NULL;
404 printk(KERN_WARNING "%s: could not initialize WEP: "
405 "load module lib80211_crypt_wep\n", dev->name);
406 return -EOPNOTSUPP;
408 *crypt = new_crypt;
411 /* If a new key was provided, set it up */
412 if (erq->length > 0) {
413 #ifdef CONFIG_LIBIPW_DEBUG
414 DECLARE_SSID_BUF(ssid);
415 #endif
417 len = erq->length <= 5 ? 5 : 13;
418 memcpy(sec.keys[key], keybuf, erq->length);
419 if (len > erq->length)
420 memset(sec.keys[key] + erq->length, 0,
421 len - erq->length);
422 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
423 key, print_ssid(ssid, sec.keys[key], len),
424 erq->length, len);
425 sec.key_sizes[key] = len;
426 if (*crypt)
427 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
428 (*crypt)->priv);
429 sec.flags |= (1 << key);
430 /* This ensures a key will be activated if no key is
431 * explicitly set */
432 if (key == sec.active_key)
433 sec.flags |= SEC_ACTIVE_KEY;
435 } else {
436 if (host_crypto) {
437 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
438 NULL, (*crypt)->priv);
439 if (len == 0) {
440 /* Set a default key of all 0 */
441 LIBIPW_DEBUG_WX("Setting key %d to all "
442 "zero.\n", key);
443 memset(sec.keys[key], 0, 13);
444 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
445 (*crypt)->priv);
446 sec.key_sizes[key] = 13;
447 sec.flags |= (1 << key);
450 /* No key data - just set the default TX key index */
451 if (key_provided) {
452 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
453 "key.\n", key);
454 ieee->crypt_info.tx_keyidx = key;
455 sec.active_key = key;
456 sec.flags |= SEC_ACTIVE_KEY;
459 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
460 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
461 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
462 WLAN_AUTH_SHARED_KEY;
463 sec.flags |= SEC_AUTH_MODE;
464 LIBIPW_DEBUG_WX("Auth: %s\n",
465 sec.auth_mode == WLAN_AUTH_OPEN ?
466 "OPEN" : "SHARED KEY");
469 /* For now we just support WEP, so only set that security level...
470 * TODO: When WPA is added this is one place that needs to change */
471 sec.flags |= SEC_LEVEL;
472 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
473 sec.encode_alg[key] = SEC_ALG_WEP;
475 done:
476 if (ieee->set_security)
477 ieee->set_security(dev, &sec);
479 /* Do not reset port if card is in Managed mode since resetting will
480 * generate new IEEE 802.11 authentication which may end up in looping
481 * with IEEE 802.1X. If your hardware requires a reset after WEP
482 * configuration (for example... Prism2), implement the reset_port in
483 * the callbacks structures used to initialize the 802.11 stack. */
484 if (ieee->reset_on_keychange &&
485 ieee->iw_mode != IW_MODE_INFRA &&
486 ieee->reset_port && ieee->reset_port(dev)) {
487 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
488 return -EINVAL;
490 return 0;
493 int libipw_wx_get_encode(struct libipw_device *ieee,
494 struct iw_request_info *info,
495 union iwreq_data *wrqu, char *keybuf)
497 struct iw_point *erq = &(wrqu->encoding);
498 int len, key;
499 struct lib80211_crypt_data *crypt;
500 struct libipw_security *sec = &ieee->sec;
502 LIBIPW_DEBUG_WX("GET_ENCODE\n");
504 key = erq->flags & IW_ENCODE_INDEX;
505 if (key) {
506 if (key > WEP_KEYS)
507 return -EINVAL;
508 key--;
509 } else
510 key = ieee->crypt_info.tx_keyidx;
512 crypt = ieee->crypt_info.crypt[key];
513 erq->flags = key + 1;
515 if (!sec->enabled) {
516 erq->length = 0;
517 erq->flags |= IW_ENCODE_DISABLED;
518 return 0;
521 len = sec->key_sizes[key];
522 memcpy(keybuf, sec->keys[key], len);
524 erq->length = len;
525 erq->flags |= IW_ENCODE_ENABLED;
527 if (ieee->open_wep)
528 erq->flags |= IW_ENCODE_OPEN;
529 else
530 erq->flags |= IW_ENCODE_RESTRICTED;
532 return 0;
535 int libipw_wx_set_encodeext(struct libipw_device *ieee,
536 struct iw_request_info *info,
537 union iwreq_data *wrqu, char *extra)
539 struct net_device *dev = ieee->dev;
540 struct iw_point *encoding = &wrqu->encoding;
541 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
542 int i, idx, ret = 0;
543 int group_key = 0;
544 const char *alg, *module;
545 struct lib80211_crypto_ops *ops;
546 struct lib80211_crypt_data **crypt;
548 struct libipw_security sec = {
549 .flags = 0,
552 idx = encoding->flags & IW_ENCODE_INDEX;
553 if (idx) {
554 if (idx < 1 || idx > WEP_KEYS)
555 return -EINVAL;
556 idx--;
557 } else
558 idx = ieee->crypt_info.tx_keyidx;
560 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
561 crypt = &ieee->crypt_info.crypt[idx];
562 group_key = 1;
563 } else {
564 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
565 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
566 return -EINVAL;
567 if (ieee->iw_mode == IW_MODE_INFRA)
568 crypt = &ieee->crypt_info.crypt[idx];
569 else
570 return -EINVAL;
573 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
574 if ((encoding->flags & IW_ENCODE_DISABLED) ||
575 ext->alg == IW_ENCODE_ALG_NONE) {
576 if (*crypt)
577 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
579 for (i = 0; i < WEP_KEYS; i++)
580 if (ieee->crypt_info.crypt[i] != NULL)
581 break;
583 if (i == WEP_KEYS) {
584 sec.enabled = 0;
585 sec.encrypt = 0;
586 sec.level = SEC_LEVEL_0;
587 sec.flags |= SEC_LEVEL;
589 goto done;
592 sec.enabled = 1;
593 sec.encrypt = 1;
595 if (group_key ? !ieee->host_mc_decrypt :
596 !(ieee->host_encrypt || ieee->host_decrypt ||
597 ieee->host_encrypt_msdu))
598 goto skip_host_crypt;
600 switch (ext->alg) {
601 case IW_ENCODE_ALG_WEP:
602 alg = "WEP";
603 module = "lib80211_crypt_wep";
604 break;
605 case IW_ENCODE_ALG_TKIP:
606 alg = "TKIP";
607 module = "lib80211_crypt_tkip";
608 break;
609 case IW_ENCODE_ALG_CCMP:
610 alg = "CCMP";
611 module = "lib80211_crypt_ccmp";
612 break;
613 default:
614 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
615 dev->name, ext->alg);
616 ret = -EINVAL;
617 goto done;
620 ops = lib80211_get_crypto_ops(alg);
621 if (ops == NULL) {
622 request_module(module);
623 ops = lib80211_get_crypto_ops(alg);
625 if (ops == NULL) {
626 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
627 dev->name, ext->alg);
628 ret = -EINVAL;
629 goto done;
632 if (*crypt == NULL || (*crypt)->ops != ops) {
633 struct lib80211_crypt_data *new_crypt;
635 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
637 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
638 if (new_crypt == NULL) {
639 ret = -ENOMEM;
640 goto done;
642 new_crypt->ops = ops;
643 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
644 new_crypt->priv = new_crypt->ops->init(idx);
645 if (new_crypt->priv == NULL) {
646 kfree(new_crypt);
647 ret = -EINVAL;
648 goto done;
650 *crypt = new_crypt;
653 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
654 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
655 (*crypt)->priv) < 0) {
656 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
657 ret = -EINVAL;
658 goto done;
661 skip_host_crypt:
662 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
663 ieee->crypt_info.tx_keyidx = idx;
664 sec.active_key = idx;
665 sec.flags |= SEC_ACTIVE_KEY;
668 if (ext->alg != IW_ENCODE_ALG_NONE) {
669 memcpy(sec.keys[idx], ext->key, ext->key_len);
670 sec.key_sizes[idx] = ext->key_len;
671 sec.flags |= (1 << idx);
672 if (ext->alg == IW_ENCODE_ALG_WEP) {
673 sec.encode_alg[idx] = SEC_ALG_WEP;
674 sec.flags |= SEC_LEVEL;
675 sec.level = SEC_LEVEL_1;
676 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
677 sec.encode_alg[idx] = SEC_ALG_TKIP;
678 sec.flags |= SEC_LEVEL;
679 sec.level = SEC_LEVEL_2;
680 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
681 sec.encode_alg[idx] = SEC_ALG_CCMP;
682 sec.flags |= SEC_LEVEL;
683 sec.level = SEC_LEVEL_3;
685 /* Don't set sec level for group keys. */
686 if (group_key)
687 sec.flags &= ~SEC_LEVEL;
689 done:
690 if (ieee->set_security)
691 ieee->set_security(ieee->dev, &sec);
694 * Do not reset port if card is in Managed mode since resetting will
695 * generate new IEEE 802.11 authentication which may end up in looping
696 * with IEEE 802.1X. If your hardware requires a reset after WEP
697 * configuration (for example... Prism2), implement the reset_port in
698 * the callbacks structures used to initialize the 802.11 stack.
700 if (ieee->reset_on_keychange &&
701 ieee->iw_mode != IW_MODE_INFRA &&
702 ieee->reset_port && ieee->reset_port(dev)) {
703 LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
704 return -EINVAL;
707 return ret;
710 int libipw_wx_get_encodeext(struct libipw_device *ieee,
711 struct iw_request_info *info,
712 union iwreq_data *wrqu, char *extra)
714 struct iw_point *encoding = &wrqu->encoding;
715 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
716 struct libipw_security *sec = &ieee->sec;
717 int idx, max_key_len;
719 max_key_len = encoding->length - sizeof(*ext);
720 if (max_key_len < 0)
721 return -EINVAL;
723 idx = encoding->flags & IW_ENCODE_INDEX;
724 if (idx) {
725 if (idx < 1 || idx > WEP_KEYS)
726 return -EINVAL;
727 idx--;
728 } else
729 idx = ieee->crypt_info.tx_keyidx;
731 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
732 ext->alg != IW_ENCODE_ALG_WEP)
733 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
734 return -EINVAL;
736 encoding->flags = idx + 1;
737 memset(ext, 0, sizeof(*ext));
739 if (!sec->enabled) {
740 ext->alg = IW_ENCODE_ALG_NONE;
741 ext->key_len = 0;
742 encoding->flags |= IW_ENCODE_DISABLED;
743 } else {
744 if (sec->encode_alg[idx] == SEC_ALG_WEP)
745 ext->alg = IW_ENCODE_ALG_WEP;
746 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
747 ext->alg = IW_ENCODE_ALG_TKIP;
748 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
749 ext->alg = IW_ENCODE_ALG_CCMP;
750 else
751 return -EINVAL;
753 ext->key_len = sec->key_sizes[idx];
754 memcpy(ext->key, sec->keys[idx], ext->key_len);
755 encoding->flags |= IW_ENCODE_ENABLED;
756 if (ext->key_len &&
757 (ext->alg == IW_ENCODE_ALG_TKIP ||
758 ext->alg == IW_ENCODE_ALG_CCMP))
759 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
763 return 0;
766 EXPORT_SYMBOL(libipw_wx_set_encodeext);
767 EXPORT_SYMBOL(libipw_wx_get_encodeext);
769 EXPORT_SYMBOL(libipw_wx_get_scan);
770 EXPORT_SYMBOL(libipw_wx_set_encode);
771 EXPORT_SYMBOL(libipw_wx_get_encode);