| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 1 | /****************************************************************************** | 
 | 2 |  | 
| James Ketrenos | ebeaddc | 2005-09-21 11:58:43 -0500 | [diff] [blame] | 3 |   Copyright(c) 2004-2005 Intel Corporation. All rights reserved. | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 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 |   <jkmaline@cc.hut.fi> | 
 | 9 |   Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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 |   James P. Ketrenos <ipw2100-admin@linux.intel.com> | 
 | 29 |   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | 
 | 30 |  | 
 | 31 | ******************************************************************************/ | 
| Jeff Garzik | bbeec90 | 2005-09-07 00:27:54 -0400 | [diff] [blame] | 32 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 33 | #include <linux/kmod.h> | 
 | 34 | #include <linux/module.h> | 
| James Ketrenos | 42e349f | 2005-09-21 11:54:07 -0500 | [diff] [blame] | 35 | #include <linux/jiffies.h> | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 36 |  | 
 | 37 | #include <net/ieee80211.h> | 
| Jeff Garzik | bbeec90 | 2005-09-07 00:27:54 -0400 | [diff] [blame] | 38 | #include <linux/wireless.h> | 
 | 39 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 40 | static const char *ieee80211_modes[] = { | 
 | 41 | 	"?", "a", "b", "ab", "g", "ag", "bg", "abg" | 
 | 42 | }; | 
 | 43 |  | 
 | 44 | #define MAX_CUSTOM_LEN 64 | 
| Larry Finger | d94606e | 2006-03-03 16:21:55 -0600 | [diff] [blame] | 45 | static char *ieee80211_translate_scan(struct ieee80211_device *ieee, | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 46 | 					   char *start, char *stop, | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 47 | 					   struct ieee80211_network *network) | 
 | 48 | { | 
 | 49 | 	char custom[MAX_CUSTOM_LEN]; | 
 | 50 | 	char *p; | 
 | 51 | 	struct iw_event iwe; | 
 | 52 | 	int i, j; | 
| Zhu Yi | 0959304 | 2006-04-13 17:17:26 +0800 | [diff] [blame] | 53 | 	char *current_val;	/* For rates */ | 
 | 54 | 	u8 rate; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 55 |  | 
 | 56 | 	/* First entry *MUST* be the AP MAC address */ | 
 | 57 | 	iwe.cmd = SIOCGIWAP; | 
 | 58 | 	iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | 
 | 59 | 	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); | 
 | 60 | 	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); | 
 | 61 |  | 
 | 62 | 	/* Remaining entries will be displayed in the order we provide them */ | 
 | 63 |  | 
 | 64 | 	/* Add the ESSID */ | 
 | 65 | 	iwe.cmd = SIOCGIWESSID; | 
 | 66 | 	iwe.u.data.flags = 1; | 
 | 67 | 	if (network->flags & NETWORK_EMPTY_ESSID) { | 
 | 68 | 		iwe.u.data.length = sizeof("<hidden>"); | 
 | 69 | 		start = iwe_stream_add_point(start, stop, &iwe, "<hidden>"); | 
 | 70 | 	} else { | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 71 | 		iwe.u.data.length = min(network->ssid_len, (u8) 32); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 72 | 		start = iwe_stream_add_point(start, stop, &iwe, network->ssid); | 
 | 73 | 	} | 
 | 74 |  | 
 | 75 | 	/* Add the protocol name */ | 
 | 76 | 	iwe.cmd = SIOCGIWNAME; | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 77 | 	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", | 
 | 78 | 		 ieee80211_modes[network->mode]); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 79 | 	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); | 
 | 80 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 81 | 	/* Add mode */ | 
 | 82 | 	iwe.cmd = SIOCGIWMODE; | 
 | 83 | 	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | 
| Jeff Garzik | 1b5cca3 | 2005-08-15 00:32:15 -0400 | [diff] [blame] | 84 | 		if (network->capability & WLAN_CAPABILITY_ESS) | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 85 | 			iwe.u.mode = IW_MODE_MASTER; | 
 | 86 | 		else | 
 | 87 | 			iwe.u.mode = IW_MODE_ADHOC; | 
 | 88 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 89 | 		start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 90 | 	} | 
 | 91 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 92 | 	/* Add frequency/channel */ | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 93 | 	iwe.cmd = SIOCGIWFREQ; | 
 | 94 | /*	iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode); | 
 | 95 | 	iwe.u.freq.e = 3; */ | 
 | 96 | 	iwe.u.freq.m = network->channel; | 
 | 97 | 	iwe.u.freq.e = 0; | 
 | 98 | 	iwe.u.freq.i = 0; | 
 | 99 | 	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); | 
 | 100 |  | 
 | 101 | 	/* Add encryption capability */ | 
 | 102 | 	iwe.cmd = SIOCGIWENCODE; | 
 | 103 | 	if (network->capability & WLAN_CAPABILITY_PRIVACY) | 
 | 104 | 		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | 
 | 105 | 	else | 
 | 106 | 		iwe.u.data.flags = IW_ENCODE_DISABLED; | 
 | 107 | 	iwe.u.data.length = 0; | 
 | 108 | 	start = iwe_stream_add_point(start, stop, &iwe, network->ssid); | 
 | 109 |  | 
 | 110 | 	/* Add basic and extended rates */ | 
| Zhu Yi | 0959304 | 2006-04-13 17:17:26 +0800 | [diff] [blame] | 111 | 	/* Rate : stuffing multiple values in a single event require a bit | 
 | 112 | 	 * more of magic - Jean II */ | 
 | 113 | 	current_val = start + IW_EV_LCP_LEN; | 
 | 114 | 	iwe.cmd = SIOCGIWRATE; | 
 | 115 | 	/* Those two flags are ignored... */ | 
 | 116 | 	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | 
 | 117 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 118 | 	for (i = 0, j = 0; i < network->rates_len;) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 119 | 		if (j < network->rates_ex_len && | 
 | 120 | 		    ((network->rates_ex[j] & 0x7F) < | 
 | 121 | 		     (network->rates[i] & 0x7F))) | 
 | 122 | 			rate = network->rates_ex[j++] & 0x7F; | 
 | 123 | 		else | 
 | 124 | 			rate = network->rates[i++] & 0x7F; | 
| Zhu Yi | 0959304 | 2006-04-13 17:17:26 +0800 | [diff] [blame] | 125 | 		/* Bit rate given in 500 kb/s units (+ 0x80) */ | 
 | 126 | 		iwe.u.bitrate.value = ((rate & 0x7f) * 500000); | 
 | 127 | 		/* Add new value to event */ | 
 | 128 | 		current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 129 | 	} | 
 | 130 | 	for (; j < network->rates_ex_len; j++) { | 
 | 131 | 		rate = network->rates_ex[j] & 0x7F; | 
| Zhu Yi | 0959304 | 2006-04-13 17:17:26 +0800 | [diff] [blame] | 132 | 		/* Bit rate given in 500 kb/s units (+ 0x80) */ | 
 | 133 | 		iwe.u.bitrate.value = ((rate & 0x7f) * 500000); | 
 | 134 | 		/* Add new value to event */ | 
 | 135 | 		current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 136 | 	} | 
| Zhu Yi | 0959304 | 2006-04-13 17:17:26 +0800 | [diff] [blame] | 137 | 	/* Check if we added any rate */ | 
 | 138 | 	if((current_val - start) > IW_EV_LCP_LEN) | 
 | 139 | 		start = current_val; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 140 |  | 
 | 141 | 	/* Add quality statistics */ | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 142 | 	iwe.cmd = IWEVQUAL; | 
| James Ketrenos | b1b508e | 2005-09-13 17:27:19 -0500 | [diff] [blame] | 143 | 	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | | 
 | 144 | 	    IW_QUAL_NOISE_UPDATED; | 
 | 145 |  | 
 | 146 | 	if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) { | 
 | 147 | 		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | | 
 | 148 | 		    IW_QUAL_LEVEL_INVALID; | 
 | 149 | 		iwe.u.qual.qual = 0; | 
| James Ketrenos | b1b508e | 2005-09-13 17:27:19 -0500 | [diff] [blame] | 150 | 	} else { | 
| Jiri Benc | 757d18f | 2005-10-10 19:16:53 +0200 | [diff] [blame] | 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) + | 
| James Ketrenos | 81f8752 | 2005-10-24 10:20:53 -0500 | [diff] [blame] | 160 | 			      62 * (ieee->perfect_rssi - | 
 | 161 | 				    network->stats.rssi))) / | 
 | 162 | 			    ((ieee->perfect_rssi - | 
 | 163 | 			      ieee->worst_rssi) * (ieee->perfect_rssi - | 
 | 164 | 						   ieee->worst_rssi)); | 
| James Ketrenos | b1b508e | 2005-09-13 17:27:19 -0500 | [diff] [blame] | 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 & IEEE80211_STATMASK_NOISE)) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 172 | 		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; | 
| James Ketrenos | b1b508e | 2005-09-13 17:27:19 -0500 | [diff] [blame] | 173 | 		iwe.u.qual.noise = 0; | 
 | 174 | 	} else { | 
 | 175 | 		iwe.u.qual.noise = network->stats.noise; | 
 | 176 | 	} | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 177 |  | 
| Zhu Yi | 7bd6436 | 2006-01-19 16:21:54 +0800 | [diff] [blame] | 178 | 	if (!(network->stats.mask & IEEE80211_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 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 185 | 	start = iwe_stream_add_event(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(start, stop, &iwe, custom); | 
 | 193 |  | 
| Zhu Yi | 4716808 | 2006-02-13 13:37:03 +0800 | [diff] [blame] | 194 | 	memset(&iwe, 0, sizeof(iwe)); | 
| James Ketrenos | 20d6471 | 2005-09-21 11:53:43 -0500 | [diff] [blame] | 195 | 	if (network->wpa_ie_len) { | 
| Zhu Yi | 4716808 | 2006-02-13 13:37:03 +0800 | [diff] [blame] | 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; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 200 | 		start = iwe_stream_add_point(start, stop, &iwe, buf); | 
 | 201 | 	} | 
 | 202 |  | 
| Zhu Yi | 4716808 | 2006-02-13 13:37:03 +0800 | [diff] [blame] | 203 | 	memset(&iwe, 0, sizeof(iwe)); | 
| James Ketrenos | 20d6471 | 2005-09-21 11:53:43 -0500 | [diff] [blame] | 204 | 	if (network->rsn_ie_len) { | 
| Zhu Yi | 4716808 | 2006-02-13 13:37:03 +0800 | [diff] [blame] | 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; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 209 | 		start = iwe_stream_add_point(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 += snprintf(p, MAX_CUSTOM_LEN - (p - custom), | 
| James Ketrenos | 42e349f | 2005-09-21 11:54:07 -0500 | [diff] [blame] | 217 | 		      " Last beacon: %dms ago", | 
 | 218 | 		      jiffies_to_msecs(jiffies - network->last_scanned)); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 219 | 	iwe.u.data.length = p - custom; | 
 | 220 | 	if (iwe.u.data.length) | 
 | 221 | 		start = iwe_stream_add_point(start, stop, &iwe, custom); | 
 | 222 |  | 
| Zhu Yi | 7bd6436 | 2006-01-19 16:21:54 +0800 | [diff] [blame] | 223 | 	/* Add spectrum management information */ | 
 | 224 | 	iwe.cmd = -1; | 
 | 225 | 	p = custom; | 
 | 226 | 	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); | 
 | 227 |  | 
 | 228 | 	if (ieee80211_get_channel_flags(ieee, network->channel) & | 
 | 229 | 	    IEEE80211_CH_INVALID) { | 
 | 230 | 		iwe.cmd = IWEVCUSTOM; | 
 | 231 | 		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); | 
 | 232 | 	} | 
 | 233 |  | 
 | 234 | 	if (ieee80211_get_channel_flags(ieee, network->channel) & | 
 | 235 | 	    IEEE80211_CH_RADAR_DETECT) { | 
 | 236 | 		iwe.cmd = IWEVCUSTOM; | 
 | 237 | 		p += snprintf(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(start, stop, &iwe, custom); | 
 | 243 | 	} | 
 | 244 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 245 | 	return start; | 
 | 246 | } | 
 | 247 |  | 
| Zhu Yi | 55cd94a | 2006-01-19 16:20:59 +0800 | [diff] [blame] | 248 | #define SCAN_ITEM_SIZE 128 | 
 | 249 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 250 | int ieee80211_wx_get_scan(struct ieee80211_device *ieee, | 
 | 251 | 			  struct iw_request_info *info, | 
 | 252 | 			  union iwreq_data *wrqu, char *extra) | 
 | 253 | { | 
 | 254 | 	struct ieee80211_network *network; | 
 | 255 | 	unsigned long flags; | 
| Zhu Yi | 55cd94a | 2006-01-19 16:20:59 +0800 | [diff] [blame] | 256 | 	int err = 0; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 257 |  | 
 | 258 | 	char *ev = extra; | 
| Zhu Yi | 55cd94a | 2006-01-19 16:20:59 +0800 | [diff] [blame] | 259 | 	char *stop = ev + wrqu->data.length; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 260 | 	int i = 0; | 
 | 261 |  | 
 | 262 | 	IEEE80211_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++; | 
| Zhu Yi | 55cd94a | 2006-01-19 16:20:59 +0800 | [diff] [blame] | 268 | 		if (stop - ev < SCAN_ITEM_SIZE) { | 
 | 269 | 			err = -E2BIG; | 
 | 270 | 			break; | 
 | 271 | 		} | 
 | 272 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 273 | 		if (ieee->scan_age == 0 || | 
 | 274 | 		    time_after(network->last_scanned + ieee->scan_age, jiffies)) | 
| Larry Finger | d94606e | 2006-03-03 16:21:55 -0600 | [diff] [blame] | 275 | 			ev = ieee80211_translate_scan(ieee, ev, stop, network); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 276 | 		else | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 277 | 			IEEE80211_DEBUG_SCAN("Not showing network '%s (" | 
| James Ketrenos | 42e349f | 2005-09-21 11:54:07 -0500 | [diff] [blame] | 278 | 					     MAC_FMT ")' due to age (%dms).\n", | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 279 | 					     escape_essid(network->ssid, | 
 | 280 | 							  network->ssid_len), | 
 | 281 | 					     MAC_ARG(network->bssid), | 
| James Ketrenos | 42e349f | 2005-09-21 11:54:07 -0500 | [diff] [blame] | 282 | 					     jiffies_to_msecs(jiffies - | 
 | 283 | 							      network-> | 
 | 284 | 							      last_scanned)); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 285 | 	} | 
 | 286 |  | 
 | 287 | 	spin_unlock_irqrestore(&ieee->lock, flags); | 
 | 288 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 289 | 	wrqu->data.length = ev - extra; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 290 | 	wrqu->data.flags = 0; | 
 | 291 |  | 
 | 292 | 	IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i); | 
 | 293 |  | 
| Zhu Yi | 55cd94a | 2006-01-19 16:20:59 +0800 | [diff] [blame] | 294 | 	return err; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 295 | } | 
 | 296 |  | 
 | 297 | int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | 
 | 298 | 			    struct iw_request_info *info, | 
 | 299 | 			    union iwreq_data *wrqu, char *keybuf) | 
 | 300 | { | 
 | 301 | 	struct iw_point *erq = &(wrqu->encoding); | 
 | 302 | 	struct net_device *dev = ieee->dev; | 
 | 303 | 	struct ieee80211_security sec = { | 
 | 304 | 		.flags = 0 | 
 | 305 | 	}; | 
 | 306 | 	int i, key, key_provided, len; | 
 | 307 | 	struct ieee80211_crypt_data **crypt; | 
| Johannes Berg | a4bf26f | 2005-12-31 11:35:20 +0100 | [diff] [blame] | 308 | 	int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 309 |  | 
 | 310 | 	IEEE80211_DEBUG_WX("SET_ENCODE\n"); | 
 | 311 |  | 
 | 312 | 	key = erq->flags & IW_ENCODE_INDEX; | 
 | 313 | 	if (key) { | 
 | 314 | 		if (key > WEP_KEYS) | 
 | 315 | 			return -EINVAL; | 
 | 316 | 		key--; | 
 | 317 | 		key_provided = 1; | 
 | 318 | 	} else { | 
 | 319 | 		key_provided = 0; | 
 | 320 | 		key = ieee->tx_keyidx; | 
 | 321 | 	} | 
 | 322 |  | 
 | 323 | 	IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? | 
 | 324 | 			   "provided" : "default"); | 
 | 325 |  | 
 | 326 | 	crypt = &ieee->crypt[key]; | 
 | 327 |  | 
 | 328 | 	if (erq->flags & IW_ENCODE_DISABLED) { | 
 | 329 | 		if (key_provided && *crypt) { | 
 | 330 | 			IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n", | 
 | 331 | 					   key); | 
 | 332 | 			ieee80211_crypt_delayed_deinit(ieee, crypt); | 
 | 333 | 		} else | 
 | 334 | 			IEEE80211_DEBUG_WX("Disabling encryption.\n"); | 
 | 335 |  | 
 | 336 | 		/* Check all the keys to see if any are still configured, | 
 | 337 | 		 * and if no key index was provided, de-init them all */ | 
 | 338 | 		for (i = 0; i < WEP_KEYS; i++) { | 
 | 339 | 			if (ieee->crypt[i] != NULL) { | 
 | 340 | 				if (key_provided) | 
 | 341 | 					break; | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 342 | 				ieee80211_crypt_delayed_deinit(ieee, | 
 | 343 | 							       &ieee->crypt[i]); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 344 | 			} | 
 | 345 | 		} | 
 | 346 |  | 
 | 347 | 		if (i == WEP_KEYS) { | 
 | 348 | 			sec.enabled = 0; | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 349 | 			sec.encrypt = 0; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 350 | 			sec.level = SEC_LEVEL_0; | 
| James Ketrenos | 259bf1f | 2005-09-21 11:54:22 -0500 | [diff] [blame] | 351 | 			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 352 | 		} | 
 | 353 |  | 
 | 354 | 		goto done; | 
 | 355 | 	} | 
 | 356 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 357 | 	sec.enabled = 1; | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 358 | 	sec.encrypt = 1; | 
| James Ketrenos | 259bf1f | 2005-09-21 11:54:22 -0500 | [diff] [blame] | 359 | 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 360 |  | 
 | 361 | 	if (*crypt != NULL && (*crypt)->ops != NULL && | 
 | 362 | 	    strcmp((*crypt)->ops->name, "WEP") != 0) { | 
 | 363 | 		/* changing to use WEP; deinit previously used algorithm | 
 | 364 | 		 * on this key */ | 
 | 365 | 		ieee80211_crypt_delayed_deinit(ieee, crypt); | 
 | 366 | 	} | 
 | 367 |  | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 368 | 	if (*crypt == NULL && host_crypto) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 369 | 		struct ieee80211_crypt_data *new_crypt; | 
 | 370 |  | 
 | 371 | 		/* take WEP into use */ | 
| Panagiotis Issaris | 0da974f | 2006-07-21 14:51:30 -0700 | [diff] [blame] | 372 | 		new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data), | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 373 | 				    GFP_KERNEL); | 
 | 374 | 		if (new_crypt == NULL) | 
 | 375 | 			return -ENOMEM; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 376 | 		new_crypt->ops = ieee80211_get_crypto_ops("WEP"); | 
 | 377 | 		if (!new_crypt->ops) { | 
 | 378 | 			request_module("ieee80211_crypt_wep"); | 
 | 379 | 			new_crypt->ops = ieee80211_get_crypto_ops("WEP"); | 
 | 380 | 		} | 
 | 381 |  | 
 | 382 | 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) | 
| James Ketrenos | 6eb6edf | 2005-09-22 10:34:15 +0000 | [diff] [blame] | 383 | 			new_crypt->priv = new_crypt->ops->init(key); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 384 |  | 
 | 385 | 		if (!new_crypt->ops || !new_crypt->priv) { | 
 | 386 | 			kfree(new_crypt); | 
 | 387 | 			new_crypt = NULL; | 
 | 388 |  | 
 | 389 | 			printk(KERN_WARNING "%s: could not initialize WEP: " | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 390 | 			       "load module ieee80211_crypt_wep\n", dev->name); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 391 | 			return -EOPNOTSUPP; | 
 | 392 | 		} | 
 | 393 | 		*crypt = new_crypt; | 
 | 394 | 	} | 
 | 395 |  | 
 | 396 | 	/* If a new key was provided, set it up */ | 
 | 397 | 	if (erq->length > 0) { | 
 | 398 | 		len = erq->length <= 5 ? 5 : 13; | 
 | 399 | 		memcpy(sec.keys[key], keybuf, erq->length); | 
 | 400 | 		if (len > erq->length) | 
 | 401 | 			memset(sec.keys[key] + erq->length, 0, | 
 | 402 | 			       len - erq->length); | 
 | 403 | 		IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", | 
 | 404 | 				   key, escape_essid(sec.keys[key], len), | 
 | 405 | 				   erq->length, len); | 
 | 406 | 		sec.key_sizes[key] = len; | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 407 | 		if (*crypt) | 
 | 408 | 			(*crypt)->ops->set_key(sec.keys[key], len, NULL, | 
 | 409 | 					       (*crypt)->priv); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 410 | 		sec.flags |= (1 << key); | 
 | 411 | 		/* This ensures a key will be activated if no key is | 
 | 412 | 		 * explicitely set */ | 
 | 413 | 		if (key == sec.active_key) | 
 | 414 | 			sec.flags |= SEC_ACTIVE_KEY; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 415 |  | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 416 | 	} else { | 
 | 417 | 		if (host_crypto) { | 
 | 418 | 			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, | 
 | 419 | 						     NULL, (*crypt)->priv); | 
 | 420 | 			if (len == 0) { | 
 | 421 | 				/* Set a default key of all 0 */ | 
 | 422 | 				IEEE80211_DEBUG_WX("Setting key %d to all " | 
 | 423 | 						   "zero.\n", key); | 
 | 424 | 				memset(sec.keys[key], 0, 13); | 
 | 425 | 				(*crypt)->ops->set_key(sec.keys[key], 13, NULL, | 
 | 426 | 						       (*crypt)->priv); | 
 | 427 | 				sec.key_sizes[key] = 13; | 
 | 428 | 				sec.flags |= (1 << key); | 
 | 429 | 			} | 
 | 430 | 		} | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 431 | 		/* No key data - just set the default TX key index */ | 
 | 432 | 		if (key_provided) { | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 433 | 			IEEE80211_DEBUG_WX("Setting key %d to default Tx " | 
 | 434 | 					   "key.\n", key); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 435 | 			ieee->tx_keyidx = key; | 
 | 436 | 			sec.active_key = key; | 
 | 437 | 			sec.flags |= SEC_ACTIVE_KEY; | 
 | 438 | 		} | 
 | 439 | 	} | 
| James Ketrenos | 7dc888f | 2005-09-21 11:58:38 -0500 | [diff] [blame] | 440 | 	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { | 
 | 441 | 		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); | 
 | 442 | 		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : | 
 | 443 | 		    WLAN_AUTH_SHARED_KEY; | 
 | 444 | 		sec.flags |= SEC_AUTH_MODE; | 
 | 445 | 		IEEE80211_DEBUG_WX("Auth: %s\n", | 
 | 446 | 				   sec.auth_mode == WLAN_AUTH_OPEN ? | 
 | 447 | 				   "OPEN" : "SHARED KEY"); | 
 | 448 | 	} | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 449 |  | 
 | 450 | 	/* For now we just support WEP, so only set that security level... | 
 | 451 | 	 * TODO: When WPA is added this is one place that needs to change */ | 
 | 452 | 	sec.flags |= SEC_LEVEL; | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 453 | 	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */ | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 454 | 	sec.encode_alg[key] = SEC_ALG_WEP; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 455 |  | 
| James Ketrenos | 259bf1f | 2005-09-21 11:54:22 -0500 | [diff] [blame] | 456 |       done: | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 457 | 	if (ieee->set_security) | 
 | 458 | 		ieee->set_security(dev, &sec); | 
 | 459 |  | 
 | 460 | 	/* Do not reset port if card is in Managed mode since resetting will | 
 | 461 | 	 * generate new IEEE 802.11 authentication which may end up in looping | 
 | 462 | 	 * with IEEE 802.1X.  If your hardware requires a reset after WEP | 
 | 463 | 	 * configuration (for example... Prism2), implement the reset_port in | 
 | 464 | 	 * the callbacks structures used to initialize the 802.11 stack. */ | 
 | 465 | 	if (ieee->reset_on_keychange && | 
 | 466 | 	    ieee->iw_mode != IW_MODE_INFRA && | 
 | 467 | 	    ieee->reset_port && ieee->reset_port(dev)) { | 
 | 468 | 		printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); | 
 | 469 | 		return -EINVAL; | 
 | 470 | 	} | 
 | 471 | 	return 0; | 
 | 472 | } | 
 | 473 |  | 
 | 474 | int ieee80211_wx_get_encode(struct ieee80211_device *ieee, | 
 | 475 | 			    struct iw_request_info *info, | 
 | 476 | 			    union iwreq_data *wrqu, char *keybuf) | 
 | 477 | { | 
 | 478 | 	struct iw_point *erq = &(wrqu->encoding); | 
 | 479 | 	int len, key; | 
 | 480 | 	struct ieee80211_crypt_data *crypt; | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 481 | 	struct ieee80211_security *sec = &ieee->sec; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 482 |  | 
 | 483 | 	IEEE80211_DEBUG_WX("GET_ENCODE\n"); | 
 | 484 |  | 
 | 485 | 	key = erq->flags & IW_ENCODE_INDEX; | 
 | 486 | 	if (key) { | 
 | 487 | 		if (key > WEP_KEYS) | 
 | 488 | 			return -EINVAL; | 
 | 489 | 		key--; | 
 | 490 | 	} else | 
 | 491 | 		key = ieee->tx_keyidx; | 
 | 492 |  | 
 | 493 | 	crypt = ieee->crypt[key]; | 
 | 494 | 	erq->flags = key + 1; | 
 | 495 |  | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 496 | 	if (!sec->enabled) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 497 | 		erq->length = 0; | 
 | 498 | 		erq->flags |= IW_ENCODE_DISABLED; | 
 | 499 | 		return 0; | 
 | 500 | 	} | 
 | 501 |  | 
| James Ketrenos | f1bf663 | 2005-09-21 11:53:54 -0500 | [diff] [blame] | 502 | 	len = sec->key_sizes[key]; | 
 | 503 | 	memcpy(keybuf, sec->keys[key], len); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 504 |  | 
| Adrian Bunk | 6274115 | 2006-04-27 02:33:42 -0700 | [diff] [blame] | 505 | 	erq->length = len; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 506 | 	erq->flags |= IW_ENCODE_ENABLED; | 
 | 507 |  | 
 | 508 | 	if (ieee->open_wep) | 
 | 509 | 		erq->flags |= IW_ENCODE_OPEN; | 
 | 510 | 	else | 
 | 511 | 		erq->flags |= IW_ENCODE_RESTRICTED; | 
 | 512 |  | 
 | 513 | 	return 0; | 
 | 514 | } | 
 | 515 |  | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 516 | int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, | 
 | 517 | 			       struct iw_request_info *info, | 
 | 518 | 			       union iwreq_data *wrqu, char *extra) | 
 | 519 | { | 
 | 520 | 	struct net_device *dev = ieee->dev; | 
 | 521 | 	struct iw_point *encoding = &wrqu->encoding; | 
 | 522 | 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | 
 | 523 | 	int i, idx, ret = 0; | 
| James Ketrenos | ccd0fda | 2005-09-21 11:58:32 -0500 | [diff] [blame] | 524 | 	int group_key = 0; | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 525 | 	const char *alg, *module; | 
 | 526 | 	struct ieee80211_crypto_ops *ops; | 
 | 527 | 	struct ieee80211_crypt_data **crypt; | 
 | 528 |  | 
 | 529 | 	struct ieee80211_security sec = { | 
 | 530 | 		.flags = 0, | 
 | 531 | 	}; | 
 | 532 |  | 
 | 533 | 	idx = encoding->flags & IW_ENCODE_INDEX; | 
 | 534 | 	if (idx) { | 
 | 535 | 		if (idx < 1 || idx > WEP_KEYS) | 
 | 536 | 			return -EINVAL; | 
 | 537 | 		idx--; | 
 | 538 | 	} else | 
 | 539 | 		idx = ieee->tx_keyidx; | 
 | 540 |  | 
| James Ketrenos | ccd0fda | 2005-09-21 11:58:32 -0500 | [diff] [blame] | 541 | 	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 542 | 		crypt = &ieee->crypt[idx]; | 
| James Ketrenos | ccd0fda | 2005-09-21 11:58:32 -0500 | [diff] [blame] | 543 | 		group_key = 1; | 
 | 544 | 	} else { | 
| Volker Braun | e189277 | 2005-10-24 10:15:36 -0500 | [diff] [blame] | 545 | 		/* some Cisco APs use idx>0 for unicast in dynamic WEP */ | 
 | 546 | 		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 547 | 			return -EINVAL; | 
 | 548 | 		if (ieee->iw_mode == IW_MODE_INFRA) | 
 | 549 | 			crypt = &ieee->crypt[idx]; | 
 | 550 | 		else | 
 | 551 | 			return -EINVAL; | 
 | 552 | 	} | 
 | 553 |  | 
 | 554 | 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | 
 | 555 | 	if ((encoding->flags & IW_ENCODE_DISABLED) || | 
 | 556 | 	    ext->alg == IW_ENCODE_ALG_NONE) { | 
 | 557 | 		if (*crypt) | 
 | 558 | 			ieee80211_crypt_delayed_deinit(ieee, crypt); | 
 | 559 |  | 
 | 560 | 		for (i = 0; i < WEP_KEYS; i++) | 
 | 561 | 			if (ieee->crypt[i] != NULL) | 
 | 562 | 				break; | 
 | 563 |  | 
 | 564 | 		if (i == WEP_KEYS) { | 
 | 565 | 			sec.enabled = 0; | 
 | 566 | 			sec.encrypt = 0; | 
 | 567 | 			sec.level = SEC_LEVEL_0; | 
 | 568 | 			sec.flags |= SEC_LEVEL; | 
 | 569 | 		} | 
 | 570 | 		goto done; | 
 | 571 | 	} | 
 | 572 |  | 
 | 573 | 	sec.enabled = 1; | 
 | 574 | 	sec.encrypt = 1; | 
 | 575 |  | 
| James Ketrenos | ccd0fda | 2005-09-21 11:58:32 -0500 | [diff] [blame] | 576 | 	if (group_key ? !ieee->host_mc_decrypt : | 
 | 577 | 	    !(ieee->host_encrypt || ieee->host_decrypt || | 
 | 578 | 	      ieee->host_encrypt_msdu)) | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 579 | 		goto skip_host_crypt; | 
 | 580 |  | 
 | 581 | 	switch (ext->alg) { | 
 | 582 | 	case IW_ENCODE_ALG_WEP: | 
 | 583 | 		alg = "WEP"; | 
 | 584 | 		module = "ieee80211_crypt_wep"; | 
 | 585 | 		break; | 
 | 586 | 	case IW_ENCODE_ALG_TKIP: | 
 | 587 | 		alg = "TKIP"; | 
 | 588 | 		module = "ieee80211_crypt_tkip"; | 
 | 589 | 		break; | 
 | 590 | 	case IW_ENCODE_ALG_CCMP: | 
 | 591 | 		alg = "CCMP"; | 
 | 592 | 		module = "ieee80211_crypt_ccmp"; | 
 | 593 | 		break; | 
 | 594 | 	default: | 
 | 595 | 		IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | 
 | 596 | 				   dev->name, ext->alg); | 
 | 597 | 		ret = -EINVAL; | 
 | 598 | 		goto done; | 
 | 599 | 	} | 
 | 600 |  | 
 | 601 | 	ops = ieee80211_get_crypto_ops(alg); | 
 | 602 | 	if (ops == NULL) { | 
 | 603 | 		request_module(module); | 
 | 604 | 		ops = ieee80211_get_crypto_ops(alg); | 
 | 605 | 	} | 
 | 606 | 	if (ops == NULL) { | 
 | 607 | 		IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | 
 | 608 | 				   dev->name, ext->alg); | 
 | 609 | 		ret = -EINVAL; | 
 | 610 | 		goto done; | 
 | 611 | 	} | 
 | 612 |  | 
 | 613 | 	if (*crypt == NULL || (*crypt)->ops != ops) { | 
 | 614 | 		struct ieee80211_crypt_data *new_crypt; | 
 | 615 |  | 
 | 616 | 		ieee80211_crypt_delayed_deinit(ieee, crypt); | 
 | 617 |  | 
| Panagiotis Issaris | 0da974f | 2006-07-21 14:51:30 -0700 | [diff] [blame] | 618 | 		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 619 | 		if (new_crypt == NULL) { | 
 | 620 | 			ret = -ENOMEM; | 
 | 621 | 			goto done; | 
 | 622 | 		} | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 623 | 		new_crypt->ops = ops; | 
 | 624 | 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) | 
| James Ketrenos | 6eb6edf | 2005-09-22 10:34:15 +0000 | [diff] [blame] | 625 | 			new_crypt->priv = new_crypt->ops->init(idx); | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 626 | 		if (new_crypt->priv == NULL) { | 
 | 627 | 			kfree(new_crypt); | 
 | 628 | 			ret = -EINVAL; | 
 | 629 | 			goto done; | 
 | 630 | 		} | 
 | 631 | 		*crypt = new_crypt; | 
 | 632 | 	} | 
 | 633 |  | 
 | 634 | 	if (ext->key_len > 0 && (*crypt)->ops->set_key && | 
 | 635 | 	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, | 
 | 636 | 				   (*crypt)->priv) < 0) { | 
 | 637 | 		IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name); | 
 | 638 | 		ret = -EINVAL; | 
 | 639 | 		goto done; | 
 | 640 | 	} | 
 | 641 |  | 
 | 642 |       skip_host_crypt: | 
 | 643 | 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | 
 | 644 | 		ieee->tx_keyidx = idx; | 
 | 645 | 		sec.active_key = idx; | 
 | 646 | 		sec.flags |= SEC_ACTIVE_KEY; | 
 | 647 | 	} | 
 | 648 |  | 
 | 649 | 	if (ext->alg != IW_ENCODE_ALG_NONE) { | 
 | 650 | 		memcpy(sec.keys[idx], ext->key, ext->key_len); | 
 | 651 | 		sec.key_sizes[idx] = ext->key_len; | 
 | 652 | 		sec.flags |= (1 << idx); | 
 | 653 | 		if (ext->alg == IW_ENCODE_ALG_WEP) { | 
 | 654 | 			sec.encode_alg[idx] = SEC_ALG_WEP; | 
 | 655 | 			sec.flags |= SEC_LEVEL; | 
 | 656 | 			sec.level = SEC_LEVEL_1; | 
 | 657 | 		} else if (ext->alg == IW_ENCODE_ALG_TKIP) { | 
 | 658 | 			sec.encode_alg[idx] = SEC_ALG_TKIP; | 
 | 659 | 			sec.flags |= SEC_LEVEL; | 
 | 660 | 			sec.level = SEC_LEVEL_2; | 
 | 661 | 		} else if (ext->alg == IW_ENCODE_ALG_CCMP) { | 
 | 662 | 			sec.encode_alg[idx] = SEC_ALG_CCMP; | 
 | 663 | 			sec.flags |= SEC_LEVEL; | 
 | 664 | 			sec.level = SEC_LEVEL_3; | 
 | 665 | 		} | 
| James Ketrenos | ccd0fda | 2005-09-21 11:58:32 -0500 | [diff] [blame] | 666 | 		/* Don't set sec level for group keys. */ | 
 | 667 | 		if (group_key) | 
 | 668 | 			sec.flags &= ~SEC_LEVEL; | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 669 | 	} | 
 | 670 |       done: | 
 | 671 | 	if (ieee->set_security) | 
 | 672 | 		ieee->set_security(ieee->dev, &sec); | 
 | 673 |  | 
 | 674 | 	/* | 
 | 675 | 	 * Do not reset port if card is in Managed mode since resetting will | 
 | 676 | 	 * generate new IEEE 802.11 authentication which may end up in looping | 
 | 677 | 	 * with IEEE 802.1X. If your hardware requires a reset after WEP | 
 | 678 | 	 * configuration (for example... Prism2), implement the reset_port in | 
 | 679 | 	 * the callbacks structures used to initialize the 802.11 stack. | 
 | 680 | 	 */ | 
 | 681 | 	if (ieee->reset_on_keychange && | 
 | 682 | 	    ieee->iw_mode != IW_MODE_INFRA && | 
 | 683 | 	    ieee->reset_port && ieee->reset_port(dev)) { | 
 | 684 | 		IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name); | 
 | 685 | 		return -EINVAL; | 
 | 686 | 	} | 
 | 687 |  | 
 | 688 | 	return ret; | 
 | 689 | } | 
 | 690 |  | 
 | 691 | int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, | 
 | 692 | 			       struct iw_request_info *info, | 
 | 693 | 			       union iwreq_data *wrqu, char *extra) | 
 | 694 | { | 
 | 695 | 	struct iw_point *encoding = &wrqu->encoding; | 
 | 696 | 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | 
 | 697 | 	struct ieee80211_security *sec = &ieee->sec; | 
 | 698 | 	int idx, max_key_len; | 
 | 699 |  | 
 | 700 | 	max_key_len = encoding->length - sizeof(*ext); | 
 | 701 | 	if (max_key_len < 0) | 
 | 702 | 		return -EINVAL; | 
 | 703 |  | 
 | 704 | 	idx = encoding->flags & IW_ENCODE_INDEX; | 
 | 705 | 	if (idx) { | 
 | 706 | 		if (idx < 1 || idx > WEP_KEYS) | 
 | 707 | 			return -EINVAL; | 
 | 708 | 		idx--; | 
 | 709 | 	} else | 
 | 710 | 		idx = ieee->tx_keyidx; | 
 | 711 |  | 
| Volker Braun | e189277 | 2005-10-24 10:15:36 -0500 | [diff] [blame] | 712 | 	if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && | 
 | 713 | 	    ext->alg != IW_ENCODE_ALG_WEP) | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 714 | 		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) | 
 | 715 | 			return -EINVAL; | 
 | 716 |  | 
 | 717 | 	encoding->flags = idx + 1; | 
 | 718 | 	memset(ext, 0, sizeof(*ext)); | 
 | 719 |  | 
 | 720 | 	if (!sec->enabled) { | 
 | 721 | 		ext->alg = IW_ENCODE_ALG_NONE; | 
 | 722 | 		ext->key_len = 0; | 
 | 723 | 		encoding->flags |= IW_ENCODE_DISABLED; | 
 | 724 | 	} else { | 
 | 725 | 		if (sec->encode_alg[idx] == SEC_ALG_WEP) | 
 | 726 | 			ext->alg = IW_ENCODE_ALG_WEP; | 
 | 727 | 		else if (sec->encode_alg[idx] == SEC_ALG_TKIP) | 
 | 728 | 			ext->alg = IW_ENCODE_ALG_TKIP; | 
 | 729 | 		else if (sec->encode_alg[idx] == SEC_ALG_CCMP) | 
 | 730 | 			ext->alg = IW_ENCODE_ALG_CCMP; | 
 | 731 | 		else | 
 | 732 | 			return -EINVAL; | 
 | 733 |  | 
 | 734 | 		ext->key_len = sec->key_sizes[idx]; | 
 | 735 | 		memcpy(ext->key, sec->keys[idx], ext->key_len); | 
 | 736 | 		encoding->flags |= IW_ENCODE_ENABLED; | 
 | 737 | 		if (ext->key_len && | 
 | 738 | 		    (ext->alg == IW_ENCODE_ALG_TKIP || | 
 | 739 | 		     ext->alg == IW_ENCODE_ALG_CCMP)) | 
 | 740 | 			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; | 
 | 741 |  | 
 | 742 | 	} | 
 | 743 |  | 
 | 744 | 	return 0; | 
 | 745 | } | 
 | 746 |  | 
| Larry Finger | dd5eeb4 | 2006-01-30 13:12:50 +0100 | [diff] [blame] | 747 | int ieee80211_wx_set_auth(struct net_device *dev, | 
 | 748 | 			  struct iw_request_info *info, | 
 | 749 | 			  union iwreq_data *wrqu, | 
 | 750 | 			  char *extra) | 
 | 751 | { | 
 | 752 | 	struct ieee80211_device *ieee = netdev_priv(dev); | 
 | 753 | 	unsigned long flags; | 
 | 754 | 	int err = 0; | 
 | 755 |  | 
 | 756 | 	spin_lock_irqsave(&ieee->lock, flags); | 
 | 757 | 	 | 
 | 758 | 	switch (wrqu->param.flags & IW_AUTH_INDEX) { | 
 | 759 | 	case IW_AUTH_WPA_VERSION: | 
 | 760 | 	case IW_AUTH_CIPHER_PAIRWISE: | 
 | 761 | 	case IW_AUTH_CIPHER_GROUP: | 
 | 762 | 	case IW_AUTH_KEY_MGMT: | 
 | 763 | 		/* | 
 | 764 | 		 * Host AP driver does not use these parameters and allows | 
 | 765 | 		 * wpa_supplicant to control them internally. | 
 | 766 | 		 */ | 
 | 767 | 		break; | 
 | 768 | 	case IW_AUTH_TKIP_COUNTERMEASURES: | 
 | 769 | 		break;		/* FIXME */ | 
 | 770 | 	case IW_AUTH_DROP_UNENCRYPTED: | 
 | 771 | 		ieee->drop_unencrypted = !!wrqu->param.value; | 
 | 772 | 		break; | 
 | 773 | 	case IW_AUTH_80211_AUTH_ALG: | 
 | 774 | 		break;		/* FIXME */ | 
 | 775 | 	case IW_AUTH_WPA_ENABLED: | 
 | 776 | 		ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value; | 
 | 777 | 		break; | 
 | 778 | 	case IW_AUTH_RX_UNENCRYPTED_EAPOL: | 
 | 779 | 		ieee->ieee802_1x = !!wrqu->param.value; | 
 | 780 | 		break; | 
 | 781 | 	case IW_AUTH_PRIVACY_INVOKED: | 
 | 782 | 		ieee->privacy_invoked = !!wrqu->param.value; | 
 | 783 | 		break; | 
 | 784 | 	default: | 
 | 785 | 		err = -EOPNOTSUPP; | 
 | 786 | 		break; | 
 | 787 | 	} | 
 | 788 | 	spin_unlock_irqrestore(&ieee->lock, flags); | 
 | 789 | 	return err; | 
 | 790 | } | 
 | 791 |  | 
 | 792 | int ieee80211_wx_get_auth(struct net_device *dev, | 
 | 793 | 			  struct iw_request_info *info, | 
 | 794 | 			  union iwreq_data *wrqu, | 
 | 795 | 			  char *extra) | 
 | 796 | { | 
 | 797 | 	struct ieee80211_device *ieee = netdev_priv(dev); | 
 | 798 | 	unsigned long flags; | 
 | 799 | 	int err = 0; | 
 | 800 |  | 
 | 801 | 	spin_lock_irqsave(&ieee->lock, flags); | 
 | 802 | 	 | 
 | 803 | 	switch (wrqu->param.flags & IW_AUTH_INDEX) { | 
 | 804 | 	case IW_AUTH_WPA_VERSION: | 
 | 805 | 	case IW_AUTH_CIPHER_PAIRWISE: | 
 | 806 | 	case IW_AUTH_CIPHER_GROUP: | 
 | 807 | 	case IW_AUTH_KEY_MGMT: | 
 | 808 | 	case IW_AUTH_TKIP_COUNTERMEASURES:		/* FIXME */ | 
 | 809 | 	case IW_AUTH_80211_AUTH_ALG:			/* FIXME */ | 
 | 810 | 		/* | 
 | 811 | 		 * Host AP driver does not use these parameters and allows | 
 | 812 | 		 * wpa_supplicant to control them internally. | 
 | 813 | 		 */ | 
 | 814 | 		err = -EOPNOTSUPP; | 
 | 815 | 		break; | 
 | 816 | 	case IW_AUTH_DROP_UNENCRYPTED: | 
 | 817 | 		wrqu->param.value = ieee->drop_unencrypted; | 
 | 818 | 		break; | 
 | 819 | 	case IW_AUTH_WPA_ENABLED: | 
 | 820 | 		wrqu->param.value = ieee->wpa_enabled; | 
 | 821 | 		break; | 
 | 822 | 	case IW_AUTH_RX_UNENCRYPTED_EAPOL: | 
 | 823 | 		wrqu->param.value = ieee->ieee802_1x; | 
 | 824 | 		break; | 
 | 825 | 	default: | 
 | 826 | 		err = -EOPNOTSUPP; | 
 | 827 | 		break; | 
 | 828 | 	} | 
 | 829 | 	spin_unlock_irqrestore(&ieee->lock, flags); | 
 | 830 | 	return err; | 
 | 831 | } | 
 | 832 |  | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 833 | EXPORT_SYMBOL(ieee80211_wx_set_encodeext); | 
 | 834 | EXPORT_SYMBOL(ieee80211_wx_get_encodeext); | 
| James Ketrenos | e0d369d | 2005-09-21 11:54:30 -0500 | [diff] [blame] | 835 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 836 | EXPORT_SYMBOL(ieee80211_wx_get_scan); | 
 | 837 | EXPORT_SYMBOL(ieee80211_wx_set_encode); | 
 | 838 | EXPORT_SYMBOL(ieee80211_wx_get_encode); | 
| Larry Finger | dd5eeb4 | 2006-01-30 13:12:50 +0100 | [diff] [blame] | 839 |  | 
 | 840 | EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth); | 
 | 841 | EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth); |