| 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); | 
| YOSHIFUJI Hideaki | 6426565 | 2007-02-09 23:24:46 +0900 | [diff] [blame] | 757 |  | 
| Larry Finger | dd5eeb4 | 2006-01-30 13:12:50 +0100 | [diff] [blame] | 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); | 
| YOSHIFUJI Hideaki | 6426565 | 2007-02-09 23:24:46 +0900 | [diff] [blame] | 802 |  | 
| Larry Finger | dd5eeb4 | 2006-01-30 13:12:50 +0100 | [diff] [blame] | 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); |