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 libipw_security *sec = &ieee->sec;
483 
484 	LIBIPW_DEBUG_WX("GET_ENCODE\n");
485 
486 	key = erq->flags & IW_ENCODE_INDEX;
487 	if (key) {
488 		if (key > WEP_KEYS)
489 			return -EINVAL;
490 		key--;
491 	} else
492 		key = ieee->crypt_info.tx_keyidx;
493 
494 	erq->flags = key + 1;
495 
496 	if (!sec->enabled) {
497 		erq->length = 0;
498 		erq->flags |= IW_ENCODE_DISABLED;
499 		return 0;
500 	}
501 
502 	len = sec->key_sizes[key];
503 	memcpy(keybuf, sec->keys[key], len);
504 
505 	erq->length = len;
506 	erq->flags |= IW_ENCODE_ENABLED;
507 
508 	if (ieee->open_wep)
509 		erq->flags |= IW_ENCODE_OPEN;
510 	else
511 		erq->flags |= IW_ENCODE_RESTRICTED;
512 
513 	return 0;
514 }
515 
516 int libipw_wx_set_encodeext(struct libipw_device *ieee,
517 			       struct iw_request_info *info,
518 			       union iwreq_data *wrqu, char *extra)
519 {
520 	struct net_device *dev = ieee->dev;
521 	struct iw_point *encoding = &wrqu->encoding;
522 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
523 	int i, idx, ret = 0;
524 	int group_key = 0;
525 	const char *alg, *module;
526 	struct lib80211_crypto_ops *ops;
527 	struct lib80211_crypt_data **crypt;
528 
529 	struct libipw_security sec = {
530 		.flags = 0,
531 	};
532 
533 	idx = encoding->flags & IW_ENCODE_INDEX;
534 	if (idx) {
535 		if (idx < 1 || idx > WEP_KEYS)
536 			return -EINVAL;
537 		idx--;
538 	} else
539 		idx = ieee->crypt_info.tx_keyidx;
540 
541 	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
542 		crypt = &ieee->crypt_info.crypt[idx];
543 		group_key = 1;
544 	} else {
545 		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
546 		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
547 			return -EINVAL;
548 		if (ieee->iw_mode == IW_MODE_INFRA)
549 			crypt = &ieee->crypt_info.crypt[idx];
550 		else
551 			return -EINVAL;
552 	}
553 
554 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
555 	if ((encoding->flags & IW_ENCODE_DISABLED) ||
556 	    ext->alg == IW_ENCODE_ALG_NONE) {
557 		if (*crypt)
558 			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
559 
560 		for (i = 0; i < WEP_KEYS; i++)
561 			if (ieee->crypt_info.crypt[i] != NULL)
562 				break;
563 
564 		if (i == WEP_KEYS) {
565 			sec.enabled = 0;
566 			sec.encrypt = 0;
567 			sec.level = SEC_LEVEL_0;
568 			sec.flags |= SEC_LEVEL;
569 		}
570 		goto done;
571 	}
572 
573 	sec.enabled = 1;
574 	sec.encrypt = 1;
575 
576 	if (group_key ? !ieee->host_mc_decrypt :
577 	    !(ieee->host_encrypt || ieee->host_decrypt ||
578 	      ieee->host_encrypt_msdu))
579 		goto skip_host_crypt;
580 
581 	switch (ext->alg) {
582 	case IW_ENCODE_ALG_WEP:
583 		alg = "WEP";
584 		module = "lib80211_crypt_wep";
585 		break;
586 	case IW_ENCODE_ALG_TKIP:
587 		alg = "TKIP";
588 		module = "lib80211_crypt_tkip";
589 		break;
590 	case IW_ENCODE_ALG_CCMP:
591 		alg = "CCMP";
592 		module = "lib80211_crypt_ccmp";
593 		break;
594 	default:
595 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
596 				   dev->name, ext->alg);
597 		ret = -EINVAL;
598 		goto done;
599 	}
600 
601 	ops = lib80211_get_crypto_ops(alg);
602 	if (ops == NULL) {
603 		request_module(module);
604 		ops = lib80211_get_crypto_ops(alg);
605 	}
606 	if (ops == NULL) {
607 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
608 				   dev->name, ext->alg);
609 		ret = -EINVAL;
610 		goto done;
611 	}
612 
613 	if (*crypt == NULL || (*crypt)->ops != ops) {
614 		struct lib80211_crypt_data *new_crypt;
615 
616 		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
617 
618 		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
619 		if (new_crypt == NULL) {
620 			ret = -ENOMEM;
621 			goto done;
622 		}
623 		new_crypt->ops = ops;
624 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
625 			new_crypt->priv = new_crypt->ops->init(idx);
626 		if (new_crypt->priv == NULL) {
627 			kfree(new_crypt);
628 			ret = -EINVAL;
629 			goto done;
630 		}
631 		*crypt = new_crypt;
632 	}
633 
634 	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
635 	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
636 				   (*crypt)->priv) < 0) {
637 		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
638 		ret = -EINVAL;
639 		goto done;
640 	}
641 
642       skip_host_crypt:
643 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
644 		ieee->crypt_info.tx_keyidx = idx;
645 		sec.active_key = idx;
646 		sec.flags |= SEC_ACTIVE_KEY;
647 	}
648 
649 	if (ext->alg != IW_ENCODE_ALG_NONE) {
650 		memcpy(sec.keys[idx], ext->key, ext->key_len);
651 		sec.key_sizes[idx] = ext->key_len;
652 		sec.flags |= (1 << idx);
653 		if (ext->alg == IW_ENCODE_ALG_WEP) {
654 			sec.encode_alg[idx] = SEC_ALG_WEP;
655 			sec.flags |= SEC_LEVEL;
656 			sec.level = SEC_LEVEL_1;
657 		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
658 			sec.encode_alg[idx] = SEC_ALG_TKIP;
659 			sec.flags |= SEC_LEVEL;
660 			sec.level = SEC_LEVEL_2;
661 		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
662 			sec.encode_alg[idx] = SEC_ALG_CCMP;
663 			sec.flags |= SEC_LEVEL;
664 			sec.level = SEC_LEVEL_3;
665 		}
666 		/* Don't set sec level for group keys. */
667 		if (group_key)
668 			sec.flags &= ~SEC_LEVEL;
669 	}
670       done:
671 	if (ieee->set_security)
672 		ieee->set_security(dev, &sec);
673 
674 	return ret;
675 }
676 
677 int libipw_wx_get_encodeext(struct libipw_device *ieee,
678 			       struct iw_request_info *info,
679 			       union iwreq_data *wrqu, char *extra)
680 {
681 	struct iw_point *encoding = &wrqu->encoding;
682 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
683 	struct libipw_security *sec = &ieee->sec;
684 	int idx, max_key_len;
685 
686 	max_key_len = encoding->length - sizeof(*ext);
687 	if (max_key_len < 0)
688 		return -EINVAL;
689 
690 	idx = encoding->flags & IW_ENCODE_INDEX;
691 	if (idx) {
692 		if (idx < 1 || idx > WEP_KEYS)
693 			return -EINVAL;
694 		idx--;
695 	} else
696 		idx = ieee->crypt_info.tx_keyidx;
697 
698 	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
699 	    ext->alg != IW_ENCODE_ALG_WEP)
700 		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
701 			return -EINVAL;
702 
703 	encoding->flags = idx + 1;
704 	memset(ext, 0, sizeof(*ext));
705 
706 	if (!sec->enabled) {
707 		ext->alg = IW_ENCODE_ALG_NONE;
708 		ext->key_len = 0;
709 		encoding->flags |= IW_ENCODE_DISABLED;
710 	} else {
711 		if (sec->encode_alg[idx] == SEC_ALG_WEP)
712 			ext->alg = IW_ENCODE_ALG_WEP;
713 		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
714 			ext->alg = IW_ENCODE_ALG_TKIP;
715 		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
716 			ext->alg = IW_ENCODE_ALG_CCMP;
717 		else
718 			return -EINVAL;
719 
720 		ext->key_len = sec->key_sizes[idx];
721 		memcpy(ext->key, sec->keys[idx], ext->key_len);
722 		encoding->flags |= IW_ENCODE_ENABLED;
723 		if (ext->key_len &&
724 		    (ext->alg == IW_ENCODE_ALG_TKIP ||
725 		     ext->alg == IW_ENCODE_ALG_CCMP))
726 			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
727 
728 	}
729 
730 	return 0;
731 }
732 
733 EXPORT_SYMBOL(libipw_wx_set_encodeext);
734 EXPORT_SYMBOL(libipw_wx_get_encodeext);
735 
736 EXPORT_SYMBOL(libipw_wx_get_scan);
737 EXPORT_SYMBOL(libipw_wx_set_encode);
738 EXPORT_SYMBOL(libipw_wx_get_encode);
739