• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/net/wireless/ipw2x00/
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/kmod.h>
34#include <linux/slab.h>
35#include <linux/module.h>
36#include <linux/jiffies.h>
37
38#include <net/lib80211.h>
39#include <linux/wireless.h>
40
41#include "libipw.h"
42
43static const char *libipw_modes[] = {
44	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
45};
46
47static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
48{
49	unsigned long end = jiffies;
50
51	if (end >= start)
52		return jiffies_to_msecs(end - start);
53
54	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
55}
56
57#define MAX_CUSTOM_LEN 64
58static char *libipw_translate_scan(struct libipw_device *ieee,
59				      char *start, char *stop,
60				      struct libipw_network *network,
61				      struct iw_request_info *info)
62{
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;
69
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);
75
76	/* Remaining entries will be displayed in the order we provide them */
77
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);
84
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);
90
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;
98
99		start = iwe_stream_add_event(info, start, stop,
100					     &iwe, IW_EV_UINT_LEN);
101	}
102
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);
110
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);
120
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;
128
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);
141	}
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);
149	}
150	/* Check if we added any rate */
151	if ((current_val - start) > iwe_stream_lcp_len(info))
152		start = current_val;
153
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;
158
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;
182	}
183
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;
189	}
190
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;
196	}
197
198	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
199
200	iwe.cmd = IWEVCUSTOM;
201	p = custom;
202
203	iwe.u.data.length = p - custom;
204	if (iwe.u.data.length)
205		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
206
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);
214	}
215
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);
223	}
224
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);
235
236	/* Add spectrum management information */
237	iwe.cmd = -1;
238	p = custom;
239	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
240
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 ");
245	}
246
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 ");
251	}
252
253	if (iwe.cmd == IWEVCUSTOM) {
254		iwe.u.data.length = p - custom;
255		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
256	}
257
258	return start;
259}
260
261#define SCAN_ITEM_SIZE 128
262
263int libipw_wx_get_scan(struct libipw_device *ieee,
264			  struct iw_request_info *info,
265			  union iwreq_data *wrqu, char *extra)
266{
267	struct libipw_network *network;
268	unsigned long flags;
269	int err = 0;
270
271	char *ev = extra;
272	char *stop = ev + wrqu->data.length;
273	int i = 0;
274	DECLARE_SSID_BUF(ssid);
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 '%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));
299		}
300	}
301
302	spin_unlock_irqrestore(&ieee->lock, flags);
303
304	wrqu->data.length = ev - extra;
305	wrqu->data.flags = 0;
306
307	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
308
309	return err;
310}
311
312int libipw_wx_set_encode(struct libipw_device *ieee,
313			    struct iw_request_info *info,
314			    union iwreq_data *wrqu, char *keybuf)
315{
316	struct iw_point *erq = &(wrqu->encoding);
317	struct net_device *dev = ieee->dev;
318	struct libipw_security sec = {
319		.flags = 0
320	};
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);
325
326	LIBIPW_DEBUG_WX("SET_ENCODE\n");
327
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;
337	}
338
339	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
340			   "provided" : "default");
341
342	crypt = &ieee->crypt_info.crypt[key];
343
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");
351
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]);
360			}
361		}
362
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;
368		}
369
370		goto done;
371	}
372
373	sec.enabled = 1;
374	sec.encrypt = 1;
375	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
376
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);
382	}
383
384	if (*crypt == NULL && host_crypto) {
385		struct lib80211_crypt_data *new_crypt;
386
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");
396		}
397
398		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
399			new_crypt->priv = new_crypt->ops->init(key);
400
401		if (!new_crypt->ops || !new_crypt->priv) {
402			kfree(new_crypt);
403			new_crypt = NULL;
404
405			printk(KERN_WARNING "%s: could not initialize WEP: "
406			       "load module lib80211_crypt_wep\n", dev->name);
407			return -EOPNOTSUPP;
408		}
409		*crypt = new_crypt;
410	}
411
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;
431
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);
445			}
446		}
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;
454		}
455	}
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");
464	}
465
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;
471
472      done:
473	if (ieee->set_security)
474		ieee->set_security(dev, &sec);
475
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;
486	}
487	return 0;
488}
489
490int libipw_wx_get_encode(struct libipw_device *ieee,
491			    struct iw_request_info *info,
492			    union iwreq_data *wrqu, char *keybuf)
493{
494	struct iw_point *erq = &(wrqu->encoding);
495	int len, key;
496	struct lib80211_crypt_data *crypt;
497	struct libipw_security *sec = &ieee->sec;
498
499	LIBIPW_DEBUG_WX("GET_ENCODE\n");
500
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;
508
509	crypt = ieee->crypt_info.crypt[key];
510	erq->flags = key + 1;
511
512	if (!sec->enabled) {
513		erq->length = 0;
514		erq->flags |= IW_ENCODE_DISABLED;
515		return 0;
516	}
517
518	len = sec->key_sizes[key];
519	memcpy(keybuf, sec->keys[key], len);
520
521	erq->length = len;
522	erq->flags |= IW_ENCODE_ENABLED;
523
524	if (ieee->open_wep)
525		erq->flags |= IW_ENCODE_OPEN;
526	else
527		erq->flags |= IW_ENCODE_RESTRICTED;
528
529	return 0;
530}
531
532int libipw_wx_set_encodeext(struct libipw_device *ieee,
533			       struct iw_request_info *info,
534			       union iwreq_data *wrqu, char *extra)
535{
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;
544
545	struct libipw_security sec = {
546		.flags = 0,
547	};
548
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;
556
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;
568	}
569
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);
575
576		for (i = 0; i < WEP_KEYS; i++)
577			if (ieee->crypt_info.crypt[i] != NULL)
578				break;
579
580		if (i == WEP_KEYS) {
581			sec.enabled = 0;
582			sec.encrypt = 0;
583			sec.level = SEC_LEVEL_0;
584			sec.flags |= SEC_LEVEL;
585		}
586		goto done;
587	}
588
589	sec.enabled = 1;
590	sec.encrypt = 1;
591
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;
596
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;
615	}
616
617	ops = lib80211_get_crypto_ops(alg);
618	if (ops == NULL) {
619		request_module(module);
620		ops = lib80211_get_crypto_ops(alg);
621	}
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;
627	}
628
629	if (*crypt == NULL || (*crypt)->ops != ops) {
630		struct lib80211_crypt_data *new_crypt;
631
632		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
633
634		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
635		if (new_crypt == NULL) {
636			ret = -ENOMEM;
637			goto done;
638		}
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;
646		}
647		*crypt = new_crypt;
648	}
649
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;
656	}
657
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;
663	}
664
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;
681		}
682		/* Don't set sec level for group keys. */
683		if (group_key)
684			sec.flags &= ~SEC_LEVEL;
685	}
686      done:
687	if (ieee->set_security)
688		ieee->set_security(ieee->dev, &sec);
689
690	/*
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.
696	 */
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;
702	}
703
704	return ret;
705}
706
707int libipw_wx_get_encodeext(struct libipw_device *ieee,
708			       struct iw_request_info *info,
709			       union iwreq_data *wrqu, char *extra)
710{
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;
715
716	max_key_len = encoding->length - sizeof(*ext);
717	if (max_key_len < 0)
718		return -EINVAL;
719
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;
727
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;
732
733	encoding->flags = idx + 1;
734	memset(ext, 0, sizeof(*ext));
735
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;
749
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;
757
758	}
759
760	return 0;
761}
762
763EXPORT_SYMBOL(libipw_wx_set_encodeext);
764EXPORT_SYMBOL(libipw_wx_get_encodeext);
765
766EXPORT_SYMBOL(libipw_wx_get_scan);
767EXPORT_SYMBOL(libipw_wx_set_encode);
768EXPORT_SYMBOL(libipw_wx_get_encode);
769