1// SPDX-License-Identifier: GPL-2.0-only
2/******************************************************************************
3
4  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
5
6  Portions of this file are based on the WEP enablement code provided by the
7  Host AP project hostap-drivers v0.1.3
8  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
9  <j@w1.fi>
10  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
11
12
13  Contact Information:
14  Intel Linux Wireless <ilw@linux.intel.com>
15  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
16
17******************************************************************************/
18
19#include <linux/hardirq.h>
20#include <linux/kmod.h>
21#include <linux/slab.h>
22#include <linux/module.h>
23#include <linux/jiffies.h>
24
25#include <net/lib80211.h>
26#include <linux/wireless.h>
27
28#include "libipw.h"
29
30static const char *libipw_modes[] = {
31	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
32};
33
34static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
35{
36	unsigned long end = jiffies;
37
38	if (end >= start)
39		return jiffies_to_msecs(end - start);
40
41	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
42}
43
44#define MAX_CUSTOM_LEN 64
45static char *libipw_translate_scan(struct libipw_device *ieee,
46				      char *start, char *stop,
47				      struct libipw_network *network,
48				      struct iw_request_info *info)
49{
50	char custom[MAX_CUSTOM_LEN];
51	char *p;
52	struct iw_event iwe;
53	int i, j;
54	char *current_val;	/* For rates */
55	u8 rate;
56
57	/* First entry *MUST* be the AP MAC address */
58	iwe.cmd = SIOCGIWAP;
59	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
60	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
61	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
62
63	/* Remaining entries will be displayed in the order we provide them */
64
65	/* Add the ESSID */
66	iwe.cmd = SIOCGIWESSID;
67	iwe.u.data.flags = 1;
68	iwe.u.data.length = min(network->ssid_len, (u8) 32);
69	start = iwe_stream_add_point(info, start, stop,
70				     &iwe, network->ssid);
71
72	/* Add the protocol name */
73	iwe.cmd = SIOCGIWNAME;
74	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
75		 libipw_modes[network->mode]);
76	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
77
78	/* Add mode */
79	iwe.cmd = SIOCGIWMODE;
80	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
81		if (network->capability & WLAN_CAPABILITY_ESS)
82			iwe.u.mode = IW_MODE_MASTER;
83		else
84			iwe.u.mode = IW_MODE_ADHOC;
85
86		start = iwe_stream_add_event(info, start, stop,
87					     &iwe, IW_EV_UINT_LEN);
88	}
89
90	/* Add channel and frequency */
91	/* Note : userspace automatically computes channel using iwrange */
92	iwe.cmd = SIOCGIWFREQ;
93	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
94	iwe.u.freq.e = 6;
95	iwe.u.freq.i = 0;
96	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
97
98	/* Add encryption capability */
99	iwe.cmd = SIOCGIWENCODE;
100	if (network->capability & WLAN_CAPABILITY_PRIVACY)
101		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
102	else
103		iwe.u.data.flags = IW_ENCODE_DISABLED;
104	iwe.u.data.length = 0;
105	start = iwe_stream_add_point(info, start, stop,
106				     &iwe, network->ssid);
107
108	/* Add basic and extended rates */
109	/* Rate : stuffing multiple values in a single event require a bit
110	 * more of magic - Jean II */
111	current_val = start + iwe_stream_lcp_len(info);
112	iwe.cmd = SIOCGIWRATE;
113	/* Those two flags are ignored... */
114	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
115
116	for (i = 0, j = 0; i < network->rates_len;) {
117		if (j < network->rates_ex_len &&
118		    ((network->rates_ex[j] & 0x7F) <
119		     (network->rates[i] & 0x7F)))
120			rate = network->rates_ex[j++] & 0x7F;
121		else
122			rate = network->rates[i++] & 0x7F;
123		/* Bit rate given in 500 kb/s units (+ 0x80) */
124		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
125		/* Add new value to event */
126		current_val = iwe_stream_add_value(info, start, current_val,
127						   stop, &iwe, IW_EV_PARAM_LEN);
128	}
129	for (; j < network->rates_ex_len; j++) {
130		rate = network->rates_ex[j] & 0x7F;
131		/* Bit rate given in 500 kb/s units (+ 0x80) */
132		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
133		/* Add new value to event */
134		current_val = iwe_stream_add_value(info, start, current_val,
135						   stop, &iwe, IW_EV_PARAM_LEN);
136	}
137	/* Check if we added any rate */
138	if ((current_val - start) > iwe_stream_lcp_len(info))
139		start = current_val;
140
141	/* Add quality statistics */
142	iwe.cmd = IWEVQUAL;
143	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
144	    IW_QUAL_NOISE_UPDATED;
145
146	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
147		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
148		    IW_QUAL_LEVEL_INVALID;
149		iwe.u.qual.qual = 0;
150	} else {
151		if (ieee->perfect_rssi == ieee->worst_rssi)
152			iwe.u.qual.qual = 100;
153		else
154			iwe.u.qual.qual =
155			    (100 *
156			     (ieee->perfect_rssi - ieee->worst_rssi) *
157			     (ieee->perfect_rssi - ieee->worst_rssi) -
158			     (ieee->perfect_rssi - network->stats.rssi) *
159			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
160			      62 * (ieee->perfect_rssi -
161				    network->stats.rssi))) /
162			    ((ieee->perfect_rssi -
163			      ieee->worst_rssi) * (ieee->perfect_rssi -
164						   ieee->worst_rssi));
165		if (iwe.u.qual.qual > 100)
166			iwe.u.qual.qual = 100;
167		else if (iwe.u.qual.qual < 1)
168			iwe.u.qual.qual = 0;
169	}
170
171	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
172		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
173		iwe.u.qual.noise = 0;
174	} else {
175		iwe.u.qual.noise = network->stats.noise;
176	}
177
178	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
179		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
180		iwe.u.qual.level = 0;
181	} else {
182		iwe.u.qual.level = network->stats.signal;
183	}
184
185	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
186
187	iwe.cmd = IWEVCUSTOM;
188	p = custom;
189
190	iwe.u.data.length = p - custom;
191	if (iwe.u.data.length)
192		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
193
194	memset(&iwe, 0, sizeof(iwe));
195	if (network->wpa_ie_len) {
196		char buf[MAX_WPA_IE_LEN];
197		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
198		iwe.cmd = IWEVGENIE;
199		iwe.u.data.length = network->wpa_ie_len;
200		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
201	}
202
203	memset(&iwe, 0, sizeof(iwe));
204	if (network->rsn_ie_len) {
205		char buf[MAX_WPA_IE_LEN];
206		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
207		iwe.cmd = IWEVGENIE;
208		iwe.u.data.length = network->rsn_ie_len;
209		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
210	}
211
212	/* Add EXTRA: Age to display seconds since last beacon/probe response
213	 * for given network. */
214	iwe.cmd = IWEVCUSTOM;
215	p = custom;
216	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom),
217		      " Last beacon: %ums ago",
218		      elapsed_jiffies_msecs(network->last_scanned));
219	iwe.u.data.length = p - custom;
220	if (iwe.u.data.length)
221		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
222
223	/* Add spectrum management information */
224	iwe.cmd = -1;
225	p = custom;
226	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
227
228	if (libipw_get_channel_flags(ieee, network->channel) &
229	    LIBIPW_CH_INVALID) {
230		iwe.cmd = IWEVCUSTOM;
231		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
232	}
233
234	if (libipw_get_channel_flags(ieee, network->channel) &
235	    LIBIPW_CH_RADAR_DETECT) {
236		iwe.cmd = IWEVCUSTOM;
237		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
238	}
239
240	if (iwe.cmd == IWEVCUSTOM) {
241		iwe.u.data.length = p - custom;
242		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
243	}
244
245	return start;
246}
247
248#define SCAN_ITEM_SIZE 128
249
250int libipw_wx_get_scan(struct libipw_device *ieee,
251			  struct iw_request_info *info,
252			  union iwreq_data *wrqu, char *extra)
253{
254	struct libipw_network *network;
255	unsigned long flags;
256	int err = 0;
257
258	char *ev = extra;
259	char *stop = ev + wrqu->data.length;
260	int i = 0;
261
262	LIBIPW_DEBUG_WX("Getting scan\n");
263
264	spin_lock_irqsave(&ieee->lock, flags);
265
266	list_for_each_entry(network, &ieee->network_list, list) {
267		i++;
268		if (stop - ev < SCAN_ITEM_SIZE) {
269			err = -E2BIG;
270			break;
271		}
272
273		if (ieee->scan_age == 0 ||
274		    time_after(network->last_scanned + ieee->scan_age, jiffies))
275			ev = libipw_translate_scan(ieee, ev, stop, network,
276						      info);
277		else {
278			LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
279					  network->ssid_len, network->ssid,
280					  network->bssid,
281					  elapsed_jiffies_msecs(
282					               network->last_scanned));
283		}
284	}
285
286	spin_unlock_irqrestore(&ieee->lock, flags);
287
288	wrqu->data.length = ev - extra;
289	wrqu->data.flags = 0;
290
291	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
292
293	return err;
294}
295
296int libipw_wx_set_encode(struct libipw_device *ieee,
297			    struct iw_request_info *info,
298			    union iwreq_data *wrqu, char *keybuf)
299{
300	struct iw_point *erq = &(wrqu->encoding);
301	struct net_device *dev = ieee->dev;
302	struct libipw_security sec = {
303		.flags = 0
304	};
305	int i, key, key_provided, len;
306	struct lib80211_crypt_data **crypt;
307	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
308
309	LIBIPW_DEBUG_WX("SET_ENCODE\n");
310
311	key = erq->flags & IW_ENCODE_INDEX;
312	if (key) {
313		if (key > WEP_KEYS)
314			return -EINVAL;
315		key--;
316		key_provided = 1;
317	} else {
318		key_provided = 0;
319		key = ieee->crypt_info.tx_keyidx;
320	}
321
322	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
323			   "provided" : "default");
324
325	crypt = &ieee->crypt_info.crypt[key];
326
327	if (erq->flags & IW_ENCODE_DISABLED) {
328		if (key_provided && *crypt) {
329			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
330					   key);
331			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
332		} else
333			LIBIPW_DEBUG_WX("Disabling encryption.\n");
334
335		/* Check all the keys to see if any are still configured,
336		 * and if no key index was provided, de-init them all */
337		for (i = 0; i < WEP_KEYS; i++) {
338			if (ieee->crypt_info.crypt[i] != NULL) {
339				if (key_provided)
340					break;
341				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
342							       &ieee->crypt_info.crypt[i]);
343			}
344		}
345
346		if (i == WEP_KEYS) {
347			sec.enabled = 0;
348			sec.encrypt = 0;
349			sec.level = SEC_LEVEL_0;
350			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
351		}
352
353		goto done;
354	}
355
356	sec.enabled = 1;
357	sec.encrypt = 1;
358	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
359
360	if (*crypt != NULL && (*crypt)->ops != NULL &&
361	    strcmp((*crypt)->ops->name, "WEP") != 0) {
362		/* changing to use WEP; deinit previously used algorithm
363		 * on this key */
364		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
365	}
366
367	if (*crypt == NULL && host_crypto) {
368		struct lib80211_crypt_data *new_crypt;
369
370		/* take WEP into use */
371		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
372				    GFP_KERNEL);
373		if (new_crypt == NULL)
374			return -ENOMEM;
375		new_crypt->ops = lib80211_get_crypto_ops("WEP");
376		if (!new_crypt->ops) {
377			request_module("lib80211_crypt_wep");
378			new_crypt->ops = lib80211_get_crypto_ops("WEP");
379		}
380
381		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
382			new_crypt->priv = new_crypt->ops->init(key);
383
384		if (!new_crypt->ops || !new_crypt->priv) {
385			kfree(new_crypt);
386			new_crypt = NULL;
387
388			printk(KERN_WARNING "%s: could not initialize WEP: "
389			       "load module lib80211_crypt_wep\n", dev->name);
390			return -EOPNOTSUPP;
391		}
392		*crypt = new_crypt;
393	}
394
395	/* If a new key was provided, set it up */
396	if (erq->length > 0) {
397		len = erq->length <= 5 ? 5 : 13;
398		memcpy(sec.keys[key], keybuf, erq->length);
399		if (len > erq->length)
400			memset(sec.keys[key] + erq->length, 0,
401			       len - erq->length);
402		LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
403				   key, len, sec.keys[key],
404				   erq->length, len);
405		sec.key_sizes[key] = len;
406		if (*crypt)
407			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
408					       (*crypt)->priv);
409		sec.flags |= (1 << key);
410		/* This ensures a key will be activated if no key is
411		 * explicitly set */
412		if (key == sec.active_key)
413			sec.flags |= SEC_ACTIVE_KEY;
414
415	} else {
416		if (host_crypto) {
417			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
418						     NULL, (*crypt)->priv);
419			if (len == 0) {
420				/* Set a default key of all 0 */
421				LIBIPW_DEBUG_WX("Setting key %d to all "
422						   "zero.\n", key);
423				memset(sec.keys[key], 0, 13);
424				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
425						       (*crypt)->priv);
426				sec.key_sizes[key] = 13;
427				sec.flags |= (1 << key);
428			}
429		}
430		/* No key data - just set the default TX key index */
431		if (key_provided) {
432			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
433					   "key.\n", key);
434			ieee->crypt_info.tx_keyidx = key;
435			sec.active_key = key;
436			sec.flags |= SEC_ACTIVE_KEY;
437		}
438	}
439	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
440		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
441		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
442		    WLAN_AUTH_SHARED_KEY;
443		sec.flags |= SEC_AUTH_MODE;
444		LIBIPW_DEBUG_WX("Auth: %s\n",
445				   sec.auth_mode == WLAN_AUTH_OPEN ?
446				   "OPEN" : "SHARED KEY");
447	}
448
449	/* For now we just support WEP, so only set that security level...
450	 * TODO: When WPA is added this is one place that needs to change */
451	sec.flags |= SEC_LEVEL;
452	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
453	sec.encode_alg[key] = SEC_ALG_WEP;
454
455      done:
456	if (ieee->set_security)
457		ieee->set_security(dev, &sec);
458
459	return 0;
460}
461
462int libipw_wx_get_encode(struct libipw_device *ieee,
463			    struct iw_request_info *info,
464			    union iwreq_data *wrqu, char *keybuf)
465{
466	struct iw_point *erq = &(wrqu->encoding);
467	int len, key;
468	struct libipw_security *sec = &ieee->sec;
469
470	LIBIPW_DEBUG_WX("GET_ENCODE\n");
471
472	key = erq->flags & IW_ENCODE_INDEX;
473	if (key) {
474		if (key > WEP_KEYS)
475			return -EINVAL;
476		key--;
477	} else
478		key = ieee->crypt_info.tx_keyidx;
479
480	erq->flags = key + 1;
481
482	if (!sec->enabled) {
483		erq->length = 0;
484		erq->flags |= IW_ENCODE_DISABLED;
485		return 0;
486	}
487
488	len = sec->key_sizes[key];
489	memcpy(keybuf, sec->keys[key], len);
490
491	erq->length = len;
492	erq->flags |= IW_ENCODE_ENABLED;
493
494	if (ieee->open_wep)
495		erq->flags |= IW_ENCODE_OPEN;
496	else
497		erq->flags |= IW_ENCODE_RESTRICTED;
498
499	return 0;
500}
501
502int libipw_wx_set_encodeext(struct libipw_device *ieee,
503			       struct iw_request_info *info,
504			       union iwreq_data *wrqu, char *extra)
505{
506	struct net_device *dev = ieee->dev;
507	struct iw_point *encoding = &wrqu->encoding;
508	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
509	int i, idx, ret = 0;
510	int group_key = 0;
511	const char *alg, *module;
512	struct lib80211_crypto_ops *ops;
513	struct lib80211_crypt_data **crypt;
514
515	struct libipw_security sec = {
516		.flags = 0,
517	};
518
519	idx = encoding->flags & IW_ENCODE_INDEX;
520	if (idx) {
521		if (idx < 1 || idx > WEP_KEYS)
522			return -EINVAL;
523		idx--;
524	} else
525		idx = ieee->crypt_info.tx_keyidx;
526
527	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
528		crypt = &ieee->crypt_info.crypt[idx];
529		group_key = 1;
530	} else {
531		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
532		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
533			return -EINVAL;
534		if (ieee->iw_mode == IW_MODE_INFRA)
535			crypt = &ieee->crypt_info.crypt[idx];
536		else
537			return -EINVAL;
538	}
539
540	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
541	if ((encoding->flags & IW_ENCODE_DISABLED) ||
542	    ext->alg == IW_ENCODE_ALG_NONE) {
543		if (*crypt)
544			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
545
546		for (i = 0; i < WEP_KEYS; i++)
547			if (ieee->crypt_info.crypt[i] != NULL)
548				break;
549
550		if (i == WEP_KEYS) {
551			sec.enabled = 0;
552			sec.encrypt = 0;
553			sec.level = SEC_LEVEL_0;
554			sec.flags |= SEC_LEVEL;
555		}
556		goto done;
557	}
558
559	sec.enabled = 1;
560	sec.encrypt = 1;
561
562	if (group_key ? !ieee->host_mc_decrypt :
563	    !(ieee->host_encrypt || ieee->host_decrypt ||
564	      ieee->host_encrypt_msdu))
565		goto skip_host_crypt;
566
567	switch (ext->alg) {
568	case IW_ENCODE_ALG_WEP:
569		alg = "WEP";
570		module = "lib80211_crypt_wep";
571		break;
572	case IW_ENCODE_ALG_TKIP:
573		alg = "TKIP";
574		module = "lib80211_crypt_tkip";
575		break;
576	case IW_ENCODE_ALG_CCMP:
577		alg = "CCMP";
578		module = "lib80211_crypt_ccmp";
579		break;
580	default:
581		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
582				   dev->name, ext->alg);
583		ret = -EINVAL;
584		goto done;
585	}
586
587	ops = lib80211_get_crypto_ops(alg);
588	if (ops == NULL) {
589		request_module(module);
590		ops = lib80211_get_crypto_ops(alg);
591	}
592	if (ops == NULL) {
593		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
594				   dev->name, ext->alg);
595		ret = -EINVAL;
596		goto done;
597	}
598
599	if (*crypt == NULL || (*crypt)->ops != ops) {
600		struct lib80211_crypt_data *new_crypt;
601
602		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
603
604		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
605		if (new_crypt == NULL) {
606			ret = -ENOMEM;
607			goto done;
608		}
609		new_crypt->ops = ops;
610		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
611			new_crypt->priv = new_crypt->ops->init(idx);
612		if (new_crypt->priv == NULL) {
613			kfree(new_crypt);
614			ret = -EINVAL;
615			goto done;
616		}
617		*crypt = new_crypt;
618	}
619
620	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
621	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
622				   (*crypt)->priv) < 0) {
623		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
624		ret = -EINVAL;
625		goto done;
626	}
627
628      skip_host_crypt:
629	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
630		ieee->crypt_info.tx_keyidx = idx;
631		sec.active_key = idx;
632		sec.flags |= SEC_ACTIVE_KEY;
633	}
634
635	if (ext->alg != IW_ENCODE_ALG_NONE) {
636		int key_len = clamp_val(ext->key_len, 0, SCM_KEY_LEN);
637
638		memcpy(sec.keys[idx], ext->key, key_len);
639		sec.key_sizes[idx] = key_len;
640		sec.flags |= (1 << idx);
641		if (ext->alg == IW_ENCODE_ALG_WEP) {
642			sec.encode_alg[idx] = SEC_ALG_WEP;
643			sec.flags |= SEC_LEVEL;
644			sec.level = SEC_LEVEL_1;
645		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
646			sec.encode_alg[idx] = SEC_ALG_TKIP;
647			sec.flags |= SEC_LEVEL;
648			sec.level = SEC_LEVEL_2;
649		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
650			sec.encode_alg[idx] = SEC_ALG_CCMP;
651			sec.flags |= SEC_LEVEL;
652			sec.level = SEC_LEVEL_3;
653		}
654		/* Don't set sec level for group keys. */
655		if (group_key)
656			sec.flags &= ~SEC_LEVEL;
657	}
658      done:
659	if (ieee->set_security)
660		ieee->set_security(dev, &sec);
661
662	return ret;
663}
664
665int libipw_wx_get_encodeext(struct libipw_device *ieee,
666			       struct iw_request_info *info,
667			       union iwreq_data *wrqu, char *extra)
668{
669	struct iw_point *encoding = &wrqu->encoding;
670	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
671	struct libipw_security *sec = &ieee->sec;
672	int idx, max_key_len;
673
674	max_key_len = encoding->length - sizeof(*ext);
675	if (max_key_len < 0)
676		return -EINVAL;
677
678	idx = encoding->flags & IW_ENCODE_INDEX;
679	if (idx) {
680		if (idx < 1 || idx > WEP_KEYS)
681			return -EINVAL;
682		idx--;
683	} else
684		idx = ieee->crypt_info.tx_keyidx;
685
686	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
687	    ext->alg != IW_ENCODE_ALG_WEP)
688		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
689			return -EINVAL;
690
691	encoding->flags = idx + 1;
692	memset(ext, 0, sizeof(*ext));
693
694	if (!sec->enabled) {
695		ext->alg = IW_ENCODE_ALG_NONE;
696		ext->key_len = 0;
697		encoding->flags |= IW_ENCODE_DISABLED;
698	} else {
699		if (sec->encode_alg[idx] == SEC_ALG_WEP)
700			ext->alg = IW_ENCODE_ALG_WEP;
701		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
702			ext->alg = IW_ENCODE_ALG_TKIP;
703		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
704			ext->alg = IW_ENCODE_ALG_CCMP;
705		else
706			return -EINVAL;
707
708		ext->key_len = sec->key_sizes[idx];
709		memcpy(ext->key, sec->keys[idx], ext->key_len);
710		encoding->flags |= IW_ENCODE_ENABLED;
711		if (ext->key_len &&
712		    (ext->alg == IW_ENCODE_ALG_TKIP ||
713		     ext->alg == IW_ENCODE_ALG_CCMP))
714			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
715
716	}
717
718	return 0;
719}
720
721EXPORT_SYMBOL(libipw_wx_set_encodeext);
722EXPORT_SYMBOL(libipw_wx_get_encodeext);
723
724EXPORT_SYMBOL(libipw_wx_get_scan);
725EXPORT_SYMBOL(libipw_wx_set_encode);
726EXPORT_SYMBOL(libipw_wx_get_encode);
727