162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci  Portions of this file are based on the WEP enablement code provided by the
762306a36Sopenharmony_ci  Host AP project hostap-drivers v0.1.3
862306a36Sopenharmony_ci  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
962306a36Sopenharmony_ci  <j@w1.fi>
1062306a36Sopenharmony_ci  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci  Contact Information:
1462306a36Sopenharmony_ci  Intel Linux Wireless <ilw@linux.intel.com>
1562306a36Sopenharmony_ci  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci******************************************************************************/
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/hardirq.h>
2062306a36Sopenharmony_ci#include <linux/kmod.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/jiffies.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <net/lib80211.h>
2662306a36Sopenharmony_ci#include <linux/wireless.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "libipw.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const char *libipw_modes[] = {
3162306a36Sopenharmony_ci	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline unsigned int elapsed_jiffies_msecs(unsigned long start)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	unsigned long end = jiffies;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (end >= start)
3962306a36Sopenharmony_ci		return jiffies_to_msecs(end - start);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define MAX_CUSTOM_LEN 64
4562306a36Sopenharmony_cistatic char *libipw_translate_scan(struct libipw_device *ieee,
4662306a36Sopenharmony_ci				      char *start, char *stop,
4762306a36Sopenharmony_ci				      struct libipw_network *network,
4862306a36Sopenharmony_ci				      struct iw_request_info *info)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	char custom[MAX_CUSTOM_LEN];
5162306a36Sopenharmony_ci	char *p;
5262306a36Sopenharmony_ci	struct iw_event iwe;
5362306a36Sopenharmony_ci	int i, j;
5462306a36Sopenharmony_ci	char *current_val;	/* For rates */
5562306a36Sopenharmony_ci	u8 rate;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* First entry *MUST* be the AP MAC address */
5862306a36Sopenharmony_ci	iwe.cmd = SIOCGIWAP;
5962306a36Sopenharmony_ci	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
6062306a36Sopenharmony_ci	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
6162306a36Sopenharmony_ci	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Remaining entries will be displayed in the order we provide them */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Add the ESSID */
6662306a36Sopenharmony_ci	iwe.cmd = SIOCGIWESSID;
6762306a36Sopenharmony_ci	iwe.u.data.flags = 1;
6862306a36Sopenharmony_ci	iwe.u.data.length = min(network->ssid_len, (u8) 32);
6962306a36Sopenharmony_ci	start = iwe_stream_add_point(info, start, stop,
7062306a36Sopenharmony_ci				     &iwe, network->ssid);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Add the protocol name */
7362306a36Sopenharmony_ci	iwe.cmd = SIOCGIWNAME;
7462306a36Sopenharmony_ci	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
7562306a36Sopenharmony_ci		 libipw_modes[network->mode]);
7662306a36Sopenharmony_ci	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Add mode */
7962306a36Sopenharmony_ci	iwe.cmd = SIOCGIWMODE;
8062306a36Sopenharmony_ci	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
8162306a36Sopenharmony_ci		if (network->capability & WLAN_CAPABILITY_ESS)
8262306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
8362306a36Sopenharmony_ci		else
8462306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_ADHOC;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		start = iwe_stream_add_event(info, start, stop,
8762306a36Sopenharmony_ci					     &iwe, IW_EV_UINT_LEN);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* Add channel and frequency */
9162306a36Sopenharmony_ci	/* Note : userspace automatically computes channel using iwrange */
9262306a36Sopenharmony_ci	iwe.cmd = SIOCGIWFREQ;
9362306a36Sopenharmony_ci	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
9462306a36Sopenharmony_ci	iwe.u.freq.e = 6;
9562306a36Sopenharmony_ci	iwe.u.freq.i = 0;
9662306a36Sopenharmony_ci	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Add encryption capability */
9962306a36Sopenharmony_ci	iwe.cmd = SIOCGIWENCODE;
10062306a36Sopenharmony_ci	if (network->capability & WLAN_CAPABILITY_PRIVACY)
10162306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_DISABLED;
10462306a36Sopenharmony_ci	iwe.u.data.length = 0;
10562306a36Sopenharmony_ci	start = iwe_stream_add_point(info, start, stop,
10662306a36Sopenharmony_ci				     &iwe, network->ssid);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Add basic and extended rates */
10962306a36Sopenharmony_ci	/* Rate : stuffing multiple values in a single event require a bit
11062306a36Sopenharmony_ci	 * more of magic - Jean II */
11162306a36Sopenharmony_ci	current_val = start + iwe_stream_lcp_len(info);
11262306a36Sopenharmony_ci	iwe.cmd = SIOCGIWRATE;
11362306a36Sopenharmony_ci	/* Those two flags are ignored... */
11462306a36Sopenharmony_ci	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	for (i = 0, j = 0; i < network->rates_len;) {
11762306a36Sopenharmony_ci		if (j < network->rates_ex_len &&
11862306a36Sopenharmony_ci		    ((network->rates_ex[j] & 0x7F) <
11962306a36Sopenharmony_ci		     (network->rates[i] & 0x7F)))
12062306a36Sopenharmony_ci			rate = network->rates_ex[j++] & 0x7F;
12162306a36Sopenharmony_ci		else
12262306a36Sopenharmony_ci			rate = network->rates[i++] & 0x7F;
12362306a36Sopenharmony_ci		/* Bit rate given in 500 kb/s units (+ 0x80) */
12462306a36Sopenharmony_ci		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
12562306a36Sopenharmony_ci		/* Add new value to event */
12662306a36Sopenharmony_ci		current_val = iwe_stream_add_value(info, start, current_val,
12762306a36Sopenharmony_ci						   stop, &iwe, IW_EV_PARAM_LEN);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	for (; j < network->rates_ex_len; j++) {
13062306a36Sopenharmony_ci		rate = network->rates_ex[j] & 0x7F;
13162306a36Sopenharmony_ci		/* Bit rate given in 500 kb/s units (+ 0x80) */
13262306a36Sopenharmony_ci		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
13362306a36Sopenharmony_ci		/* Add new value to event */
13462306a36Sopenharmony_ci		current_val = iwe_stream_add_value(info, start, current_val,
13562306a36Sopenharmony_ci						   stop, &iwe, IW_EV_PARAM_LEN);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	/* Check if we added any rate */
13862306a36Sopenharmony_ci	if ((current_val - start) > iwe_stream_lcp_len(info))
13962306a36Sopenharmony_ci		start = current_val;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Add quality statistics */
14262306a36Sopenharmony_ci	iwe.cmd = IWEVQUAL;
14362306a36Sopenharmony_ci	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
14462306a36Sopenharmony_ci	    IW_QUAL_NOISE_UPDATED;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
14762306a36Sopenharmony_ci		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
14862306a36Sopenharmony_ci		    IW_QUAL_LEVEL_INVALID;
14962306a36Sopenharmony_ci		iwe.u.qual.qual = 0;
15062306a36Sopenharmony_ci	} else {
15162306a36Sopenharmony_ci		if (ieee->perfect_rssi == ieee->worst_rssi)
15262306a36Sopenharmony_ci			iwe.u.qual.qual = 100;
15362306a36Sopenharmony_ci		else
15462306a36Sopenharmony_ci			iwe.u.qual.qual =
15562306a36Sopenharmony_ci			    (100 *
15662306a36Sopenharmony_ci			     (ieee->perfect_rssi - ieee->worst_rssi) *
15762306a36Sopenharmony_ci			     (ieee->perfect_rssi - ieee->worst_rssi) -
15862306a36Sopenharmony_ci			     (ieee->perfect_rssi - network->stats.rssi) *
15962306a36Sopenharmony_ci			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
16062306a36Sopenharmony_ci			      62 * (ieee->perfect_rssi -
16162306a36Sopenharmony_ci				    network->stats.rssi))) /
16262306a36Sopenharmony_ci			    ((ieee->perfect_rssi -
16362306a36Sopenharmony_ci			      ieee->worst_rssi) * (ieee->perfect_rssi -
16462306a36Sopenharmony_ci						   ieee->worst_rssi));
16562306a36Sopenharmony_ci		if (iwe.u.qual.qual > 100)
16662306a36Sopenharmony_ci			iwe.u.qual.qual = 100;
16762306a36Sopenharmony_ci		else if (iwe.u.qual.qual < 1)
16862306a36Sopenharmony_ci			iwe.u.qual.qual = 0;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
17262306a36Sopenharmony_ci		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
17362306a36Sopenharmony_ci		iwe.u.qual.noise = 0;
17462306a36Sopenharmony_ci	} else {
17562306a36Sopenharmony_ci		iwe.u.qual.noise = network->stats.noise;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
17962306a36Sopenharmony_ci		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
18062306a36Sopenharmony_ci		iwe.u.qual.level = 0;
18162306a36Sopenharmony_ci	} else {
18262306a36Sopenharmony_ci		iwe.u.qual.level = network->stats.signal;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	iwe.cmd = IWEVCUSTOM;
18862306a36Sopenharmony_ci	p = custom;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	iwe.u.data.length = p - custom;
19162306a36Sopenharmony_ci	if (iwe.u.data.length)
19262306a36Sopenharmony_ci		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
19562306a36Sopenharmony_ci	if (network->wpa_ie_len) {
19662306a36Sopenharmony_ci		char buf[MAX_WPA_IE_LEN];
19762306a36Sopenharmony_ci		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
19862306a36Sopenharmony_ci		iwe.cmd = IWEVGENIE;
19962306a36Sopenharmony_ci		iwe.u.data.length = network->wpa_ie_len;
20062306a36Sopenharmony_ci		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
20462306a36Sopenharmony_ci	if (network->rsn_ie_len) {
20562306a36Sopenharmony_ci		char buf[MAX_WPA_IE_LEN];
20662306a36Sopenharmony_ci		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
20762306a36Sopenharmony_ci		iwe.cmd = IWEVGENIE;
20862306a36Sopenharmony_ci		iwe.u.data.length = network->rsn_ie_len;
20962306a36Sopenharmony_ci		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* Add EXTRA: Age to display seconds since last beacon/probe response
21362306a36Sopenharmony_ci	 * for given network. */
21462306a36Sopenharmony_ci	iwe.cmd = IWEVCUSTOM;
21562306a36Sopenharmony_ci	p = custom;
21662306a36Sopenharmony_ci	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom),
21762306a36Sopenharmony_ci		      " Last beacon: %ums ago",
21862306a36Sopenharmony_ci		      elapsed_jiffies_msecs(network->last_scanned));
21962306a36Sopenharmony_ci	iwe.u.data.length = p - custom;
22062306a36Sopenharmony_ci	if (iwe.u.data.length)
22162306a36Sopenharmony_ci		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Add spectrum management information */
22462306a36Sopenharmony_ci	iwe.cmd = -1;
22562306a36Sopenharmony_ci	p = custom;
22662306a36Sopenharmony_ci	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (libipw_get_channel_flags(ieee, network->channel) &
22962306a36Sopenharmony_ci	    LIBIPW_CH_INVALID) {
23062306a36Sopenharmony_ci		iwe.cmd = IWEVCUSTOM;
23162306a36Sopenharmony_ci		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (libipw_get_channel_flags(ieee, network->channel) &
23562306a36Sopenharmony_ci	    LIBIPW_CH_RADAR_DETECT) {
23662306a36Sopenharmony_ci		iwe.cmd = IWEVCUSTOM;
23762306a36Sopenharmony_ci		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (iwe.cmd == IWEVCUSTOM) {
24162306a36Sopenharmony_ci		iwe.u.data.length = p - custom;
24262306a36Sopenharmony_ci		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return start;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci#define SCAN_ITEM_SIZE 128
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint libipw_wx_get_scan(struct libipw_device *ieee,
25162306a36Sopenharmony_ci			  struct iw_request_info *info,
25262306a36Sopenharmony_ci			  union iwreq_data *wrqu, char *extra)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct libipw_network *network;
25562306a36Sopenharmony_ci	unsigned long flags;
25662306a36Sopenharmony_ci	int err = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	char *ev = extra;
25962306a36Sopenharmony_ci	char *stop = ev + wrqu->data.length;
26062306a36Sopenharmony_ci	int i = 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	LIBIPW_DEBUG_WX("Getting scan\n");
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	spin_lock_irqsave(&ieee->lock, flags);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	list_for_each_entry(network, &ieee->network_list, list) {
26762306a36Sopenharmony_ci		i++;
26862306a36Sopenharmony_ci		if (stop - ev < SCAN_ITEM_SIZE) {
26962306a36Sopenharmony_ci			err = -E2BIG;
27062306a36Sopenharmony_ci			break;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		if (ieee->scan_age == 0 ||
27462306a36Sopenharmony_ci		    time_after(network->last_scanned + ieee->scan_age, jiffies))
27562306a36Sopenharmony_ci			ev = libipw_translate_scan(ieee, ev, stop, network,
27662306a36Sopenharmony_ci						      info);
27762306a36Sopenharmony_ci		else {
27862306a36Sopenharmony_ci			LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
27962306a36Sopenharmony_ci					  network->ssid_len, network->ssid,
28062306a36Sopenharmony_ci					  network->bssid,
28162306a36Sopenharmony_ci					  elapsed_jiffies_msecs(
28262306a36Sopenharmony_ci					               network->last_scanned));
28362306a36Sopenharmony_ci		}
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ieee->lock, flags);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	wrqu->data.length = ev - extra;
28962306a36Sopenharmony_ci	wrqu->data.flags = 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return err;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciint libipw_wx_set_encode(struct libipw_device *ieee,
29762306a36Sopenharmony_ci			    struct iw_request_info *info,
29862306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *keybuf)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct iw_point *erq = &(wrqu->encoding);
30162306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
30262306a36Sopenharmony_ci	struct libipw_security sec = {
30362306a36Sopenharmony_ci		.flags = 0
30462306a36Sopenharmony_ci	};
30562306a36Sopenharmony_ci	int i, key, key_provided, len;
30662306a36Sopenharmony_ci	struct lib80211_crypt_data **crypt;
30762306a36Sopenharmony_ci	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	LIBIPW_DEBUG_WX("SET_ENCODE\n");
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	key = erq->flags & IW_ENCODE_INDEX;
31262306a36Sopenharmony_ci	if (key) {
31362306a36Sopenharmony_ci		if (key > WEP_KEYS)
31462306a36Sopenharmony_ci			return -EINVAL;
31562306a36Sopenharmony_ci		key--;
31662306a36Sopenharmony_ci		key_provided = 1;
31762306a36Sopenharmony_ci	} else {
31862306a36Sopenharmony_ci		key_provided = 0;
31962306a36Sopenharmony_ci		key = ieee->crypt_info.tx_keyidx;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
32362306a36Sopenharmony_ci			   "provided" : "default");
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	crypt = &ieee->crypt_info.crypt[key];
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (erq->flags & IW_ENCODE_DISABLED) {
32862306a36Sopenharmony_ci		if (key_provided && *crypt) {
32962306a36Sopenharmony_ci			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
33062306a36Sopenharmony_ci					   key);
33162306a36Sopenharmony_ci			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
33262306a36Sopenharmony_ci		} else
33362306a36Sopenharmony_ci			LIBIPW_DEBUG_WX("Disabling encryption.\n");
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		/* Check all the keys to see if any are still configured,
33662306a36Sopenharmony_ci		 * and if no key index was provided, de-init them all */
33762306a36Sopenharmony_ci		for (i = 0; i < WEP_KEYS; i++) {
33862306a36Sopenharmony_ci			if (ieee->crypt_info.crypt[i] != NULL) {
33962306a36Sopenharmony_ci				if (key_provided)
34062306a36Sopenharmony_ci					break;
34162306a36Sopenharmony_ci				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
34262306a36Sopenharmony_ci							       &ieee->crypt_info.crypt[i]);
34362306a36Sopenharmony_ci			}
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		if (i == WEP_KEYS) {
34762306a36Sopenharmony_ci			sec.enabled = 0;
34862306a36Sopenharmony_ci			sec.encrypt = 0;
34962306a36Sopenharmony_ci			sec.level = SEC_LEVEL_0;
35062306a36Sopenharmony_ci			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		goto done;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	sec.enabled = 1;
35762306a36Sopenharmony_ci	sec.encrypt = 1;
35862306a36Sopenharmony_ci	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (*crypt != NULL && (*crypt)->ops != NULL &&
36162306a36Sopenharmony_ci	    strcmp((*crypt)->ops->name, "WEP") != 0) {
36262306a36Sopenharmony_ci		/* changing to use WEP; deinit previously used algorithm
36362306a36Sopenharmony_ci		 * on this key */
36462306a36Sopenharmony_ci		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (*crypt == NULL && host_crypto) {
36862306a36Sopenharmony_ci		struct lib80211_crypt_data *new_crypt;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* take WEP into use */
37162306a36Sopenharmony_ci		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
37262306a36Sopenharmony_ci				    GFP_KERNEL);
37362306a36Sopenharmony_ci		if (new_crypt == NULL)
37462306a36Sopenharmony_ci			return -ENOMEM;
37562306a36Sopenharmony_ci		new_crypt->ops = lib80211_get_crypto_ops("WEP");
37662306a36Sopenharmony_ci		if (!new_crypt->ops) {
37762306a36Sopenharmony_ci			request_module("lib80211_crypt_wep");
37862306a36Sopenharmony_ci			new_crypt->ops = lib80211_get_crypto_ops("WEP");
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
38262306a36Sopenharmony_ci			new_crypt->priv = new_crypt->ops->init(key);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (!new_crypt->ops || !new_crypt->priv) {
38562306a36Sopenharmony_ci			kfree(new_crypt);
38662306a36Sopenharmony_ci			new_crypt = NULL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci			printk(KERN_WARNING "%s: could not initialize WEP: "
38962306a36Sopenharmony_ci			       "load module lib80211_crypt_wep\n", dev->name);
39062306a36Sopenharmony_ci			return -EOPNOTSUPP;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci		*crypt = new_crypt;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* If a new key was provided, set it up */
39662306a36Sopenharmony_ci	if (erq->length > 0) {
39762306a36Sopenharmony_ci		len = erq->length <= 5 ? 5 : 13;
39862306a36Sopenharmony_ci		memcpy(sec.keys[key], keybuf, erq->length);
39962306a36Sopenharmony_ci		if (len > erq->length)
40062306a36Sopenharmony_ci			memset(sec.keys[key] + erq->length, 0,
40162306a36Sopenharmony_ci			       len - erq->length);
40262306a36Sopenharmony_ci		LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
40362306a36Sopenharmony_ci				   key, len, sec.keys[key],
40462306a36Sopenharmony_ci				   erq->length, len);
40562306a36Sopenharmony_ci		sec.key_sizes[key] = len;
40662306a36Sopenharmony_ci		if (*crypt)
40762306a36Sopenharmony_ci			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
40862306a36Sopenharmony_ci					       (*crypt)->priv);
40962306a36Sopenharmony_ci		sec.flags |= (1 << key);
41062306a36Sopenharmony_ci		/* This ensures a key will be activated if no key is
41162306a36Sopenharmony_ci		 * explicitly set */
41262306a36Sopenharmony_ci		if (key == sec.active_key)
41362306a36Sopenharmony_ci			sec.flags |= SEC_ACTIVE_KEY;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	} else {
41662306a36Sopenharmony_ci		if (host_crypto) {
41762306a36Sopenharmony_ci			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
41862306a36Sopenharmony_ci						     NULL, (*crypt)->priv);
41962306a36Sopenharmony_ci			if (len == 0) {
42062306a36Sopenharmony_ci				/* Set a default key of all 0 */
42162306a36Sopenharmony_ci				LIBIPW_DEBUG_WX("Setting key %d to all "
42262306a36Sopenharmony_ci						   "zero.\n", key);
42362306a36Sopenharmony_ci				memset(sec.keys[key], 0, 13);
42462306a36Sopenharmony_ci				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
42562306a36Sopenharmony_ci						       (*crypt)->priv);
42662306a36Sopenharmony_ci				sec.key_sizes[key] = 13;
42762306a36Sopenharmony_ci				sec.flags |= (1 << key);
42862306a36Sopenharmony_ci			}
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		/* No key data - just set the default TX key index */
43162306a36Sopenharmony_ci		if (key_provided) {
43262306a36Sopenharmony_ci			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
43362306a36Sopenharmony_ci					   "key.\n", key);
43462306a36Sopenharmony_ci			ieee->crypt_info.tx_keyidx = key;
43562306a36Sopenharmony_ci			sec.active_key = key;
43662306a36Sopenharmony_ci			sec.flags |= SEC_ACTIVE_KEY;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
44062306a36Sopenharmony_ci		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
44162306a36Sopenharmony_ci		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
44262306a36Sopenharmony_ci		    WLAN_AUTH_SHARED_KEY;
44362306a36Sopenharmony_ci		sec.flags |= SEC_AUTH_MODE;
44462306a36Sopenharmony_ci		LIBIPW_DEBUG_WX("Auth: %s\n",
44562306a36Sopenharmony_ci				   sec.auth_mode == WLAN_AUTH_OPEN ?
44662306a36Sopenharmony_ci				   "OPEN" : "SHARED KEY");
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* For now we just support WEP, so only set that security level...
45062306a36Sopenharmony_ci	 * TODO: When WPA is added this is one place that needs to change */
45162306a36Sopenharmony_ci	sec.flags |= SEC_LEVEL;
45262306a36Sopenharmony_ci	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
45362306a36Sopenharmony_ci	sec.encode_alg[key] = SEC_ALG_WEP;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci      done:
45662306a36Sopenharmony_ci	if (ieee->set_security)
45762306a36Sopenharmony_ci		ieee->set_security(dev, &sec);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciint libipw_wx_get_encode(struct libipw_device *ieee,
46362306a36Sopenharmony_ci			    struct iw_request_info *info,
46462306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *keybuf)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct iw_point *erq = &(wrqu->encoding);
46762306a36Sopenharmony_ci	int len, key;
46862306a36Sopenharmony_ci	struct libipw_security *sec = &ieee->sec;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	LIBIPW_DEBUG_WX("GET_ENCODE\n");
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	key = erq->flags & IW_ENCODE_INDEX;
47362306a36Sopenharmony_ci	if (key) {
47462306a36Sopenharmony_ci		if (key > WEP_KEYS)
47562306a36Sopenharmony_ci			return -EINVAL;
47662306a36Sopenharmony_ci		key--;
47762306a36Sopenharmony_ci	} else
47862306a36Sopenharmony_ci		key = ieee->crypt_info.tx_keyidx;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	erq->flags = key + 1;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (!sec->enabled) {
48362306a36Sopenharmony_ci		erq->length = 0;
48462306a36Sopenharmony_ci		erq->flags |= IW_ENCODE_DISABLED;
48562306a36Sopenharmony_ci		return 0;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	len = sec->key_sizes[key];
48962306a36Sopenharmony_ci	memcpy(keybuf, sec->keys[key], len);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	erq->length = len;
49262306a36Sopenharmony_ci	erq->flags |= IW_ENCODE_ENABLED;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (ieee->open_wep)
49562306a36Sopenharmony_ci		erq->flags |= IW_ENCODE_OPEN;
49662306a36Sopenharmony_ci	else
49762306a36Sopenharmony_ci		erq->flags |= IW_ENCODE_RESTRICTED;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciint libipw_wx_set_encodeext(struct libipw_device *ieee,
50362306a36Sopenharmony_ci			       struct iw_request_info *info,
50462306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct net_device *dev = ieee->dev;
50762306a36Sopenharmony_ci	struct iw_point *encoding = &wrqu->encoding;
50862306a36Sopenharmony_ci	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
50962306a36Sopenharmony_ci	int i, idx, ret = 0;
51062306a36Sopenharmony_ci	int group_key = 0;
51162306a36Sopenharmony_ci	const char *alg, *module;
51262306a36Sopenharmony_ci	struct lib80211_crypto_ops *ops;
51362306a36Sopenharmony_ci	struct lib80211_crypt_data **crypt;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	struct libipw_security sec = {
51662306a36Sopenharmony_ci		.flags = 0,
51762306a36Sopenharmony_ci	};
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	idx = encoding->flags & IW_ENCODE_INDEX;
52062306a36Sopenharmony_ci	if (idx) {
52162306a36Sopenharmony_ci		if (idx < 1 || idx > WEP_KEYS)
52262306a36Sopenharmony_ci			return -EINVAL;
52362306a36Sopenharmony_ci		idx--;
52462306a36Sopenharmony_ci	} else
52562306a36Sopenharmony_ci		idx = ieee->crypt_info.tx_keyidx;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
52862306a36Sopenharmony_ci		crypt = &ieee->crypt_info.crypt[idx];
52962306a36Sopenharmony_ci		group_key = 1;
53062306a36Sopenharmony_ci	} else {
53162306a36Sopenharmony_ci		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
53262306a36Sopenharmony_ci		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
53362306a36Sopenharmony_ci			return -EINVAL;
53462306a36Sopenharmony_ci		if (ieee->iw_mode == IW_MODE_INFRA)
53562306a36Sopenharmony_ci			crypt = &ieee->crypt_info.crypt[idx];
53662306a36Sopenharmony_ci		else
53762306a36Sopenharmony_ci			return -EINVAL;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
54162306a36Sopenharmony_ci	if ((encoding->flags & IW_ENCODE_DISABLED) ||
54262306a36Sopenharmony_ci	    ext->alg == IW_ENCODE_ALG_NONE) {
54362306a36Sopenharmony_ci		if (*crypt)
54462306a36Sopenharmony_ci			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		for (i = 0; i < WEP_KEYS; i++)
54762306a36Sopenharmony_ci			if (ieee->crypt_info.crypt[i] != NULL)
54862306a36Sopenharmony_ci				break;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		if (i == WEP_KEYS) {
55162306a36Sopenharmony_ci			sec.enabled = 0;
55262306a36Sopenharmony_ci			sec.encrypt = 0;
55362306a36Sopenharmony_ci			sec.level = SEC_LEVEL_0;
55462306a36Sopenharmony_ci			sec.flags |= SEC_LEVEL;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		goto done;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	sec.enabled = 1;
56062306a36Sopenharmony_ci	sec.encrypt = 1;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (group_key ? !ieee->host_mc_decrypt :
56362306a36Sopenharmony_ci	    !(ieee->host_encrypt || ieee->host_decrypt ||
56462306a36Sopenharmony_ci	      ieee->host_encrypt_msdu))
56562306a36Sopenharmony_ci		goto skip_host_crypt;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	switch (ext->alg) {
56862306a36Sopenharmony_ci	case IW_ENCODE_ALG_WEP:
56962306a36Sopenharmony_ci		alg = "WEP";
57062306a36Sopenharmony_ci		module = "lib80211_crypt_wep";
57162306a36Sopenharmony_ci		break;
57262306a36Sopenharmony_ci	case IW_ENCODE_ALG_TKIP:
57362306a36Sopenharmony_ci		alg = "TKIP";
57462306a36Sopenharmony_ci		module = "lib80211_crypt_tkip";
57562306a36Sopenharmony_ci		break;
57662306a36Sopenharmony_ci	case IW_ENCODE_ALG_CCMP:
57762306a36Sopenharmony_ci		alg = "CCMP";
57862306a36Sopenharmony_ci		module = "lib80211_crypt_ccmp";
57962306a36Sopenharmony_ci		break;
58062306a36Sopenharmony_ci	default:
58162306a36Sopenharmony_ci		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
58262306a36Sopenharmony_ci				   dev->name, ext->alg);
58362306a36Sopenharmony_ci		ret = -EINVAL;
58462306a36Sopenharmony_ci		goto done;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ops = lib80211_get_crypto_ops(alg);
58862306a36Sopenharmony_ci	if (ops == NULL) {
58962306a36Sopenharmony_ci		request_module(module);
59062306a36Sopenharmony_ci		ops = lib80211_get_crypto_ops(alg);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	if (ops == NULL) {
59362306a36Sopenharmony_ci		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
59462306a36Sopenharmony_ci				   dev->name, ext->alg);
59562306a36Sopenharmony_ci		ret = -EINVAL;
59662306a36Sopenharmony_ci		goto done;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (*crypt == NULL || (*crypt)->ops != ops) {
60062306a36Sopenharmony_ci		struct lib80211_crypt_data *new_crypt;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
60562306a36Sopenharmony_ci		if (new_crypt == NULL) {
60662306a36Sopenharmony_ci			ret = -ENOMEM;
60762306a36Sopenharmony_ci			goto done;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci		new_crypt->ops = ops;
61062306a36Sopenharmony_ci		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
61162306a36Sopenharmony_ci			new_crypt->priv = new_crypt->ops->init(idx);
61262306a36Sopenharmony_ci		if (new_crypt->priv == NULL) {
61362306a36Sopenharmony_ci			kfree(new_crypt);
61462306a36Sopenharmony_ci			ret = -EINVAL;
61562306a36Sopenharmony_ci			goto done;
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci		*crypt = new_crypt;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
62162306a36Sopenharmony_ci	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
62262306a36Sopenharmony_ci				   (*crypt)->priv) < 0) {
62362306a36Sopenharmony_ci		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
62462306a36Sopenharmony_ci		ret = -EINVAL;
62562306a36Sopenharmony_ci		goto done;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci      skip_host_crypt:
62962306a36Sopenharmony_ci	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
63062306a36Sopenharmony_ci		ieee->crypt_info.tx_keyidx = idx;
63162306a36Sopenharmony_ci		sec.active_key = idx;
63262306a36Sopenharmony_ci		sec.flags |= SEC_ACTIVE_KEY;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (ext->alg != IW_ENCODE_ALG_NONE) {
63662306a36Sopenharmony_ci		int key_len = clamp_val(ext->key_len, 0, SCM_KEY_LEN);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		memcpy(sec.keys[idx], ext->key, key_len);
63962306a36Sopenharmony_ci		sec.key_sizes[idx] = key_len;
64062306a36Sopenharmony_ci		sec.flags |= (1 << idx);
64162306a36Sopenharmony_ci		if (ext->alg == IW_ENCODE_ALG_WEP) {
64262306a36Sopenharmony_ci			sec.encode_alg[idx] = SEC_ALG_WEP;
64362306a36Sopenharmony_ci			sec.flags |= SEC_LEVEL;
64462306a36Sopenharmony_ci			sec.level = SEC_LEVEL_1;
64562306a36Sopenharmony_ci		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
64662306a36Sopenharmony_ci			sec.encode_alg[idx] = SEC_ALG_TKIP;
64762306a36Sopenharmony_ci			sec.flags |= SEC_LEVEL;
64862306a36Sopenharmony_ci			sec.level = SEC_LEVEL_2;
64962306a36Sopenharmony_ci		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
65062306a36Sopenharmony_ci			sec.encode_alg[idx] = SEC_ALG_CCMP;
65162306a36Sopenharmony_ci			sec.flags |= SEC_LEVEL;
65262306a36Sopenharmony_ci			sec.level = SEC_LEVEL_3;
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci		/* Don't set sec level for group keys. */
65562306a36Sopenharmony_ci		if (group_key)
65662306a36Sopenharmony_ci			sec.flags &= ~SEC_LEVEL;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci      done:
65962306a36Sopenharmony_ci	if (ieee->set_security)
66062306a36Sopenharmony_ci		ieee->set_security(dev, &sec);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return ret;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ciint libipw_wx_get_encodeext(struct libipw_device *ieee,
66662306a36Sopenharmony_ci			       struct iw_request_info *info,
66762306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct iw_point *encoding = &wrqu->encoding;
67062306a36Sopenharmony_ci	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
67162306a36Sopenharmony_ci	struct libipw_security *sec = &ieee->sec;
67262306a36Sopenharmony_ci	int idx, max_key_len;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	max_key_len = encoding->length - sizeof(*ext);
67562306a36Sopenharmony_ci	if (max_key_len < 0)
67662306a36Sopenharmony_ci		return -EINVAL;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	idx = encoding->flags & IW_ENCODE_INDEX;
67962306a36Sopenharmony_ci	if (idx) {
68062306a36Sopenharmony_ci		if (idx < 1 || idx > WEP_KEYS)
68162306a36Sopenharmony_ci			return -EINVAL;
68262306a36Sopenharmony_ci		idx--;
68362306a36Sopenharmony_ci	} else
68462306a36Sopenharmony_ci		idx = ieee->crypt_info.tx_keyidx;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
68762306a36Sopenharmony_ci	    ext->alg != IW_ENCODE_ALG_WEP)
68862306a36Sopenharmony_ci		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
68962306a36Sopenharmony_ci			return -EINVAL;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	encoding->flags = idx + 1;
69262306a36Sopenharmony_ci	memset(ext, 0, sizeof(*ext));
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (!sec->enabled) {
69562306a36Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_NONE;
69662306a36Sopenharmony_ci		ext->key_len = 0;
69762306a36Sopenharmony_ci		encoding->flags |= IW_ENCODE_DISABLED;
69862306a36Sopenharmony_ci	} else {
69962306a36Sopenharmony_ci		if (sec->encode_alg[idx] == SEC_ALG_WEP)
70062306a36Sopenharmony_ci			ext->alg = IW_ENCODE_ALG_WEP;
70162306a36Sopenharmony_ci		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
70262306a36Sopenharmony_ci			ext->alg = IW_ENCODE_ALG_TKIP;
70362306a36Sopenharmony_ci		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
70462306a36Sopenharmony_ci			ext->alg = IW_ENCODE_ALG_CCMP;
70562306a36Sopenharmony_ci		else
70662306a36Sopenharmony_ci			return -EINVAL;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		ext->key_len = sec->key_sizes[idx];
70962306a36Sopenharmony_ci		memcpy(ext->key, sec->keys[idx], ext->key_len);
71062306a36Sopenharmony_ci		encoding->flags |= IW_ENCODE_ENABLED;
71162306a36Sopenharmony_ci		if (ext->key_len &&
71262306a36Sopenharmony_ci		    (ext->alg == IW_ENCODE_ALG_TKIP ||
71362306a36Sopenharmony_ci		     ext->alg == IW_ENCODE_ALG_CCMP))
71462306a36Sopenharmony_ci			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_wx_set_encodeext);
72262306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_wx_get_encodeext);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_wx_get_scan);
72562306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_wx_set_encode);
72662306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_wx_get_encode);
727