proc: use seq_puts()/seq_putc() where possible
[linux-2.6/next.git] / drivers / net / wireless / ipw2x00 / libipw_wx.c
blobd7bd6cf00a81611f04f32cb96cde0cac1c53586d
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/slab.h>
35 #include <linux/module.h>
36 #include <linux/jiffies.h>
38 #include <net/lib80211.h>
39 #include <linux/wireless.h>
41 #include "libipw.h"
43 static const char *libipw_modes[] = {
44 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
47 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
49 unsigned long end = jiffies;
51 if (end >= start)
52 return jiffies_to_msecs(end - start);
54 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
57 #define MAX_CUSTOM_LEN 64
58 static char *libipw_translate_scan(struct libipw_device *ieee,
59 char *start, char *stop,
60 struct libipw_network *network,
61 struct iw_request_info *info)
63 char custom[MAX_CUSTOM_LEN];
64 char *p;
65 struct iw_event iwe;
66 int i, j;
67 char *current_val; /* For rates */
68 u8 rate;
70 /* First entry *MUST* be the AP MAC address */
71 iwe.cmd = SIOCGIWAP;
72 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
73 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
74 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
76 /* Remaining entries will be displayed in the order we provide them */
78 /* Add the ESSID */
79 iwe.cmd = SIOCGIWESSID;
80 iwe.u.data.flags = 1;
81 iwe.u.data.length = min(network->ssid_len, (u8) 32);
82 start = iwe_stream_add_point(info, start, stop,
83 &iwe, network->ssid);
85 /* Add the protocol name */
86 iwe.cmd = SIOCGIWNAME;
87 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
88 libipw_modes[network->mode]);
89 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
91 /* Add mode */
92 iwe.cmd = SIOCGIWMODE;
93 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
94 if (network->capability & WLAN_CAPABILITY_ESS)
95 iwe.u.mode = IW_MODE_MASTER;
96 else
97 iwe.u.mode = IW_MODE_ADHOC;
99 start = iwe_stream_add_event(info, start, stop,
100 &iwe, IW_EV_UINT_LEN);
103 /* Add channel and frequency */
104 /* Note : userspace automatically computes channel using iwrange */
105 iwe.cmd = SIOCGIWFREQ;
106 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
107 iwe.u.freq.e = 6;
108 iwe.u.freq.i = 0;
109 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
111 /* Add encryption capability */
112 iwe.cmd = SIOCGIWENCODE;
113 if (network->capability & WLAN_CAPABILITY_PRIVACY)
114 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
115 else
116 iwe.u.data.flags = IW_ENCODE_DISABLED;
117 iwe.u.data.length = 0;
118 start = iwe_stream_add_point(info, start, stop,
119 &iwe, network->ssid);
121 /* Add basic and extended rates */
122 /* Rate : stuffing multiple values in a single event require a bit
123 * more of magic - Jean II */
124 current_val = start + iwe_stream_lcp_len(info);
125 iwe.cmd = SIOCGIWRATE;
126 /* Those two flags are ignored... */
127 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129 for (i = 0, j = 0; i < network->rates_len;) {
130 if (j < network->rates_ex_len &&
131 ((network->rates_ex[j] & 0x7F) <
132 (network->rates[i] & 0x7F)))
133 rate = network->rates_ex[j++] & 0x7F;
134 else
135 rate = network->rates[i++] & 0x7F;
136 /* Bit rate given in 500 kb/s units (+ 0x80) */
137 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
138 /* Add new value to event */
139 current_val = iwe_stream_add_value(info, start, current_val,
140 stop, &iwe, IW_EV_PARAM_LEN);
142 for (; j < network->rates_ex_len; j++) {
143 rate = network->rates_ex[j] & 0x7F;
144 /* Bit rate given in 500 kb/s units (+ 0x80) */
145 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
146 /* Add new value to event */
147 current_val = iwe_stream_add_value(info, start, current_val,
148 stop, &iwe, IW_EV_PARAM_LEN);
150 /* Check if we added any rate */
151 if ((current_val - start) > iwe_stream_lcp_len(info))
152 start = current_val;
154 /* Add quality statistics */
155 iwe.cmd = IWEVQUAL;
156 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
157 IW_QUAL_NOISE_UPDATED;
159 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
160 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
161 IW_QUAL_LEVEL_INVALID;
162 iwe.u.qual.qual = 0;
163 } else {
164 if (ieee->perfect_rssi == ieee->worst_rssi)
165 iwe.u.qual.qual = 100;
166 else
167 iwe.u.qual.qual =
168 (100 *
169 (ieee->perfect_rssi - ieee->worst_rssi) *
170 (ieee->perfect_rssi - ieee->worst_rssi) -
171 (ieee->perfect_rssi - network->stats.rssi) *
172 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
173 62 * (ieee->perfect_rssi -
174 network->stats.rssi))) /
175 ((ieee->perfect_rssi -
176 ieee->worst_rssi) * (ieee->perfect_rssi -
177 ieee->worst_rssi));
178 if (iwe.u.qual.qual > 100)
179 iwe.u.qual.qual = 100;
180 else if (iwe.u.qual.qual < 1)
181 iwe.u.qual.qual = 0;
184 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
185 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
186 iwe.u.qual.noise = 0;
187 } else {
188 iwe.u.qual.noise = network->stats.noise;
191 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
192 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
193 iwe.u.qual.level = 0;
194 } else {
195 iwe.u.qual.level = network->stats.signal;
198 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
200 iwe.cmd = IWEVCUSTOM;
201 p = custom;
203 iwe.u.data.length = p - custom;
204 if (iwe.u.data.length)
205 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
207 memset(&iwe, 0, sizeof(iwe));
208 if (network->wpa_ie_len) {
209 char buf[MAX_WPA_IE_LEN];
210 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
211 iwe.cmd = IWEVGENIE;
212 iwe.u.data.length = network->wpa_ie_len;
213 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
216 memset(&iwe, 0, sizeof(iwe));
217 if (network->rsn_ie_len) {
218 char buf[MAX_WPA_IE_LEN];
219 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
220 iwe.cmd = IWEVGENIE;
221 iwe.u.data.length = network->rsn_ie_len;
222 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
225 /* Add EXTRA: Age to display seconds since last beacon/probe response
226 * for given network. */
227 iwe.cmd = IWEVCUSTOM;
228 p = custom;
229 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
230 " Last beacon: %ums ago",
231 elapsed_jiffies_msecs(network->last_scanned));
232 iwe.u.data.length = p - custom;
233 if (iwe.u.data.length)
234 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
236 /* Add spectrum management information */
237 iwe.cmd = -1;
238 p = custom;
239 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241 if (libipw_get_channel_flags(ieee, network->channel) &
242 LIBIPW_CH_INVALID) {
243 iwe.cmd = IWEVCUSTOM;
244 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
247 if (libipw_get_channel_flags(ieee, network->channel) &
248 LIBIPW_CH_RADAR_DETECT) {
249 iwe.cmd = IWEVCUSTOM;
250 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
253 if (iwe.cmd == IWEVCUSTOM) {
254 iwe.u.data.length = p - custom;
255 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
258 return start;
261 #define SCAN_ITEM_SIZE 128
263 int libipw_wx_get_scan(struct libipw_device *ieee,
264 struct iw_request_info *info,
265 union iwreq_data *wrqu, char *extra)
267 struct libipw_network *network;
268 unsigned long flags;
269 int err = 0;
271 char *ev = extra;
272 char *stop = ev + wrqu->data.length;
273 int i = 0;
274 DECLARE_SSID_BUF(ssid);
276 LIBIPW_DEBUG_WX("Getting scan\n");
278 spin_lock_irqsave(&ieee->lock, flags);
280 list_for_each_entry(network, &ieee->network_list, list) {
281 i++;
282 if (stop - ev < SCAN_ITEM_SIZE) {
283 err = -E2BIG;
284 break;
287 if (ieee->scan_age == 0 ||
288 time_after(network->last_scanned + ieee->scan_age, jiffies))
289 ev = libipw_translate_scan(ieee, ev, stop, network,
290 info);
291 else {
292 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
293 "%pM)' due to age (%ums).\n",
294 print_ssid(ssid, network->ssid,
295 network->ssid_len),
296 network->bssid,
297 elapsed_jiffies_msecs(
298 network->last_scanned));
302 spin_unlock_irqrestore(&ieee->lock, flags);
304 wrqu->data.length = ev - extra;
305 wrqu->data.flags = 0;
307 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
309 return err;
312 int libipw_wx_set_encode(struct libipw_device *ieee,
313 struct iw_request_info *info,
314 union iwreq_data *wrqu, char *keybuf)
316 struct iw_point *erq = &(wrqu->encoding);
317 struct net_device *dev = ieee->dev;
318 struct libipw_security sec = {
319 .flags = 0
321 int i, key, key_provided, len;
322 struct lib80211_crypt_data **crypt;
323 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
324 DECLARE_SSID_BUF(ssid);
326 LIBIPW_DEBUG_WX("SET_ENCODE\n");
328 key = erq->flags & IW_ENCODE_INDEX;
329 if (key) {
330 if (key > WEP_KEYS)
331 return -EINVAL;
332 key--;
333 key_provided = 1;
334 } else {
335 key_provided = 0;
336 key = ieee->crypt_info.tx_keyidx;
339 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
340 "provided" : "default");
342 crypt = &ieee->crypt_info.crypt[key];
344 if (erq->flags & IW_ENCODE_DISABLED) {
345 if (key_provided && *crypt) {
346 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
347 key);
348 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
349 } else
350 LIBIPW_DEBUG_WX("Disabling encryption.\n");
352 /* Check all the keys to see if any are still configured,
353 * and if no key index was provided, de-init them all */
354 for (i = 0; i < WEP_KEYS; i++) {
355 if (ieee->crypt_info.crypt[i] != NULL) {
356 if (key_provided)
357 break;
358 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
359 &ieee->crypt_info.crypt[i]);
363 if (i == WEP_KEYS) {
364 sec.enabled = 0;
365 sec.encrypt = 0;
366 sec.level = SEC_LEVEL_0;
367 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
370 goto done;
373 sec.enabled = 1;
374 sec.encrypt = 1;
375 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
377 if (*crypt != NULL && (*crypt)->ops != NULL &&
378 strcmp((*crypt)->ops->name, "WEP") != 0) {
379 /* changing to use WEP; deinit previously used algorithm
380 * on this key */
381 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
384 if (*crypt == NULL && host_crypto) {
385 struct lib80211_crypt_data *new_crypt;
387 /* take WEP into use */
388 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
389 GFP_KERNEL);
390 if (new_crypt == NULL)
391 return -ENOMEM;
392 new_crypt->ops = lib80211_get_crypto_ops("WEP");
393 if (!new_crypt->ops) {
394 request_module("lib80211_crypt_wep");
395 new_crypt->ops = lib80211_get_crypto_ops("WEP");
398 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
399 new_crypt->priv = new_crypt->ops->init(key);
401 if (!new_crypt->ops || !new_crypt->priv) {
402 kfree(new_crypt);
403 new_crypt = NULL;
405 printk(KERN_WARNING "%s: could not initialize WEP: "
406 "load module lib80211_crypt_wep\n", dev->name);
407 return -EOPNOTSUPP;
409 *crypt = new_crypt;
412 /* If a new key was provided, set it up */
413 if (erq->length > 0) {
414 len = erq->length <= 5 ? 5 : 13;
415 memcpy(sec.keys[key], keybuf, erq->length);
416 if (len > erq->length)
417 memset(sec.keys[key] + erq->length, 0,
418 len - erq->length);
419 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
420 key, print_ssid(ssid, sec.keys[key], len),
421 erq->length, len);
422 sec.key_sizes[key] = len;
423 if (*crypt)
424 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
425 (*crypt)->priv);
426 sec.flags |= (1 << key);
427 /* This ensures a key will be activated if no key is
428 * explicitly set */
429 if (key == sec.active_key)
430 sec.flags |= SEC_ACTIVE_KEY;
432 } else {
433 if (host_crypto) {
434 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
435 NULL, (*crypt)->priv);
436 if (len == 0) {
437 /* Set a default key of all 0 */
438 LIBIPW_DEBUG_WX("Setting key %d to all "
439 "zero.\n", key);
440 memset(sec.keys[key], 0, 13);
441 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
442 (*crypt)->priv);
443 sec.key_sizes[key] = 13;
444 sec.flags |= (1 << key);
447 /* No key data - just set the default TX key index */
448 if (key_provided) {
449 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
450 "key.\n", key);
451 ieee->crypt_info.tx_keyidx = key;
452 sec.active_key = key;
453 sec.flags |= SEC_ACTIVE_KEY;
456 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
457 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
458 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
459 WLAN_AUTH_SHARED_KEY;
460 sec.flags |= SEC_AUTH_MODE;
461 LIBIPW_DEBUG_WX("Auth: %s\n",
462 sec.auth_mode == WLAN_AUTH_OPEN ?
463 "OPEN" : "SHARED KEY");
466 /* For now we just support WEP, so only set that security level...
467 * TODO: When WPA is added this is one place that needs to change */
468 sec.flags |= SEC_LEVEL;
469 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
470 sec.encode_alg[key] = SEC_ALG_WEP;
472 done:
473 if (ieee->set_security)
474 ieee->set_security(dev, &sec);
476 /* Do not reset port if card is in Managed mode since resetting will
477 * generate new IEEE 802.11 authentication which may end up in looping
478 * with IEEE 802.1X. If your hardware requires a reset after WEP
479 * configuration (for example... Prism2), implement the reset_port in
480 * the callbacks structures used to initialize the 802.11 stack. */
481 if (ieee->reset_on_keychange &&
482 ieee->iw_mode != IW_MODE_INFRA &&
483 ieee->reset_port && ieee->reset_port(dev)) {
484 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
485 return -EINVAL;
487 return 0;
490 int libipw_wx_get_encode(struct libipw_device *ieee,
491 struct iw_request_info *info,
492 union iwreq_data *wrqu, char *keybuf)
494 struct iw_point *erq = &(wrqu->encoding);
495 int len, key;
496 struct lib80211_crypt_data *crypt;
497 struct libipw_security *sec = &ieee->sec;
499 LIBIPW_DEBUG_WX("GET_ENCODE\n");
501 key = erq->flags & IW_ENCODE_INDEX;
502 if (key) {
503 if (key > WEP_KEYS)
504 return -EINVAL;
505 key--;
506 } else
507 key = ieee->crypt_info.tx_keyidx;
509 crypt = ieee->crypt_info.crypt[key];
510 erq->flags = key + 1;
512 if (!sec->enabled) {
513 erq->length = 0;
514 erq->flags |= IW_ENCODE_DISABLED;
515 return 0;
518 len = sec->key_sizes[key];
519 memcpy(keybuf, sec->keys[key], len);
521 erq->length = len;
522 erq->flags |= IW_ENCODE_ENABLED;
524 if (ieee->open_wep)
525 erq->flags |= IW_ENCODE_OPEN;
526 else
527 erq->flags |= IW_ENCODE_RESTRICTED;
529 return 0;
532 int libipw_wx_set_encodeext(struct libipw_device *ieee,
533 struct iw_request_info *info,
534 union iwreq_data *wrqu, char *extra)
536 struct net_device *dev = ieee->dev;
537 struct iw_point *encoding = &wrqu->encoding;
538 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
539 int i, idx, ret = 0;
540 int group_key = 0;
541 const char *alg, *module;
542 struct lib80211_crypto_ops *ops;
543 struct lib80211_crypt_data **crypt;
545 struct libipw_security sec = {
546 .flags = 0,
549 idx = encoding->flags & IW_ENCODE_INDEX;
550 if (idx) {
551 if (idx < 1 || idx > WEP_KEYS)
552 return -EINVAL;
553 idx--;
554 } else
555 idx = ieee->crypt_info.tx_keyidx;
557 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
558 crypt = &ieee->crypt_info.crypt[idx];
559 group_key = 1;
560 } else {
561 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
562 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
563 return -EINVAL;
564 if (ieee->iw_mode == IW_MODE_INFRA)
565 crypt = &ieee->crypt_info.crypt[idx];
566 else
567 return -EINVAL;
570 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
571 if ((encoding->flags & IW_ENCODE_DISABLED) ||
572 ext->alg == IW_ENCODE_ALG_NONE) {
573 if (*crypt)
574 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
576 for (i = 0; i < WEP_KEYS; i++)
577 if (ieee->crypt_info.crypt[i] != NULL)
578 break;
580 if (i == WEP_KEYS) {
581 sec.enabled = 0;
582 sec.encrypt = 0;
583 sec.level = SEC_LEVEL_0;
584 sec.flags |= SEC_LEVEL;
586 goto done;
589 sec.enabled = 1;
590 sec.encrypt = 1;
592 if (group_key ? !ieee->host_mc_decrypt :
593 !(ieee->host_encrypt || ieee->host_decrypt ||
594 ieee->host_encrypt_msdu))
595 goto skip_host_crypt;
597 switch (ext->alg) {
598 case IW_ENCODE_ALG_WEP:
599 alg = "WEP";
600 module = "lib80211_crypt_wep";
601 break;
602 case IW_ENCODE_ALG_TKIP:
603 alg = "TKIP";
604 module = "lib80211_crypt_tkip";
605 break;
606 case IW_ENCODE_ALG_CCMP:
607 alg = "CCMP";
608 module = "lib80211_crypt_ccmp";
609 break;
610 default:
611 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
612 dev->name, ext->alg);
613 ret = -EINVAL;
614 goto done;
617 ops = lib80211_get_crypto_ops(alg);
618 if (ops == NULL) {
619 request_module(module);
620 ops = lib80211_get_crypto_ops(alg);
622 if (ops == NULL) {
623 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
624 dev->name, ext->alg);
625 ret = -EINVAL;
626 goto done;
629 if (*crypt == NULL || (*crypt)->ops != ops) {
630 struct lib80211_crypt_data *new_crypt;
632 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
634 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
635 if (new_crypt == NULL) {
636 ret = -ENOMEM;
637 goto done;
639 new_crypt->ops = ops;
640 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
641 new_crypt->priv = new_crypt->ops->init(idx);
642 if (new_crypt->priv == NULL) {
643 kfree(new_crypt);
644 ret = -EINVAL;
645 goto done;
647 *crypt = new_crypt;
650 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
651 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
652 (*crypt)->priv) < 0) {
653 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
654 ret = -EINVAL;
655 goto done;
658 skip_host_crypt:
659 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
660 ieee->crypt_info.tx_keyidx = idx;
661 sec.active_key = idx;
662 sec.flags |= SEC_ACTIVE_KEY;
665 if (ext->alg != IW_ENCODE_ALG_NONE) {
666 memcpy(sec.keys[idx], ext->key, ext->key_len);
667 sec.key_sizes[idx] = ext->key_len;
668 sec.flags |= (1 << idx);
669 if (ext->alg == IW_ENCODE_ALG_WEP) {
670 sec.encode_alg[idx] = SEC_ALG_WEP;
671 sec.flags |= SEC_LEVEL;
672 sec.level = SEC_LEVEL_1;
673 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
674 sec.encode_alg[idx] = SEC_ALG_TKIP;
675 sec.flags |= SEC_LEVEL;
676 sec.level = SEC_LEVEL_2;
677 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
678 sec.encode_alg[idx] = SEC_ALG_CCMP;
679 sec.flags |= SEC_LEVEL;
680 sec.level = SEC_LEVEL_3;
682 /* Don't set sec level for group keys. */
683 if (group_key)
684 sec.flags &= ~SEC_LEVEL;
686 done:
687 if (ieee->set_security)
688 ieee->set_security(ieee->dev, &sec);
691 * Do not reset port if card is in Managed mode since resetting will
692 * generate new IEEE 802.11 authentication which may end up in looping
693 * with IEEE 802.1X. If your hardware requires a reset after WEP
694 * configuration (for example... Prism2), implement the reset_port in
695 * the callbacks structures used to initialize the 802.11 stack.
697 if (ieee->reset_on_keychange &&
698 ieee->iw_mode != IW_MODE_INFRA &&
699 ieee->reset_port && ieee->reset_port(dev)) {
700 LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
701 return -EINVAL;
704 return ret;
707 int libipw_wx_get_encodeext(struct libipw_device *ieee,
708 struct iw_request_info *info,
709 union iwreq_data *wrqu, char *extra)
711 struct iw_point *encoding = &wrqu->encoding;
712 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
713 struct libipw_security *sec = &ieee->sec;
714 int idx, max_key_len;
716 max_key_len = encoding->length - sizeof(*ext);
717 if (max_key_len < 0)
718 return -EINVAL;
720 idx = encoding->flags & IW_ENCODE_INDEX;
721 if (idx) {
722 if (idx < 1 || idx > WEP_KEYS)
723 return -EINVAL;
724 idx--;
725 } else
726 idx = ieee->crypt_info.tx_keyidx;
728 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
729 ext->alg != IW_ENCODE_ALG_WEP)
730 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
731 return -EINVAL;
733 encoding->flags = idx + 1;
734 memset(ext, 0, sizeof(*ext));
736 if (!sec->enabled) {
737 ext->alg = IW_ENCODE_ALG_NONE;
738 ext->key_len = 0;
739 encoding->flags |= IW_ENCODE_DISABLED;
740 } else {
741 if (sec->encode_alg[idx] == SEC_ALG_WEP)
742 ext->alg = IW_ENCODE_ALG_WEP;
743 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
744 ext->alg = IW_ENCODE_ALG_TKIP;
745 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
746 ext->alg = IW_ENCODE_ALG_CCMP;
747 else
748 return -EINVAL;
750 ext->key_len = sec->key_sizes[idx];
751 memcpy(ext->key, sec->keys[idx], ext->key_len);
752 encoding->flags |= IW_ENCODE_ENABLED;
753 if (ext->key_len &&
754 (ext->alg == IW_ENCODE_ALG_TKIP ||
755 ext->alg == IW_ENCODE_ALG_CCMP))
756 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
760 return 0;
763 EXPORT_SYMBOL(libipw_wx_set_encodeext);
764 EXPORT_SYMBOL(libipw_wx_get_encodeext);
766 EXPORT_SYMBOL(libipw_wx_get_scan);
767 EXPORT_SYMBOL(libipw_wx_set_encode);
768 EXPORT_SYMBOL(libipw_wx_get_encode);