Linux Audio

Check our new training course

Loading...
  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
 44static const char *libipw_modes[] = {
 45	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
 46};
 47
 48static 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
 59static 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
264int 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	DECLARE_SSID_BUF(ssid);
276
277	LIBIPW_DEBUG_WX("Getting scan\n");
278
279	spin_lock_irqsave(&ieee->lock, flags);
280
281	list_for_each_entry(network, &ieee->network_list, list) {
282		i++;
283		if (stop - ev < SCAN_ITEM_SIZE) {
284			err = -E2BIG;
285			break;
286		}
287
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));
300		}
301	}
302
303	spin_unlock_irqrestore(&ieee->lock, flags);
304
305	wrqu->data.length = ev - extra;
306	wrqu->data.flags = 0;
307
308	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
309
310	return err;
311}
312
313int libipw_wx_set_encode(struct libipw_device *ieee,
314			    struct iw_request_info *info,
315			    union iwreq_data *wrqu, char *keybuf)
316{
317	struct iw_point *erq = &(wrqu->encoding);
318	struct net_device *dev = ieee->dev;
319	struct libipw_security sec = {
320		.flags = 0
321	};
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);
326
327	LIBIPW_DEBUG_WX("SET_ENCODE\n");
328
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;
338	}
339
340	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
341			   "provided" : "default");
342
343	crypt = &ieee->crypt_info.crypt[key];
344
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");
352
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]);
361			}
362		}
363
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;
369		}
370
371		goto done;
372	}
373
374	sec.enabled = 1;
375	sec.encrypt = 1;
376	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
377
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);
383	}
384
385	if (*crypt == NULL && host_crypto) {
386		struct lib80211_crypt_data *new_crypt;
387
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");
397		}
398
399		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
400			new_crypt->priv = new_crypt->ops->init(key);
401
402		if (!new_crypt->ops || !new_crypt->priv) {
403			kfree(new_crypt);
404			new_crypt = NULL;
405
406			printk(KERN_WARNING "%s: could not initialize WEP: "
407			       "load module lib80211_crypt_wep\n", dev->name);
408			return -EOPNOTSUPP;
409		}
410		*crypt = new_crypt;
411	}
412
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;
432
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);
446			}
447		}
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;
455		}
456	}
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");
465	}
466
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;
472
473      done:
474	if (ieee->set_security)
475		ieee->set_security(dev, &sec);
476
477	return 0;
478}
479
480int libipw_wx_get_encode(struct libipw_device *ieee,
481			    struct iw_request_info *info,
482			    union iwreq_data *wrqu, char *keybuf)
483{
484	struct iw_point *erq = &(wrqu->encoding);
485	int len, key;
486	struct lib80211_crypt_data *crypt;
487	struct libipw_security *sec = &ieee->sec;
488
489	LIBIPW_DEBUG_WX("GET_ENCODE\n");
490
491	key = erq->flags & IW_ENCODE_INDEX;
492	if (key) {
493		if (key > WEP_KEYS)
494			return -EINVAL;
495		key--;
496	} else
497		key = ieee->crypt_info.tx_keyidx;
498
499	crypt = ieee->crypt_info.crypt[key];
500	erq->flags = key + 1;
501
502	if (!sec->enabled) {
503		erq->length = 0;
504		erq->flags |= IW_ENCODE_DISABLED;
505		return 0;
506	}
507
508	len = sec->key_sizes[key];
509	memcpy(keybuf, sec->keys[key], len);
510
511	erq->length = len;
512	erq->flags |= IW_ENCODE_ENABLED;
513
514	if (ieee->open_wep)
515		erq->flags |= IW_ENCODE_OPEN;
516	else
517		erq->flags |= IW_ENCODE_RESTRICTED;
518
519	return 0;
520}
521
522int libipw_wx_set_encodeext(struct libipw_device *ieee,
523			       struct iw_request_info *info,
524			       union iwreq_data *wrqu, char *extra)
525{
526	struct net_device *dev = ieee->dev;
527	struct iw_point *encoding = &wrqu->encoding;
528	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529	int i, idx, ret = 0;
530	int group_key = 0;
531	const char *alg, *module;
532	struct lib80211_crypto_ops *ops;
533	struct lib80211_crypt_data **crypt;
534
535	struct libipw_security sec = {
536		.flags = 0,
537	};
538
539	idx = encoding->flags & IW_ENCODE_INDEX;
540	if (idx) {
541		if (idx < 1 || idx > WEP_KEYS)
542			return -EINVAL;
543		idx--;
544	} else
545		idx = ieee->crypt_info.tx_keyidx;
546
547	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
548		crypt = &ieee->crypt_info.crypt[idx];
549		group_key = 1;
550	} else {
551		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
552		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
553			return -EINVAL;
554		if (ieee->iw_mode == IW_MODE_INFRA)
555			crypt = &ieee->crypt_info.crypt[idx];
556		else
557			return -EINVAL;
558	}
559
560	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561	if ((encoding->flags & IW_ENCODE_DISABLED) ||
562	    ext->alg == IW_ENCODE_ALG_NONE) {
563		if (*crypt)
564			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
565
566		for (i = 0; i < WEP_KEYS; i++)
567			if (ieee->crypt_info.crypt[i] != NULL)
568				break;
569
570		if (i == WEP_KEYS) {
571			sec.enabled = 0;
572			sec.encrypt = 0;
573			sec.level = SEC_LEVEL_0;
574			sec.flags |= SEC_LEVEL;
575		}
576		goto done;
577	}
578
579	sec.enabled = 1;
580	sec.encrypt = 1;
581
582	if (group_key ? !ieee->host_mc_decrypt :
583	    !(ieee->host_encrypt || ieee->host_decrypt ||
584	      ieee->host_encrypt_msdu))
585		goto skip_host_crypt;
586
587	switch (ext->alg) {
588	case IW_ENCODE_ALG_WEP:
589		alg = "WEP";
590		module = "lib80211_crypt_wep";
591		break;
592	case IW_ENCODE_ALG_TKIP:
593		alg = "TKIP";
594		module = "lib80211_crypt_tkip";
595		break;
596	case IW_ENCODE_ALG_CCMP:
597		alg = "CCMP";
598		module = "lib80211_crypt_ccmp";
599		break;
600	default:
601		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
602				   dev->name, ext->alg);
603		ret = -EINVAL;
604		goto done;
605	}
606
607	ops = lib80211_get_crypto_ops(alg);
608	if (ops == NULL) {
609		request_module(module);
610		ops = lib80211_get_crypto_ops(alg);
611	}
612	if (ops == NULL) {
613		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
614				   dev->name, ext->alg);
615		ret = -EINVAL;
616		goto done;
617	}
618
619	if (*crypt == NULL || (*crypt)->ops != ops) {
620		struct lib80211_crypt_data *new_crypt;
621
622		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
623
624		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
625		if (new_crypt == NULL) {
626			ret = -ENOMEM;
627			goto done;
628		}
629		new_crypt->ops = ops;
630		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
631			new_crypt->priv = new_crypt->ops->init(idx);
632		if (new_crypt->priv == NULL) {
633			kfree(new_crypt);
634			ret = -EINVAL;
635			goto done;
636		}
637		*crypt = new_crypt;
638	}
639
640	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642				   (*crypt)->priv) < 0) {
643		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
644		ret = -EINVAL;
645		goto done;
646	}
647
648      skip_host_crypt:
649	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650		ieee->crypt_info.tx_keyidx = idx;
651		sec.active_key = idx;
652		sec.flags |= SEC_ACTIVE_KEY;
653	}
654
655	if (ext->alg != IW_ENCODE_ALG_NONE) {
656		memcpy(sec.keys[idx], ext->key, ext->key_len);
657		sec.key_sizes[idx] = ext->key_len;
658		sec.flags |= (1 << idx);
659		if (ext->alg == IW_ENCODE_ALG_WEP) {
660			sec.encode_alg[idx] = SEC_ALG_WEP;
661			sec.flags |= SEC_LEVEL;
662			sec.level = SEC_LEVEL_1;
663		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664			sec.encode_alg[idx] = SEC_ALG_TKIP;
665			sec.flags |= SEC_LEVEL;
666			sec.level = SEC_LEVEL_2;
667		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668			sec.encode_alg[idx] = SEC_ALG_CCMP;
669			sec.flags |= SEC_LEVEL;
670			sec.level = SEC_LEVEL_3;
671		}
672		/* Don't set sec level for group keys. */
673		if (group_key)
674			sec.flags &= ~SEC_LEVEL;
675	}
676      done:
677	if (ieee->set_security)
678		ieee->set_security(ieee->dev, &sec);
679
680	return ret;
681}
682
683int libipw_wx_get_encodeext(struct libipw_device *ieee,
684			       struct iw_request_info *info,
685			       union iwreq_data *wrqu, char *extra)
686{
687	struct iw_point *encoding = &wrqu->encoding;
688	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
689	struct libipw_security *sec = &ieee->sec;
690	int idx, max_key_len;
691
692	max_key_len = encoding->length - sizeof(*ext);
693	if (max_key_len < 0)
694		return -EINVAL;
695
696	idx = encoding->flags & IW_ENCODE_INDEX;
697	if (idx) {
698		if (idx < 1 || idx > WEP_KEYS)
699			return -EINVAL;
700		idx--;
701	} else
702		idx = ieee->crypt_info.tx_keyidx;
703
704	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
705	    ext->alg != IW_ENCODE_ALG_WEP)
706		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707			return -EINVAL;
708
709	encoding->flags = idx + 1;
710	memset(ext, 0, sizeof(*ext));
711
712	if (!sec->enabled) {
713		ext->alg = IW_ENCODE_ALG_NONE;
714		ext->key_len = 0;
715		encoding->flags |= IW_ENCODE_DISABLED;
716	} else {
717		if (sec->encode_alg[idx] == SEC_ALG_WEP)
718			ext->alg = IW_ENCODE_ALG_WEP;
719		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720			ext->alg = IW_ENCODE_ALG_TKIP;
721		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722			ext->alg = IW_ENCODE_ALG_CCMP;
723		else
724			return -EINVAL;
725
726		ext->key_len = sec->key_sizes[idx];
727		memcpy(ext->key, sec->keys[idx], ext->key_len);
728		encoding->flags |= IW_ENCODE_ENABLED;
729		if (ext->key_len &&
730		    (ext->alg == IW_ENCODE_ALG_TKIP ||
731		     ext->alg == IW_ENCODE_ALG_CCMP))
732			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
733
734	}
735
736	return 0;
737}
738
739EXPORT_SYMBOL(libipw_wx_set_encodeext);
740EXPORT_SYMBOL(libipw_wx_get_encodeext);
741
742EXPORT_SYMBOL(libipw_wx_get_scan);
743EXPORT_SYMBOL(libipw_wx_set_encode);
744EXPORT_SYMBOL(libipw_wx_get_encode);