Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
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
310int 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
476int 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
518int 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
679int 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
735EXPORT_SYMBOL(libipw_wx_set_encodeext);
736EXPORT_SYMBOL(libipw_wx_get_encodeext);
737
738EXPORT_SYMBOL(libipw_wx_get_scan);
739EXPORT_SYMBOL(libipw_wx_set_encode);
740EXPORT_SYMBOL(libipw_wx_get_encode);