1 /******************************************************************************
2 
3   Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
4 
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>
10 
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.
14 
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.
19 
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.
23 
24   The full GNU General Public License is included in this distribution in the
25   file called LICENSE.
26 
27   Contact Information:
28   Intel Linux Wireless <ilw@linux.intel.com>
29   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 
31 ******************************************************************************/
32 
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>
38 
39 #include <net/lib80211.h>
40 #include <linux/wireless.h>
41 
42 #include "libipw.h"
43 
44 static const char *libipw_modes[] = {
45 	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
46 };
47 
48 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
49 {
50 	unsigned long end = jiffies;
51 
52 	if (end >= start)
53 		return jiffies_to_msecs(end - start);
54 
55 	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
56 }
57 
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)
63 {
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;
70 
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);
76 
77 	/* Remaining entries will be displayed in the order we provide them */
78 
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);
85 
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);
91 
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;
99 
100 		start = iwe_stream_add_event(info, start, stop,
101 					     &iwe, IW_EV_UINT_LEN);
102 	}
103 
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);
111 
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);
121 
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;
129 
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);
142 	}
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);
150 	}
151 	/* Check if we added any rate */
152 	if ((current_val - start) > iwe_stream_lcp_len(info))
153 		start = current_val;
154 
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;
159 
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;
183 	}
184 
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;
190 	}
191 
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;
197 	}
198 
199 	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
200 
201 	iwe.cmd = IWEVCUSTOM;
202 	p = custom;
203 
204 	iwe.u.data.length = p - custom;
205 	if (iwe.u.data.length)
206 		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
207 
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);
215 	}
216 
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);
224 	}
225 
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);
236 
237 	/* Add spectrum management information */
238 	iwe.cmd = -1;
239 	p = custom;
240 	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241 
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 ");
246 	}
247 
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 ");
252 	}
253 
254 	if (iwe.cmd == IWEVCUSTOM) {
255 		iwe.u.data.length = p - custom;
256 		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
257 	}
258 
259 	return start;
260 }
261 
262 #define SCAN_ITEM_SIZE 128
263 
264 int libipw_wx_get_scan(struct libipw_device *ieee,
265 			  struct iw_request_info *info,
266 			  union iwreq_data *wrqu, char *extra)
267 {
268 	struct libipw_network *network;
269 	unsigned long flags;
270 	int err = 0;
271 
272 	char *ev = extra;
273 	char *stop = ev + wrqu->data.length;
274 	int i = 0;
275 
276 	LIBIPW_DEBUG_WX("Getting scan\n");
277 
278 	spin_lock_irqsave(&ieee->lock, flags);
279 
280 	list_for_each_entry(network, &ieee->network_list, list) {
281 		i++;
282 		if (stop - ev < SCAN_ITEM_SIZE) {
283 			err = -E2BIG;
284 			break;
285 		}
286 
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 '%*pE (%pM)' due to age (%ums).\n",
293 					  network->ssid_len, network->ssid,
294 					  network->bssid,
295 					  elapsed_jiffies_msecs(
296 					               network->last_scanned));
297 		}
298 	}
299 
300 	spin_unlock_irqrestore(&ieee->lock, flags);
301 
302 	wrqu->data.length = ev - extra;
303 	wrqu->data.flags = 0;
304 
305 	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
306 
307 	return err;
308 }
309 
310 int libipw_wx_set_encode(struct libipw_device *ieee,
311 			    struct iw_request_info *info,
312 			    union iwreq_data *wrqu, char *keybuf)
313 {
314 	struct iw_point *erq = &(wrqu->encoding);
315 	struct net_device *dev = ieee->dev;
316 	struct libipw_security sec = {
317 		.flags = 0
318 	};
319 	int i, key, key_provided, len;
320 	struct lib80211_crypt_data **crypt;
321 	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
322 
323 	LIBIPW_DEBUG_WX("SET_ENCODE\n");
324 
325 	key = erq->flags & IW_ENCODE_INDEX;
326 	if (key) {
327 		if (key > WEP_KEYS)
328 			return -EINVAL;
329 		key--;
330 		key_provided = 1;
331 	} else {
332 		key_provided = 0;
333 		key = ieee->crypt_info.tx_keyidx;
334 	}
335 
336 	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
337 			   "provided" : "default");
338 
339 	crypt = &ieee->crypt_info.crypt[key];
340 
341 	if (erq->flags & IW_ENCODE_DISABLED) {
342 		if (key_provided && *crypt) {
343 			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
344 					   key);
345 			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
346 		} else
347 			LIBIPW_DEBUG_WX("Disabling encryption.\n");
348 
349 		/* Check all the keys to see if any are still configured,
350 		 * and if no key index was provided, de-init them all */
351 		for (i = 0; i < WEP_KEYS; i++) {
352 			if (ieee->crypt_info.crypt[i] != NULL) {
353 				if (key_provided)
354 					break;
355 				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
356 							       &ieee->crypt_info.crypt[i]);
357 			}
358 		}
359 
360 		if (i == WEP_KEYS) {
361 			sec.enabled = 0;
362 			sec.encrypt = 0;
363 			sec.level = SEC_LEVEL_0;
364 			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
365 		}
366 
367 		goto done;
368 	}
369 
370 	sec.enabled = 1;
371 	sec.encrypt = 1;
372 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
373 
374 	if (*crypt != NULL && (*crypt)->ops != NULL &&
375 	    strcmp((*crypt)->ops->name, "WEP") != 0) {
376 		/* changing to use WEP; deinit previously used algorithm
377 		 * on this key */
378 		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
379 	}
380 
381 	if (*crypt == NULL && host_crypto) {
382 		struct lib80211_crypt_data *new_crypt;
383 
384 		/* take WEP into use */
385 		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
386 				    GFP_KERNEL);
387 		if (new_crypt == NULL)
388 			return -ENOMEM;
389 		new_crypt->ops = lib80211_get_crypto_ops("WEP");
390 		if (!new_crypt->ops) {
391 			request_module("lib80211_crypt_wep");
392 			new_crypt->ops = lib80211_get_crypto_ops("WEP");
393 		}
394 
395 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
396 			new_crypt->priv = new_crypt->ops->init(key);
397 
398 		if (!new_crypt->ops || !new_crypt->priv) {
399 			kfree(new_crypt);
400 			new_crypt = NULL;
401 
402 			printk(KERN_WARNING "%s: could not initialize WEP: "
403 			       "load module lib80211_crypt_wep\n", dev->name);
404 			return -EOPNOTSUPP;
405 		}
406 		*crypt = new_crypt;
407 	}
408 
409 	/* If a new key was provided, set it up */
410 	if (erq->length > 0) {
411 		len = erq->length <= 5 ? 5 : 13;
412 		memcpy(sec.keys[key], keybuf, erq->length);
413 		if (len > erq->length)
414 			memset(sec.keys[key] + erq->length, 0,
415 			       len - erq->length);
416 		LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
417 				   key, len, sec.keys[key],
418 				   erq->length, len);
419 		sec.key_sizes[key] = len;
420 		if (*crypt)
421 			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
422 					       (*crypt)->priv);
423 		sec.flags |= (1 << key);
424 		/* This ensures a key will be activated if no key is
425 		 * explicitly set */
426 		if (key == sec.active_key)
427 			sec.flags |= SEC_ACTIVE_KEY;
428 
429 	} else {
430 		if (host_crypto) {
431 			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
432 						     NULL, (*crypt)->priv);
433 			if (len == 0) {
434 				/* Set a default key of all 0 */
435 				LIBIPW_DEBUG_WX("Setting key %d to all "
436 						   "zero.\n", key);
437 				memset(sec.keys[key], 0, 13);
438 				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
439 						       (*crypt)->priv);
440 				sec.key_sizes[key] = 13;
441 				sec.flags |= (1 << key);
442 			}
443 		}
444 		/* No key data - just set the default TX key index */
445 		if (key_provided) {
446 			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
447 					   "key.\n", key);
448 			ieee->crypt_info.tx_keyidx = key;
449 			sec.active_key = key;
450 			sec.flags |= SEC_ACTIVE_KEY;
451 		}
452 	}
453 	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
454 		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
455 		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
456 		    WLAN_AUTH_SHARED_KEY;
457 		sec.flags |= SEC_AUTH_MODE;
458 		LIBIPW_DEBUG_WX("Auth: %s\n",
459 				   sec.auth_mode == WLAN_AUTH_OPEN ?
460 				   "OPEN" : "SHARED KEY");
461 	}
462 
463 	/* For now we just support WEP, so only set that security level...
464 	 * TODO: When WPA is added this is one place that needs to change */
465 	sec.flags |= SEC_LEVEL;
466 	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
467 	sec.encode_alg[key] = SEC_ALG_WEP;
468 
469       done:
470 	if (ieee->set_security)
471 		ieee->set_security(dev, &sec);
472 
473 	return 0;
474 }
475 
476 int libipw_wx_get_encode(struct libipw_device *ieee,
477 			    struct iw_request_info *info,
478 			    union iwreq_data *wrqu, char *keybuf)
479 {
480 	struct iw_point *erq = &(wrqu->encoding);
481 	int len, key;
482 	struct lib80211_crypt_data *crypt;
483 	struct libipw_security *sec = &ieee->sec;
484 
485 	LIBIPW_DEBUG_WX("GET_ENCODE\n");
486 
487 	key = erq->flags & IW_ENCODE_INDEX;
488 	if (key) {
489 		if (key > WEP_KEYS)
490 			return -EINVAL;
491 		key--;
492 	} else
493 		key = ieee->crypt_info.tx_keyidx;
494 
495 	crypt = ieee->crypt_info.crypt[key];
496 	erq->flags = key + 1;
497 
498 	if (!sec->enabled) {
499 		erq->length = 0;
500 		erq->flags |= IW_ENCODE_DISABLED;
501 		return 0;
502 	}
503 
504 	len = sec->key_sizes[key];
505 	memcpy(keybuf, sec->keys[key], len);
506 
507 	erq->length = len;
508 	erq->flags |= IW_ENCODE_ENABLED;
509 
510 	if (ieee->open_wep)
511 		erq->flags |= IW_ENCODE_OPEN;
512 	else
513 		erq->flags |= IW_ENCODE_RESTRICTED;
514 
515 	return 0;
516 }
517 
518 int libipw_wx_set_encodeext(struct libipw_device *ieee,
519 			       struct iw_request_info *info,
520 			       union iwreq_data *wrqu, char *extra)
521 {
522 	struct net_device *dev = ieee->dev;
523 	struct iw_point *encoding = &wrqu->encoding;
524 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
525 	int i, idx, ret = 0;
526 	int group_key = 0;
527 	const char *alg, *module;
528 	struct lib80211_crypto_ops *ops;
529 	struct lib80211_crypt_data **crypt;
530 
531 	struct libipw_security sec = {
532 		.flags = 0,
533 	};
534 
535 	idx = encoding->flags & IW_ENCODE_INDEX;
536 	if (idx) {
537 		if (idx < 1 || idx > WEP_KEYS)
538 			return -EINVAL;
539 		idx--;
540 	} else
541 		idx = ieee->crypt_info.tx_keyidx;
542 
543 	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
544 		crypt = &ieee->crypt_info.crypt[idx];
545 		group_key = 1;
546 	} else {
547 		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
548 		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
549 			return -EINVAL;
550 		if (ieee->iw_mode == IW_MODE_INFRA)
551 			crypt = &ieee->crypt_info.crypt[idx];
552 		else
553 			return -EINVAL;
554 	}
555 
556 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
557 	if ((encoding->flags & IW_ENCODE_DISABLED) ||
558 	    ext->alg == IW_ENCODE_ALG_NONE) {
559 		if (*crypt)
560 			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
561 
562 		for (i = 0; i < WEP_KEYS; i++)
563 			if (ieee->crypt_info.crypt[i] != NULL)
564 				break;
565 
566 		if (i == WEP_KEYS) {
567 			sec.enabled = 0;
568 			sec.encrypt = 0;
569 			sec.level = SEC_LEVEL_0;
570 			sec.flags |= SEC_LEVEL;
571 		}
572 		goto done;
573 	}
574 
575 	sec.enabled = 1;
576 	sec.encrypt = 1;
577 
578 	if (group_key ? !ieee->host_mc_decrypt :
579 	    !(ieee->host_encrypt || ieee->host_decrypt ||
580 	      ieee->host_encrypt_msdu))
581 		goto skip_host_crypt;
582 
583 	switch (ext->alg) {
584 	case IW_ENCODE_ALG_WEP:
585 		alg = "WEP";
586 		module = "lib80211_crypt_wep";
587 		break;
588 	case IW_ENCODE_ALG_TKIP:
589 		alg = "TKIP";
590 		module = "lib80211_crypt_tkip";
591 		break;
592 	case IW_ENCODE_ALG_CCMP:
593 		alg = "CCMP";
594 		module = "lib80211_crypt_ccmp";
595 		break;
596 	default:
597 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
598 				   dev->name, ext->alg);
599 		ret = -EINVAL;
600 		goto done;
601 	}
602 
603 	ops = lib80211_get_crypto_ops(alg);
604 	if (ops == NULL) {
605 		request_module(module);
606 		ops = lib80211_get_crypto_ops(alg);
607 	}
608 	if (ops == NULL) {
609 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
610 				   dev->name, ext->alg);
611 		ret = -EINVAL;
612 		goto done;
613 	}
614 
615 	if (*crypt == NULL || (*crypt)->ops != ops) {
616 		struct lib80211_crypt_data *new_crypt;
617 
618 		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
619 
620 		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
621 		if (new_crypt == NULL) {
622 			ret = -ENOMEM;
623 			goto done;
624 		}
625 		new_crypt->ops = ops;
626 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
627 			new_crypt->priv = new_crypt->ops->init(idx);
628 		if (new_crypt->priv == NULL) {
629 			kfree(new_crypt);
630 			ret = -EINVAL;
631 			goto done;
632 		}
633 		*crypt = new_crypt;
634 	}
635 
636 	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
637 	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
638 				   (*crypt)->priv) < 0) {
639 		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
640 		ret = -EINVAL;
641 		goto done;
642 	}
643 
644       skip_host_crypt:
645 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
646 		ieee->crypt_info.tx_keyidx = idx;
647 		sec.active_key = idx;
648 		sec.flags |= SEC_ACTIVE_KEY;
649 	}
650 
651 	if (ext->alg != IW_ENCODE_ALG_NONE) {
652 		memcpy(sec.keys[idx], ext->key, ext->key_len);
653 		sec.key_sizes[idx] = ext->key_len;
654 		sec.flags |= (1 << idx);
655 		if (ext->alg == IW_ENCODE_ALG_WEP) {
656 			sec.encode_alg[idx] = SEC_ALG_WEP;
657 			sec.flags |= SEC_LEVEL;
658 			sec.level = SEC_LEVEL_1;
659 		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
660 			sec.encode_alg[idx] = SEC_ALG_TKIP;
661 			sec.flags |= SEC_LEVEL;
662 			sec.level = SEC_LEVEL_2;
663 		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
664 			sec.encode_alg[idx] = SEC_ALG_CCMP;
665 			sec.flags |= SEC_LEVEL;
666 			sec.level = SEC_LEVEL_3;
667 		}
668 		/* Don't set sec level for group keys. */
669 		if (group_key)
670 			sec.flags &= ~SEC_LEVEL;
671 	}
672       done:
673 	if (ieee->set_security)
674 		ieee->set_security(dev, &sec);
675 
676 	return ret;
677 }
678 
679 int libipw_wx_get_encodeext(struct libipw_device *ieee,
680 			       struct iw_request_info *info,
681 			       union iwreq_data *wrqu, char *extra)
682 {
683 	struct iw_point *encoding = &wrqu->encoding;
684 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
685 	struct libipw_security *sec = &ieee->sec;
686 	int idx, max_key_len;
687 
688 	max_key_len = encoding->length - sizeof(*ext);
689 	if (max_key_len < 0)
690 		return -EINVAL;
691 
692 	idx = encoding->flags & IW_ENCODE_INDEX;
693 	if (idx) {
694 		if (idx < 1 || idx > WEP_KEYS)
695 			return -EINVAL;
696 		idx--;
697 	} else
698 		idx = ieee->crypt_info.tx_keyidx;
699 
700 	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
701 	    ext->alg != IW_ENCODE_ALG_WEP)
702 		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
703 			return -EINVAL;
704 
705 	encoding->flags = idx + 1;
706 	memset(ext, 0, sizeof(*ext));
707 
708 	if (!sec->enabled) {
709 		ext->alg = IW_ENCODE_ALG_NONE;
710 		ext->key_len = 0;
711 		encoding->flags |= IW_ENCODE_DISABLED;
712 	} else {
713 		if (sec->encode_alg[idx] == SEC_ALG_WEP)
714 			ext->alg = IW_ENCODE_ALG_WEP;
715 		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
716 			ext->alg = IW_ENCODE_ALG_TKIP;
717 		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
718 			ext->alg = IW_ENCODE_ALG_CCMP;
719 		else
720 			return -EINVAL;
721 
722 		ext->key_len = sec->key_sizes[idx];
723 		memcpy(ext->key, sec->keys[idx], ext->key_len);
724 		encoding->flags |= IW_ENCODE_ENABLED;
725 		if (ext->key_len &&
726 		    (ext->alg == IW_ENCODE_ALG_TKIP ||
727 		     ext->alg == IW_ENCODE_ALG_CCMP))
728 			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
729 
730 	}
731 
732 	return 0;
733 }
734 
735 EXPORT_SYMBOL(libipw_wx_set_encodeext);
736 EXPORT_SYMBOL(libipw_wx_get_encodeext);
737 
738 EXPORT_SYMBOL(libipw_wx_get_scan);
739 EXPORT_SYMBOL(libipw_wx_set_encode);
740 EXPORT_SYMBOL(libipw_wx_get_encode);
741