Merge branch 'akpm'
[linux-2.6/next.git] / drivers / net / wireless / ipw2x00 / libipw_wx.c
blob6623e5052254c86ffdb3ca4cf7a9c2e32da7ebb5
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/hardirq.h>
34 #include <linux/kmod.h>
35 #include <linux/slab.h>
36 #include <linux/module.h>
37 #include <linux/jiffies.h>
39 #include <net/lib80211.h>
40 #include <linux/wireless.h>
42 #include "libipw.h"
44 static const char *libipw_modes[] = {
45 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
48 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
50 unsigned long end = jiffies;
52 if (end >= start)
53 return jiffies_to_msecs(end - start);
55 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
58 #define MAX_CUSTOM_LEN 64
59 static char *libipw_translate_scan(struct libipw_device *ieee,
60 char *start, char *stop,
61 struct libipw_network *network,
62 struct iw_request_info *info)
64 char custom[MAX_CUSTOM_LEN];
65 char *p;
66 struct iw_event iwe;
67 int i, j;
68 char *current_val; /* For rates */
69 u8 rate;
71 /* First entry *MUST* be the AP MAC address */
72 iwe.cmd = SIOCGIWAP;
73 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
74 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
75 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
77 /* Remaining entries will be displayed in the order we provide them */
79 /* Add the ESSID */
80 iwe.cmd = SIOCGIWESSID;
81 iwe.u.data.flags = 1;
82 iwe.u.data.length = min(network->ssid_len, (u8) 32);
83 start = iwe_stream_add_point(info, start, stop,
84 &iwe, network->ssid);
86 /* Add the protocol name */
87 iwe.cmd = SIOCGIWNAME;
88 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
89 libipw_modes[network->mode]);
90 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
92 /* Add mode */
93 iwe.cmd = SIOCGIWMODE;
94 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
95 if (network->capability & WLAN_CAPABILITY_ESS)
96 iwe.u.mode = IW_MODE_MASTER;
97 else
98 iwe.u.mode = IW_MODE_ADHOC;
100 start = iwe_stream_add_event(info, start, stop,
101 &iwe, IW_EV_UINT_LEN);
104 /* Add channel and frequency */
105 /* Note : userspace automatically computes channel using iwrange */
106 iwe.cmd = SIOCGIWFREQ;
107 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
108 iwe.u.freq.e = 6;
109 iwe.u.freq.i = 0;
110 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
112 /* Add encryption capability */
113 iwe.cmd = SIOCGIWENCODE;
114 if (network->capability & WLAN_CAPABILITY_PRIVACY)
115 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
116 else
117 iwe.u.data.flags = IW_ENCODE_DISABLED;
118 iwe.u.data.length = 0;
119 start = iwe_stream_add_point(info, start, stop,
120 &iwe, network->ssid);
122 /* Add basic and extended rates */
123 /* Rate : stuffing multiple values in a single event require a bit
124 * more of magic - Jean II */
125 current_val = start + iwe_stream_lcp_len(info);
126 iwe.cmd = SIOCGIWRATE;
127 /* Those two flags are ignored... */
128 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
130 for (i = 0, j = 0; i < network->rates_len;) {
131 if (j < network->rates_ex_len &&
132 ((network->rates_ex[j] & 0x7F) <
133 (network->rates[i] & 0x7F)))
134 rate = network->rates_ex[j++] & 0x7F;
135 else
136 rate = network->rates[i++] & 0x7F;
137 /* Bit rate given in 500 kb/s units (+ 0x80) */
138 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139 /* Add new value to event */
140 current_val = iwe_stream_add_value(info, start, current_val,
141 stop, &iwe, IW_EV_PARAM_LEN);
143 for (; j < network->rates_ex_len; j++) {
144 rate = network->rates_ex[j] & 0x7F;
145 /* Bit rate given in 500 kb/s units (+ 0x80) */
146 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147 /* Add new value to event */
148 current_val = iwe_stream_add_value(info, start, current_val,
149 stop, &iwe, IW_EV_PARAM_LEN);
151 /* Check if we added any rate */
152 if ((current_val - start) > iwe_stream_lcp_len(info))
153 start = current_val;
155 /* Add quality statistics */
156 iwe.cmd = IWEVQUAL;
157 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
158 IW_QUAL_NOISE_UPDATED;
160 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
161 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
162 IW_QUAL_LEVEL_INVALID;
163 iwe.u.qual.qual = 0;
164 } else {
165 if (ieee->perfect_rssi == ieee->worst_rssi)
166 iwe.u.qual.qual = 100;
167 else
168 iwe.u.qual.qual =
169 (100 *
170 (ieee->perfect_rssi - ieee->worst_rssi) *
171 (ieee->perfect_rssi - ieee->worst_rssi) -
172 (ieee->perfect_rssi - network->stats.rssi) *
173 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
174 62 * (ieee->perfect_rssi -
175 network->stats.rssi))) /
176 ((ieee->perfect_rssi -
177 ieee->worst_rssi) * (ieee->perfect_rssi -
178 ieee->worst_rssi));
179 if (iwe.u.qual.qual > 100)
180 iwe.u.qual.qual = 100;
181 else if (iwe.u.qual.qual < 1)
182 iwe.u.qual.qual = 0;
185 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
186 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
187 iwe.u.qual.noise = 0;
188 } else {
189 iwe.u.qual.noise = network->stats.noise;
192 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
193 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
194 iwe.u.qual.level = 0;
195 } else {
196 iwe.u.qual.level = network->stats.signal;
199 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
201 iwe.cmd = IWEVCUSTOM;
202 p = custom;
204 iwe.u.data.length = p - custom;
205 if (iwe.u.data.length)
206 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
208 memset(&iwe, 0, sizeof(iwe));
209 if (network->wpa_ie_len) {
210 char buf[MAX_WPA_IE_LEN];
211 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
212 iwe.cmd = IWEVGENIE;
213 iwe.u.data.length = network->wpa_ie_len;
214 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
217 memset(&iwe, 0, sizeof(iwe));
218 if (network->rsn_ie_len) {
219 char buf[MAX_WPA_IE_LEN];
220 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
221 iwe.cmd = IWEVGENIE;
222 iwe.u.data.length = network->rsn_ie_len;
223 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
226 /* Add EXTRA: Age to display seconds since last beacon/probe response
227 * for given network. */
228 iwe.cmd = IWEVCUSTOM;
229 p = custom;
230 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
231 " Last beacon: %ums ago",
232 elapsed_jiffies_msecs(network->last_scanned));
233 iwe.u.data.length = p - custom;
234 if (iwe.u.data.length)
235 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
237 /* Add spectrum management information */
238 iwe.cmd = -1;
239 p = custom;
240 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
242 if (libipw_get_channel_flags(ieee, network->channel) &
243 LIBIPW_CH_INVALID) {
244 iwe.cmd = IWEVCUSTOM;
245 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
248 if (libipw_get_channel_flags(ieee, network->channel) &
249 LIBIPW_CH_RADAR_DETECT) {
250 iwe.cmd = IWEVCUSTOM;
251 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
254 if (iwe.cmd == IWEVCUSTOM) {
255 iwe.u.data.length = p - custom;
256 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
259 return start;
262 #define SCAN_ITEM_SIZE 128
264 int libipw_wx_get_scan(struct libipw_device *ieee,
265 struct iw_request_info *info,
266 union iwreq_data *wrqu, char *extra)
268 struct libipw_network *network;
269 unsigned long flags;
270 int err = 0;
272 char *ev = extra;
273 char *stop = ev + wrqu->data.length;
274 int i = 0;
275 DECLARE_SSID_BUF(ssid);
277 LIBIPW_DEBUG_WX("Getting scan\n");
279 spin_lock_irqsave(&ieee->lock, flags);
281 list_for_each_entry(network, &ieee->network_list, list) {
282 i++;
283 if (stop - ev < SCAN_ITEM_SIZE) {
284 err = -E2BIG;
285 break;
288 if (ieee->scan_age == 0 ||
289 time_after(network->last_scanned + ieee->scan_age, jiffies))
290 ev = libipw_translate_scan(ieee, ev, stop, network,
291 info);
292 else {
293 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
294 "%pM)' due to age (%ums).\n",
295 print_ssid(ssid, network->ssid,
296 network->ssid_len),
297 network->bssid,
298 elapsed_jiffies_msecs(
299 network->last_scanned));
303 spin_unlock_irqrestore(&ieee->lock, flags);
305 wrqu->data.length = ev - extra;
306 wrqu->data.flags = 0;
308 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
310 return err;
313 int libipw_wx_set_encode(struct libipw_device *ieee,
314 struct iw_request_info *info,
315 union iwreq_data *wrqu, char *keybuf)
317 struct iw_point *erq = &(wrqu->encoding);
318 struct net_device *dev = ieee->dev;
319 struct libipw_security sec = {
320 .flags = 0
322 int i, key, key_provided, len;
323 struct lib80211_crypt_data **crypt;
324 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
325 DECLARE_SSID_BUF(ssid);
327 LIBIPW_DEBUG_WX("SET_ENCODE\n");
329 key = erq->flags & IW_ENCODE_INDEX;
330 if (key) {
331 if (key > WEP_KEYS)
332 return -EINVAL;
333 key--;
334 key_provided = 1;
335 } else {
336 key_provided = 0;
337 key = ieee->crypt_info.tx_keyidx;
340 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
341 "provided" : "default");
343 crypt = &ieee->crypt_info.crypt[key];
345 if (erq->flags & IW_ENCODE_DISABLED) {
346 if (key_provided && *crypt) {
347 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
348 key);
349 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
350 } else
351 LIBIPW_DEBUG_WX("Disabling encryption.\n");
353 /* Check all the keys to see if any are still configured,
354 * and if no key index was provided, de-init them all */
355 for (i = 0; i < WEP_KEYS; i++) {
356 if (ieee->crypt_info.crypt[i] != NULL) {
357 if (key_provided)
358 break;
359 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
360 &ieee->crypt_info.crypt[i]);
364 if (i == WEP_KEYS) {
365 sec.enabled = 0;
366 sec.encrypt = 0;
367 sec.level = SEC_LEVEL_0;
368 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
371 goto done;
374 sec.enabled = 1;
375 sec.encrypt = 1;
376 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
378 if (*crypt != NULL && (*crypt)->ops != NULL &&
379 strcmp((*crypt)->ops->name, "WEP") != 0) {
380 /* changing to use WEP; deinit previously used algorithm
381 * on this key */
382 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
385 if (*crypt == NULL && host_crypto) {
386 struct lib80211_crypt_data *new_crypt;
388 /* take WEP into use */
389 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
390 GFP_KERNEL);
391 if (new_crypt == NULL)
392 return -ENOMEM;
393 new_crypt->ops = lib80211_get_crypto_ops("WEP");
394 if (!new_crypt->ops) {
395 request_module("lib80211_crypt_wep");
396 new_crypt->ops = lib80211_get_crypto_ops("WEP");
399 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
400 new_crypt->priv = new_crypt->ops->init(key);
402 if (!new_crypt->ops || !new_crypt->priv) {
403 kfree(new_crypt);
404 new_crypt = NULL;
406 printk(KERN_WARNING "%s: could not initialize WEP: "
407 "load module lib80211_crypt_wep\n", dev->name);
408 return -EOPNOTSUPP;
410 *crypt = new_crypt;
413 /* If a new key was provided, set it up */
414 if (erq->length > 0) {
415 len = erq->length <= 5 ? 5 : 13;
416 memcpy(sec.keys[key], keybuf, erq->length);
417 if (len > erq->length)
418 memset(sec.keys[key] + erq->length, 0,
419 len - erq->length);
420 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
421 key, print_ssid(ssid, sec.keys[key], len),
422 erq->length, len);
423 sec.key_sizes[key] = len;
424 if (*crypt)
425 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
426 (*crypt)->priv);
427 sec.flags |= (1 << key);
428 /* This ensures a key will be activated if no key is
429 * explicitly set */
430 if (key == sec.active_key)
431 sec.flags |= SEC_ACTIVE_KEY;
433 } else {
434 if (host_crypto) {
435 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
436 NULL, (*crypt)->priv);
437 if (len == 0) {
438 /* Set a default key of all 0 */
439 LIBIPW_DEBUG_WX("Setting key %d to all "
440 "zero.\n", key);
441 memset(sec.keys[key], 0, 13);
442 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
443 (*crypt)->priv);
444 sec.key_sizes[key] = 13;
445 sec.flags |= (1 << key);
448 /* No key data - just set the default TX key index */
449 if (key_provided) {
450 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
451 "key.\n", key);
452 ieee->crypt_info.tx_keyidx = key;
453 sec.active_key = key;
454 sec.flags |= SEC_ACTIVE_KEY;
457 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
458 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
459 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
460 WLAN_AUTH_SHARED_KEY;
461 sec.flags |= SEC_AUTH_MODE;
462 LIBIPW_DEBUG_WX("Auth: %s\n",
463 sec.auth_mode == WLAN_AUTH_OPEN ?
464 "OPEN" : "SHARED KEY");
467 /* For now we just support WEP, so only set that security level...
468 * TODO: When WPA is added this is one place that needs to change */
469 sec.flags |= SEC_LEVEL;
470 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
471 sec.encode_alg[key] = SEC_ALG_WEP;
473 done:
474 if (ieee->set_security)
475 ieee->set_security(dev, &sec);
477 /* Do not reset port if card is in Managed mode since resetting will
478 * generate new IEEE 802.11 authentication which may end up in looping
479 * with IEEE 802.1X. If your hardware requires a reset after WEP
480 * configuration (for example... Prism2), implement the reset_port in
481 * the callbacks structures used to initialize the 802.11 stack. */
482 if (ieee->reset_on_keychange &&
483 ieee->iw_mode != IW_MODE_INFRA &&
484 ieee->reset_port && ieee->reset_port(dev)) {
485 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
486 return -EINVAL;
488 return 0;
491 int libipw_wx_get_encode(struct libipw_device *ieee,
492 struct iw_request_info *info,
493 union iwreq_data *wrqu, char *keybuf)
495 struct iw_point *erq = &(wrqu->encoding);
496 int len, key;
497 struct lib80211_crypt_data *crypt;
498 struct libipw_security *sec = &ieee->sec;
500 LIBIPW_DEBUG_WX("GET_ENCODE\n");
502 key = erq->flags & IW_ENCODE_INDEX;
503 if (key) {
504 if (key > WEP_KEYS)
505 return -EINVAL;
506 key--;
507 } else
508 key = ieee->crypt_info.tx_keyidx;
510 crypt = ieee->crypt_info.crypt[key];
511 erq->flags = key + 1;
513 if (!sec->enabled) {
514 erq->length = 0;
515 erq->flags |= IW_ENCODE_DISABLED;
516 return 0;
519 len = sec->key_sizes[key];
520 memcpy(keybuf, sec->keys[key], len);
522 erq->length = len;
523 erq->flags |= IW_ENCODE_ENABLED;
525 if (ieee->open_wep)
526 erq->flags |= IW_ENCODE_OPEN;
527 else
528 erq->flags |= IW_ENCODE_RESTRICTED;
530 return 0;
533 int libipw_wx_set_encodeext(struct libipw_device *ieee,
534 struct iw_request_info *info,
535 union iwreq_data *wrqu, char *extra)
537 struct net_device *dev = ieee->dev;
538 struct iw_point *encoding = &wrqu->encoding;
539 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
540 int i, idx, ret = 0;
541 int group_key = 0;
542 const char *alg, *module;
543 struct lib80211_crypto_ops *ops;
544 struct lib80211_crypt_data **crypt;
546 struct libipw_security sec = {
547 .flags = 0,
550 idx = encoding->flags & IW_ENCODE_INDEX;
551 if (idx) {
552 if (idx < 1 || idx > WEP_KEYS)
553 return -EINVAL;
554 idx--;
555 } else
556 idx = ieee->crypt_info.tx_keyidx;
558 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
559 crypt = &ieee->crypt_info.crypt[idx];
560 group_key = 1;
561 } else {
562 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
563 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
564 return -EINVAL;
565 if (ieee->iw_mode == IW_MODE_INFRA)
566 crypt = &ieee->crypt_info.crypt[idx];
567 else
568 return -EINVAL;
571 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
572 if ((encoding->flags & IW_ENCODE_DISABLED) ||
573 ext->alg == IW_ENCODE_ALG_NONE) {
574 if (*crypt)
575 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
577 for (i = 0; i < WEP_KEYS; i++)
578 if (ieee->crypt_info.crypt[i] != NULL)
579 break;
581 if (i == WEP_KEYS) {
582 sec.enabled = 0;
583 sec.encrypt = 0;
584 sec.level = SEC_LEVEL_0;
585 sec.flags |= SEC_LEVEL;
587 goto done;
590 sec.enabled = 1;
591 sec.encrypt = 1;
593 if (group_key ? !ieee->host_mc_decrypt :
594 !(ieee->host_encrypt || ieee->host_decrypt ||
595 ieee->host_encrypt_msdu))
596 goto skip_host_crypt;
598 switch (ext->alg) {
599 case IW_ENCODE_ALG_WEP:
600 alg = "WEP";
601 module = "lib80211_crypt_wep";
602 break;
603 case IW_ENCODE_ALG_TKIP:
604 alg = "TKIP";
605 module = "lib80211_crypt_tkip";
606 break;
607 case IW_ENCODE_ALG_CCMP:
608 alg = "CCMP";
609 module = "lib80211_crypt_ccmp";
610 break;
611 default:
612 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
613 dev->name, ext->alg);
614 ret = -EINVAL;
615 goto done;
618 ops = lib80211_get_crypto_ops(alg);
619 if (ops == NULL) {
620 request_module(module);
621 ops = lib80211_get_crypto_ops(alg);
623 if (ops == NULL) {
624 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
625 dev->name, ext->alg);
626 ret = -EINVAL;
627 goto done;
630 if (*crypt == NULL || (*crypt)->ops != ops) {
631 struct lib80211_crypt_data *new_crypt;
633 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
635 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
636 if (new_crypt == NULL) {
637 ret = -ENOMEM;
638 goto done;
640 new_crypt->ops = ops;
641 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
642 new_crypt->priv = new_crypt->ops->init(idx);
643 if (new_crypt->priv == NULL) {
644 kfree(new_crypt);
645 ret = -EINVAL;
646 goto done;
648 *crypt = new_crypt;
651 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
652 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
653 (*crypt)->priv) < 0) {
654 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
655 ret = -EINVAL;
656 goto done;
659 skip_host_crypt:
660 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
661 ieee->crypt_info.tx_keyidx = idx;
662 sec.active_key = idx;
663 sec.flags |= SEC_ACTIVE_KEY;
666 if (ext->alg != IW_ENCODE_ALG_NONE) {
667 memcpy(sec.keys[idx], ext->key, ext->key_len);
668 sec.key_sizes[idx] = ext->key_len;
669 sec.flags |= (1 << idx);
670 if (ext->alg == IW_ENCODE_ALG_WEP) {
671 sec.encode_alg[idx] = SEC_ALG_WEP;
672 sec.flags |= SEC_LEVEL;
673 sec.level = SEC_LEVEL_1;
674 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
675 sec.encode_alg[idx] = SEC_ALG_TKIP;
676 sec.flags |= SEC_LEVEL;
677 sec.level = SEC_LEVEL_2;
678 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
679 sec.encode_alg[idx] = SEC_ALG_CCMP;
680 sec.flags |= SEC_LEVEL;
681 sec.level = SEC_LEVEL_3;
683 /* Don't set sec level for group keys. */
684 if (group_key)
685 sec.flags &= ~SEC_LEVEL;
687 done:
688 if (ieee->set_security)
689 ieee->set_security(ieee->dev, &sec);
692 * Do not reset port if card is in Managed mode since resetting will
693 * generate new IEEE 802.11 authentication which may end up in looping
694 * with IEEE 802.1X. If your hardware requires a reset after WEP
695 * configuration (for example... Prism2), implement the reset_port in
696 * the callbacks structures used to initialize the 802.11 stack.
698 if (ieee->reset_on_keychange &&
699 ieee->iw_mode != IW_MODE_INFRA &&
700 ieee->reset_port && ieee->reset_port(dev)) {
701 LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
702 return -EINVAL;
705 return ret;
708 int libipw_wx_get_encodeext(struct libipw_device *ieee,
709 struct iw_request_info *info,
710 union iwreq_data *wrqu, char *extra)
712 struct iw_point *encoding = &wrqu->encoding;
713 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
714 struct libipw_security *sec = &ieee->sec;
715 int idx, max_key_len;
717 max_key_len = encoding->length - sizeof(*ext);
718 if (max_key_len < 0)
719 return -EINVAL;
721 idx = encoding->flags & IW_ENCODE_INDEX;
722 if (idx) {
723 if (idx < 1 || idx > WEP_KEYS)
724 return -EINVAL;
725 idx--;
726 } else
727 idx = ieee->crypt_info.tx_keyidx;
729 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
730 ext->alg != IW_ENCODE_ALG_WEP)
731 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
732 return -EINVAL;
734 encoding->flags = idx + 1;
735 memset(ext, 0, sizeof(*ext));
737 if (!sec->enabled) {
738 ext->alg = IW_ENCODE_ALG_NONE;
739 ext->key_len = 0;
740 encoding->flags |= IW_ENCODE_DISABLED;
741 } else {
742 if (sec->encode_alg[idx] == SEC_ALG_WEP)
743 ext->alg = IW_ENCODE_ALG_WEP;
744 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
745 ext->alg = IW_ENCODE_ALG_TKIP;
746 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
747 ext->alg = IW_ENCODE_ALG_CCMP;
748 else
749 return -EINVAL;
751 ext->key_len = sec->key_sizes[idx];
752 memcpy(ext->key, sec->keys[idx], ext->key_len);
753 encoding->flags |= IW_ENCODE_ENABLED;
754 if (ext->key_len &&
755 (ext->alg == IW_ENCODE_ALG_TKIP ||
756 ext->alg == IW_ENCODE_ALG_CCMP))
757 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
761 return 0;
764 EXPORT_SYMBOL(libipw_wx_set_encodeext);
765 EXPORT_SYMBOL(libipw_wx_get_encodeext);
767 EXPORT_SYMBOL(libipw_wx_get_scan);
768 EXPORT_SYMBOL(libipw_wx_set_encode);
769 EXPORT_SYMBOL(libipw_wx_get_encode);