blob: 750e94e17114dfd65f5530b79310abca3d633910 [file] [log] [blame]
Jerry Chuang8fc85982009-11-03 07:17:11 -02001/******************************************************************************
2
3 Copyright(c) 2004 Intel Corporation. All rights reserved.
4
5 Portions of this file are based on the WEP enablement code provided by the
6 Host AP project hostap-drivers v0.1.3
7 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
8 <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******************************************************************************/
32#include <linux/wireless.h>
33#include <linux/version.h>
34#include <linux/kmod.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
Jerry Chuang8fc85982009-11-03 07:17:11 -020036#include <linux/module.h>
37
38#include "ieee80211.h"
Jerry Chuang8fc85982009-11-03 07:17:11 -020039struct modes_unit {
40 char *mode_string;
41 int mode_size;
42};
43struct modes_unit ieee80211_modes[] = {
44 {"a",1},
45 {"b",1},
46 {"g",1},
47 {"?",1},
48 {"N-24G",5},
49 {"N-5G",4},
50};
51
Jerry Chuang8fc85982009-11-03 07:17:11 -020052#define iwe_stream_add_event_rsl iwe_stream_add_event
Jerry Chuang8fc85982009-11-03 07:17:11 -020053
54#define MAX_CUSTOM_LEN 64
55static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee,
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020056 char *start, char *stop,
Jerry Chuang8fc85982009-11-03 07:17:11 -020057 struct ieee80211_network *network,
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020058 struct iw_request_info *info)
Jerry Chuang8fc85982009-11-03 07:17:11 -020059{
60 char custom[MAX_CUSTOM_LEN];
61 char proto_name[IFNAMSIZ];
62 char *pname = proto_name;
63 char *p;
64 struct iw_event iwe;
65 int i, j;
66 u16 max_rate, rate;
67 static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33};
68
69 /* First entry *MUST* be the AP MAC address */
70 iwe.cmd = SIOCGIWAP;
71 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
72 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
Jerry Chuang8fc85982009-11-03 07:17:11 -020073 start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jerry Chuang8fc85982009-11-03 07:17:11 -020074 /* Remaining entries will be displayed in the order we provide them */
75
76 /* Add the ESSID */
77 iwe.cmd = SIOCGIWESSID;
78 iwe.u.data.flags = 1;
79// if (network->flags & NETWORK_EMPTY_ESSID) {
80 if (network->ssid_len == 0) {
81 iwe.u.data.length = sizeof("<hidden>");
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020082 start = iwe_stream_add_point(info, start, stop, &iwe, "<hidden>");
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020083 } else {
Jerry Chuang8fc85982009-11-03 07:17:11 -020084 iwe.u.data.length = min(network->ssid_len, (u8)32);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020085 start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020086 }
Jerry Chuang8fc85982009-11-03 07:17:11 -020087 /* Add the protocol name */
88 iwe.cmd = SIOCGIWNAME;
89 for(i=0; i<(sizeof(ieee80211_modes)/sizeof(ieee80211_modes[0])); i++) {
90 if(network->mode&(1<<i)) {
91 sprintf(pname,ieee80211_modes[i].mode_string,ieee80211_modes[i].mode_size);
92 pname +=ieee80211_modes[i].mode_size;
93 }
94 }
95 *pname = '\0';
96 snprintf(iwe.u.name, IFNAMSIZ, "IEEE802.11%s", proto_name);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020097 start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -020098 /* Add mode */
99 iwe.cmd = SIOCGIWMODE;
100 if (network->capability &
Jerry Chuang8fc85982009-11-03 07:17:11 -0200101 (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) {
102 if (network->capability & WLAN_CAPABILITY_BSS)
103 iwe.u.mode = IW_MODE_MASTER;
104 else
105 iwe.u.mode = IW_MODE_ADHOC;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200106 start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_UINT_LEN);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200107 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200108
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200109 /* Add frequency/channel */
Jerry Chuang8fc85982009-11-03 07:17:11 -0200110 iwe.cmd = SIOCGIWFREQ;
111/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
112 iwe.u.freq.e = 3; */
113 iwe.u.freq.m = network->channel;
114 iwe.u.freq.e = 0;
115 iwe.u.freq.i = 0;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200116 start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200117 /* Add encryption capability */
118 iwe.cmd = SIOCGIWENCODE;
119 if (network->capability & WLAN_CAPABILITY_PRIVACY)
120 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
121 else
122 iwe.u.data.flags = IW_ENCODE_DISABLED;
123 iwe.u.data.length = 0;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200124 start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200125 /* Add basic and extended rates */
126 max_rate = 0;
127 p = custom;
128 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
129 for (i = 0, j = 0; i < network->rates_len; ) {
130 if (j < network->rates_ex_len &&
131 ((network->rates_ex[j] & 0x7F) <
132 (network->rates[i] & 0x7F)))
133 rate = network->rates_ex[j++] & 0x7F;
134 else
135 rate = network->rates[i++] & 0x7F;
136 if (rate > max_rate)
137 max_rate = rate;
138 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
139 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
140 }
141 for (; j < network->rates_ex_len; j++) {
142 rate = network->rates_ex[j] & 0x7F;
143 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
144 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
145 if (rate > max_rate)
146 max_rate = rate;
147 }
148
149 if (network->mode >= IEEE_N_24G)//add N rate here;
150 {
151 PHT_CAPABILITY_ELE ht_cap = NULL;
152 bool is40M = false, isShortGI = false;
153 u8 max_mcs = 0;
154 if (!memcmp(network->bssht.bdHTCapBuf, EWC11NHTCap, 4))
155 ht_cap = (PHT_CAPABILITY_ELE)&network->bssht.bdHTCapBuf[4];
156 else
157 ht_cap = (PHT_CAPABILITY_ELE)&network->bssht.bdHTCapBuf[0];
158 is40M = (ht_cap->ChlWidth)?1:0;
159 isShortGI = (ht_cap->ChlWidth)?
160 ((ht_cap->ShortGI40Mhz)?1:0):
161 ((ht_cap->ShortGI20Mhz)?1:0);
162
163 max_mcs = HTGetHighestMCSRate(ieee, ht_cap->MCS, MCS_FILTER_ALL);
164 rate = MCS_DATA_RATE[is40M][isShortGI][max_mcs&0x7f];
165 if (rate > max_rate)
166 max_rate = rate;
167 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200168 iwe.cmd = SIOCGIWRATE;
169 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
170 iwe.u.bitrate.value = max_rate * 500000;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200171 start = iwe_stream_add_event_rsl(info, start, stop, &iwe,
Jerry Chuang8fc85982009-11-03 07:17:11 -0200172 IW_EV_PARAM_LEN);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200173 iwe.cmd = IWEVCUSTOM;
174 iwe.u.data.length = p - custom;
175 if (iwe.u.data.length)
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200176 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200177 /* Add quality statistics */
178 /* TODO: Fix these values... */
179 iwe.cmd = IWEVQUAL;
180 iwe.u.qual.qual = network->stats.signal;
181 iwe.u.qual.level = network->stats.rssi;
182 iwe.u.qual.noise = network->stats.noise;
183 iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
184 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
185 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
186 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
187 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
188 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
189 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
190 iwe.u.qual.updated = 7;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200191 start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200192 iwe.cmd = IWEVCUSTOM;
193 p = custom;
194
195 iwe.u.data.length = p - custom;
196 if (iwe.u.data.length)
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200197 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200198#if (WIRELESS_EXT < 18)
199 if (ieee->wpa_enabled && network->wpa_ie_len){
200 char buf[MAX_WPA_IE_LEN * 2 + 30];
201 // printk("WPA IE\n");
202 u8 *p = buf;
203 p += sprintf(p, "wpa_ie=");
204 for (i = 0; i < network->wpa_ie_len; i++) {
205 p += sprintf(p, "%02x", network->wpa_ie[i]);
206 }
207
208 memset(&iwe, 0, sizeof(iwe));
209 iwe.cmd = IWEVCUSTOM;
210 iwe.u.data.length = strlen(buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200211 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200212 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200213
214 if (ieee->wpa_enabled && network->rsn_ie_len){
215 char buf[MAX_WPA_IE_LEN * 2 + 30];
216
217 u8 *p = buf;
218 p += sprintf(p, "rsn_ie=");
219 for (i = 0; i < network->rsn_ie_len; i++) {
220 p += sprintf(p, "%02x", network->rsn_ie[i]);
221 }
222
223 memset(&iwe, 0, sizeof(iwe));
224 iwe.cmd = IWEVCUSTOM;
225 iwe.u.data.length = strlen(buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200226 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200227 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200228#else
229 memset(&iwe, 0, sizeof(iwe));
230 if (network->wpa_ie_len)
231 {
232 char buf[MAX_WPA_IE_LEN];
233 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
234 iwe.cmd = IWEVGENIE;
235 iwe.u.data.length = network->wpa_ie_len;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200236 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200237 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200238 memset(&iwe, 0, sizeof(iwe));
239 if (network->rsn_ie_len)
240 {
241 char buf[MAX_WPA_IE_LEN];
242 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
243 iwe.cmd = IWEVGENIE;
244 iwe.u.data.length = network->rsn_ie_len;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200245 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200246 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200247#endif
248
249
250 /* Add EXTRA: Age to display seconds since last beacon/probe response
251 * for given network. */
252 iwe.cmd = IWEVCUSTOM;
253 p = custom;
254 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
255 " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100));
256 iwe.u.data.length = p - custom;
257 if (iwe.u.data.length)
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200258 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200259
260 return start;
261}
262
263int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
264 struct iw_request_info *info,
265 union iwreq_data *wrqu, char *extra)
266{
267 struct ieee80211_network *network;
268 unsigned long flags;
269
270 char *ev = extra;
271// char *stop = ev + IW_SCAN_MAX_DATA;
272 char *stop = ev + wrqu->data.length;//IW_SCAN_MAX_DATA;
273 //char *stop = ev + IW_SCAN_MAX_DATA;
274 int i = 0;
275 int err = 0;
276 IEEE80211_DEBUG_WX("Getting scan\n");
277 down(&ieee->wx_sem);
278 spin_lock_irqsave(&ieee->lock, flags);
279
280 list_for_each_entry(network, &ieee->network_list, list) {
281 i++;
282 if((stop-ev)<200)
283 {
284 err = -E2BIG;
285 break;
286 }
287 if (ieee->scan_age == 0 ||
288 time_after(network->last_scanned + ieee->scan_age, jiffies))
289 ev = rtl819x_translate_scan(ieee, ev, stop, network, info);
290 else
291 IEEE80211_DEBUG_SCAN(
292 "Not showing network '%s ("
Joe Perches0ee9f672009-12-06 11:34:52 -0800293 "%pM)' due to age (%lums).\n",
Jerry Chuang8fc85982009-11-03 07:17:11 -0200294 escape_essid(network->ssid,
295 network->ssid_len),
Joe Perches0ee9f672009-12-06 11:34:52 -0800296 network->bssid,
Jerry Chuang8fc85982009-11-03 07:17:11 -0200297 (jiffies - network->last_scanned) / (HZ / 100));
298 }
299
300 spin_unlock_irqrestore(&ieee->lock, flags);
301 up(&ieee->wx_sem);
302 wrqu->data.length = ev - extra;
303 wrqu->data.flags = 0;
304
305 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
306
307 return err;
308}
309
310int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
311 struct iw_request_info *info,
312 union iwreq_data *wrqu, char *keybuf)
313{
314 struct iw_point *erq = &(wrqu->encoding);
315 struct net_device *dev = ieee->dev;
316 struct ieee80211_security sec = {
317 .flags = 0
318 };
319 int i, key, key_provided, len;
320 struct ieee80211_crypt_data **crypt;
321
322 IEEE80211_DEBUG_WX("SET_ENCODE\n");
323
324 key = erq->flags & IW_ENCODE_INDEX;
325 if (key) {
326 if (key > WEP_KEYS)
327 return -EINVAL;
328 key--;
329 key_provided = 1;
330 } else {
331 key_provided = 0;
332 key = ieee->tx_keyidx;
333 }
334
335 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
336 "provided" : "default");
337 crypt = &ieee->crypt[key];
338
339 if (erq->flags & IW_ENCODE_DISABLED) {
340 if (key_provided && *crypt) {
341 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
342 key);
343 ieee80211_crypt_delayed_deinit(ieee, crypt);
344 } else
345 IEEE80211_DEBUG_WX("Disabling encryption.\n");
346
347 /* Check all the keys to see if any are still configured,
348 * and if no key index was provided, de-init them all */
349 for (i = 0; i < WEP_KEYS; i++) {
350 if (ieee->crypt[i] != NULL) {
351 if (key_provided)
352 break;
353 ieee80211_crypt_delayed_deinit(
354 ieee, &ieee->crypt[i]);
355 }
356 }
357
358 if (i == WEP_KEYS) {
359 sec.enabled = 0;
360 sec.level = SEC_LEVEL_0;
361 sec.flags |= SEC_ENABLED | SEC_LEVEL;
362 }
363
364 goto done;
365 }
366
367
368
369 sec.enabled = 1;
370 sec.flags |= SEC_ENABLED;
371
372 if (*crypt != NULL && (*crypt)->ops != NULL &&
373 strcmp((*crypt)->ops->name, "WEP") != 0) {
374 /* changing to use WEP; deinit previously used algorithm
375 * on this key */
376 ieee80211_crypt_delayed_deinit(ieee, crypt);
377 }
378
379 if (*crypt == NULL) {
380 struct ieee80211_crypt_data *new_crypt;
381
382 /* take WEP into use */
383 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
384 GFP_KERNEL);
385 if (new_crypt == NULL)
386 return -ENOMEM;
387 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
388 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
389 if (!new_crypt->ops) {
390 request_module("ieee80211_crypt_wep");
391 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
392 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200393 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
Jerry Chuang8fc85982009-11-03 07:17:11 -0200394 new_crypt->priv = new_crypt->ops->init(key);
395
396 if (!new_crypt->ops || !new_crypt->priv) {
397 kfree(new_crypt);
398 new_crypt = NULL;
399
400 printk(KERN_WARNING "%s: could not initialize WEP: "
401 "load module ieee80211_crypt_wep\n",
402 dev->name);
403 return -EOPNOTSUPP;
404 }
405 *crypt = new_crypt;
406 }
407
408 /* If a new key was provided, set it up */
409 if (erq->length > 0) {
410 len = erq->length <= 5 ? 5 : 13;
411 memcpy(sec.keys[key], keybuf, erq->length);
412 if (len > erq->length)
413 memset(sec.keys[key] + erq->length, 0,
414 len - erq->length);
415 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
416 key, escape_essid(sec.keys[key], len),
417 erq->length, len);
418 sec.key_sizes[key] = len;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200419 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
Jerry Chuang8fc85982009-11-03 07:17:11 -0200420 (*crypt)->priv);
421 sec.flags |= (1 << key);
422 /* This ensures a key will be activated if no key is
423 * explicitely set */
424 if (key == sec.active_key)
425 sec.flags |= SEC_ACTIVE_KEY;
426 ieee->tx_keyidx = key;
427
428 } else {
429 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
430 NULL, (*crypt)->priv);
431 if (len == 0) {
432 /* Set a default key of all 0 */
433 printk("Setting key %d to all zero.\n",
434 key);
435
436 IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
437 key);
438 memset(sec.keys[key], 0, 13);
439 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
440 (*crypt)->priv);
441 sec.key_sizes[key] = 13;
442 sec.flags |= (1 << key);
443 }
444
445 /* No key data - just set the default TX key index */
446 if (key_provided) {
447 IEEE80211_DEBUG_WX(
448 "Setting key %d to default Tx key.\n", key);
449 ieee->tx_keyidx = key;
450 sec.active_key = key;
451 sec.flags |= SEC_ACTIVE_KEY;
452 }
453 }
454
455 done:
456 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
457 ieee->auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
458 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
459 sec.flags |= SEC_AUTH_MODE;
460 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
461 "OPEN" : "SHARED KEY");
462
463 /* For now we just support WEP, so only set that security level...
464 * TODO: When WPA is added this is one place that needs to change */
465 sec.flags |= SEC_LEVEL;
466 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
467
468 if (ieee->set_security)
469 ieee->set_security(dev, &sec);
470
471 /* Do not reset port if card is in Managed mode since resetting will
472 * generate new IEEE 802.11 authentication which may end up in looping
473 * with IEEE 802.1X. If your hardware requires a reset after WEP
474 * configuration (for example... Prism2), implement the reset_port in
475 * the callbacks structures used to initialize the 802.11 stack. */
476 if (ieee->reset_on_keychange &&
477 ieee->iw_mode != IW_MODE_INFRA &&
478 ieee->reset_port && ieee->reset_port(dev)) {
479 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
480 return -EINVAL;
481 }
482 return 0;
483}
484
485int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
486 struct iw_request_info *info,
487 union iwreq_data *wrqu, char *keybuf)
488{
489 struct iw_point *erq = &(wrqu->encoding);
490 int len, key;
491 struct ieee80211_crypt_data *crypt;
492
493 IEEE80211_DEBUG_WX("GET_ENCODE\n");
494
495 if(ieee->iw_mode == IW_MODE_MONITOR)
496 return -1;
497
498 key = erq->flags & IW_ENCODE_INDEX;
499 if (key) {
500 if (key > WEP_KEYS)
501 return -EINVAL;
502 key--;
503 } else
504 key = ieee->tx_keyidx;
505
506 crypt = ieee->crypt[key];
507 erq->flags = key + 1;
508
509 if (crypt == NULL || crypt->ops == NULL) {
510 erq->length = 0;
511 erq->flags |= IW_ENCODE_DISABLED;
512 return 0;
513 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200514 len = crypt->ops->get_key(keybuf, SCM_KEY_LEN, NULL, crypt->priv);
515 erq->length = (len >= 0 ? len : 0);
516
517 erq->flags |= IW_ENCODE_ENABLED;
518
519 if (ieee->open_wep)
520 erq->flags |= IW_ENCODE_OPEN;
521 else
522 erq->flags |= IW_ENCODE_RESTRICTED;
523
524 return 0;
525}
526#if (WIRELESS_EXT >= 18)
527int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee,
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200528 struct iw_request_info *info,
529 union iwreq_data *wrqu, char *extra)
Jerry Chuang8fc85982009-11-03 07:17:11 -0200530{
531 int ret = 0;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200532 struct net_device *dev = ieee->dev;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200533 struct iw_point *encoding = &wrqu->encoding;
534 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
535 int i, idx;
536 int group_key = 0;
537 const char *alg, *module;
538 struct ieee80211_crypto_ops *ops;
539 struct ieee80211_crypt_data **crypt;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200540
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200541 struct ieee80211_security sec = {
542 .flags = 0,
543 };
Jerry Chuang8fc85982009-11-03 07:17:11 -0200544 //printk("======>encoding flag:%x,ext flag:%x, ext alg:%d\n", encoding->flags,ext->ext_flags, ext->alg);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200545 idx = encoding->flags & IW_ENCODE_INDEX;
546 if (idx) {
547 if (idx < 1 || idx > WEP_KEYS)
548 return -EINVAL;
549 idx--;
550 } else
551 idx = ieee->tx_keyidx;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200552
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200553 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
Jerry Chuang8fc85982009-11-03 07:17:11 -0200554
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200555 crypt = &ieee->crypt[idx];
Jerry Chuang8fc85982009-11-03 07:17:11 -0200556
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200557 group_key = 1;
558 } else {
559 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
Jerry Chuang8fc85982009-11-03 07:17:11 -0200560 //printk("not group key, flags:%x, ext->alg:%d\n", ext->ext_flags, ext->alg);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200561 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
562 return -EINVAL;
563 if (ieee->iw_mode == IW_MODE_INFRA)
Jerry Chuang8fc85982009-11-03 07:17:11 -0200564
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200565 crypt = &ieee->crypt[idx];
Jerry Chuang8fc85982009-11-03 07:17:11 -0200566
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200567 else
568 return -EINVAL;
569 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200570
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200571 sec.flags |= SEC_ENABLED;// | SEC_ENCRYPT;
572 if ((encoding->flags & IW_ENCODE_DISABLED) ||
573 ext->alg == IW_ENCODE_ALG_NONE) {
574 if (*crypt)
575 ieee80211_crypt_delayed_deinit(ieee, crypt);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200576
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200577 for (i = 0; i < WEP_KEYS; i++)
Jerry Chuang8fc85982009-11-03 07:17:11 -0200578
579 if (ieee->crypt[i] != NULL)
580
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200581 break;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200582
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200583 if (i == WEP_KEYS) {
584 sec.enabled = 0;
585 // sec.encrypt = 0;
586 sec.level = SEC_LEVEL_0;
587 sec.flags |= SEC_LEVEL;
588 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200589 //printk("disabled: flag:%x\n", encoding->flags);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200590 goto done;
591 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200592
593 sec.enabled = 1;
594 // sec.encrypt = 1;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200595 switch (ext->alg) {
596 case IW_ENCODE_ALG_WEP:
597 alg = "WEP";
598 module = "ieee80211_crypt_wep";
599 break;
600 case IW_ENCODE_ALG_TKIP:
601 alg = "TKIP";
602 module = "ieee80211_crypt_tkip";
603 break;
604 case IW_ENCODE_ALG_CCMP:
605 alg = "CCMP";
606 module = "ieee80211_crypt_ccmp";
607 break;
608 default:
609 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
610 dev->name, ext->alg);
611 ret = -EINVAL;
612 goto done;
613 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200614 printk("alg name:%s\n",alg);
615
616 ops = ieee80211_get_crypto_ops(alg);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200617 if (ops == NULL) {
618 request_module(module);
619 ops = ieee80211_get_crypto_ops(alg);
620 }
621 if (ops == NULL) {
622 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
623 dev->name, ext->alg);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200624 printk("========>unknown crypto alg %d\n", ext->alg);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200625 ret = -EINVAL;
626 goto done;
627 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200628
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200629 if (*crypt == NULL || (*crypt)->ops != ops) {
630 struct ieee80211_crypt_data *new_crypt;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200631
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200632 ieee80211_crypt_delayed_deinit(ieee, crypt);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200633
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200634 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200635 if (new_crypt == NULL) {
636 ret = -ENOMEM;
637 goto done;
638 }
639 new_crypt->ops = ops;
640 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
641 new_crypt->priv = new_crypt->ops->init(idx);
642 if (new_crypt->priv == NULL) {
643 kfree(new_crypt);
644 ret = -EINVAL;
645 goto done;
646 }
647 *crypt = new_crypt;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200648
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200649 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200650
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200651 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
652 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
653 (*crypt)->priv) < 0) {
654 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200655 printk("key setting failed\n");
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200656 ret = -EINVAL;
657 goto done;
658 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200659 //skip_host_crypt:
660 //printk("skip_host_crypt:ext_flags:%x\n", ext->ext_flags);
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200661 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
662 ieee->tx_keyidx = idx;
663 sec.active_key = idx;
664 sec.flags |= SEC_ACTIVE_KEY;
665 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200666
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200667 if (ext->alg != IW_ENCODE_ALG_NONE) {
668 //memcpy(sec.keys[idx], ext->key, ext->key_len);
669 sec.key_sizes[idx] = ext->key_len;
670 sec.flags |= (1 << idx);
671 if (ext->alg == IW_ENCODE_ALG_WEP) {
672 // sec.encode_alg[idx] = SEC_ALG_WEP;
673 sec.flags |= SEC_LEVEL;
674 sec.level = SEC_LEVEL_1;
675 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
676 // sec.encode_alg[idx] = SEC_ALG_TKIP;
677 sec.flags |= SEC_LEVEL;
678 sec.level = SEC_LEVEL_2;
679 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
680 // sec.encode_alg[idx] = SEC_ALG_CCMP;
681 sec.flags |= SEC_LEVEL;
682 sec.level = SEC_LEVEL_3;
683 }
684 /* Don't set sec level for group keys. */
685 if (group_key)
686 sec.flags &= ~SEC_LEVEL;
687 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200688done:
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200689 if (ieee->set_security)
690 ieee->set_security(ieee->dev, &sec);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200691
692 if (ieee->reset_on_keychange &&
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200693 ieee->iw_mode != IW_MODE_INFRA &&
694 ieee->reset_port && ieee->reset_port(dev)) {
695 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
696 return -EINVAL;
697 }
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200698 return ret;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200699}
700
701int ieee80211_wx_get_encode_ext(struct ieee80211_device *ieee,
702 struct iw_request_info *info,
703 union iwreq_data *wrqu, char *extra)
704{
705 struct iw_point *encoding = &wrqu->encoding;
706 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
707 struct ieee80211_crypt_data *crypt;
708 int idx, max_key_len;
709
710 max_key_len = encoding->length - sizeof(*ext);
711 if (max_key_len < 0)
712 return -EINVAL;
713
714 idx = encoding->flags & IW_ENCODE_INDEX;
715 if (idx) {
716 if (idx < 1 || idx > WEP_KEYS)
717 return -EINVAL;
718 idx--;
719 } else
720 idx = ieee->tx_keyidx;
721
Roel Kluin716323c2009-12-23 02:36:43 +0100722 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Jerry Chuang8fc85982009-11-03 07:17:11 -0200723 ext->alg != IW_ENCODE_ALG_WEP)
724 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
725 return -EINVAL;
726
727 crypt = ieee->crypt[idx];
728 encoding->flags = idx + 1;
729 memset(ext, 0, sizeof(*ext));
730
731 if (crypt == NULL || crypt->ops == NULL ) {
732 ext->alg = IW_ENCODE_ALG_NONE;
733 ext->key_len = 0;
734 encoding->flags |= IW_ENCODE_DISABLED;
735 } else {
736 if (strcmp(crypt->ops->name, "WEP") == 0 )
737 ext->alg = IW_ENCODE_ALG_WEP;
738 else if (strcmp(crypt->ops->name, "TKIP"))
739 ext->alg = IW_ENCODE_ALG_TKIP;
740 else if (strcmp(crypt->ops->name, "CCMP"))
741 ext->alg = IW_ENCODE_ALG_CCMP;
742 else
743 return -EINVAL;
744 ext->key_len = crypt->ops->get_key(ext->key, SCM_KEY_LEN, NULL, crypt->priv);
745 encoding->flags |= IW_ENCODE_ENABLED;
746 if (ext->key_len &&
747 (ext->alg == IW_ENCODE_ALG_TKIP ||
748 ext->alg == IW_ENCODE_ALG_CCMP))
749 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
750
751 }
752
753 return 0;
754}
755
756int ieee80211_wx_set_mlme(struct ieee80211_device *ieee,
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200757 struct iw_request_info *info,
758 union iwreq_data *wrqu, char *extra)
Jerry Chuang8fc85982009-11-03 07:17:11 -0200759{
Jerry Chuang8fc85982009-11-03 07:17:11 -0200760 struct iw_mlme *mlme = (struct iw_mlme *) extra;
761 switch (mlme->cmd) {
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200762 case IW_MLME_DEAUTH:
Jerry Chuang8fc85982009-11-03 07:17:11 -0200763 case IW_MLME_DISASSOC:
764 ieee80211_disassociate(ieee);
765 break;
766 default:
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200767 return -EOPNOTSUPP;
768 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200769 return 0;
770}
771
772int ieee80211_wx_set_auth(struct ieee80211_device *ieee,
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200773 struct iw_request_info *info,
774 struct iw_param *data, char *extra)
Jerry Chuang8fc85982009-11-03 07:17:11 -0200775{
Jerry Chuang8fc85982009-11-03 07:17:11 -0200776 switch (data->flags & IW_AUTH_INDEX) {
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200777 case IW_AUTH_WPA_VERSION:
Jerry Chuang8fc85982009-11-03 07:17:11 -0200778 /*need to support wpa2 here*/
779 //printk("wpa version:%x\n", data->value);
780 break;
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200781 case IW_AUTH_CIPHER_PAIRWISE:
782 case IW_AUTH_CIPHER_GROUP:
783 case IW_AUTH_KEY_MGMT:
784 /*
Jerry Chuang8fc85982009-11-03 07:17:11 -0200785 * * Host AP driver does not use these parameters and allows
786 * * wpa_supplicant to control them internally.
787 * */
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200788 break;
789 case IW_AUTH_TKIP_COUNTERMEASURES:
790 ieee->tkip_countermeasures = data->value;
791 break;
792 case IW_AUTH_DROP_UNENCRYPTED:
793 ieee->drop_unencrypted = data->value;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200794 break;
795
796 case IW_AUTH_80211_AUTH_ALG:
797 //printk("======>%s():data->value is %d\n",__FUNCTION__,data->value);
798 // ieee->open_wep = (data->value&IW_AUTH_ALG_OPEN_SYSTEM)?1:0;
799 if(data->value & IW_AUTH_ALG_SHARED_KEY){
800 ieee->open_wep = 0;
801 ieee->auth_mode = 1;
802 }
803 else if(data->value & IW_AUTH_ALG_OPEN_SYSTEM){
804 ieee->open_wep = 1;
805 ieee->auth_mode = 0;
806 }
807 else if(data->value & IW_AUTH_ALG_LEAP){
808 ieee->open_wep = 1;
809 ieee->auth_mode = 2;
810 //printk("hahahaa:LEAP\n");
811 }
812 else
813 return -EINVAL;
814 //printk("open_wep:%d\n", ieee->open_wep);
815 break;
816
Jerry Chuang8fc85982009-11-03 07:17:11 -0200817 case IW_AUTH_WPA_ENABLED:
818 ieee->wpa_enabled = (data->value)?1:0;
819 //printk("enalbe wpa:%d\n", ieee->wpa_enabled);
820 break;
821
Jerry Chuang8fc85982009-11-03 07:17:11 -0200822 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200823 ieee->ieee802_1x = data->value;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200824 break;
825 case IW_AUTH_PRIVACY_INVOKED:
826 ieee->privacy_invoked = data->value;
827 break;
828 default:
Mauro Carvalho Chehabe4063222009-11-03 07:42:46 -0200829 return -EOPNOTSUPP;
Jerry Chuang8fc85982009-11-03 07:17:11 -0200830 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200831 return 0;
832}
833#endif
Jerry Chuang8fc85982009-11-03 07:17:11 -0200834int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len)
835{
Jerry Chuang8fc85982009-11-03 07:17:11 -0200836 u8 *buf;
837
838 if (len>MAX_WPA_IE_LEN || (len && ie == NULL))
839 {
840 // printk("return error out, len:%d\n", len);
841 return -EINVAL;
842 }
843
844
845 if (len)
846 {
847 if (len != ie[1]+2)
848 {
Randy Dunlap3d8affc0012009-11-24 12:13:55 -0800849 printk("len:%zu, ie:%d\n", len, ie[1]);
Jerry Chuang8fc85982009-11-03 07:17:11 -0200850 return -EINVAL;
851 }
852 buf = kmalloc(len, GFP_KERNEL);
853 if (buf == NULL)
854 return -ENOMEM;
855 memcpy(buf, ie, len);
856 kfree(ieee->wpa_ie);
857 ieee->wpa_ie = buf;
858 ieee->wpa_ie_len = len;
859 }
860 else{
861 if (ieee->wpa_ie)
862 kfree(ieee->wpa_ie);
863 ieee->wpa_ie = NULL;
864 ieee->wpa_ie_len = 0;
865 }
Jerry Chuang8fc85982009-11-03 07:17:11 -0200866 return 0;
867
868}
Jerry Chuang8fc85982009-11-03 07:17:11 -0200869
Jerry Chuang8fc85982009-11-03 07:17:11 -0200870EXPORT_SYMBOL(ieee80211_wx_set_gen_ie);
871#if (WIRELESS_EXT >= 18)
872EXPORT_SYMBOL(ieee80211_wx_set_mlme);
873EXPORT_SYMBOL(ieee80211_wx_set_auth);
874EXPORT_SYMBOL(ieee80211_wx_set_encode_ext);
875EXPORT_SYMBOL(ieee80211_wx_get_encode_ext);
876#endif
877EXPORT_SYMBOL(ieee80211_wx_get_scan);
878EXPORT_SYMBOL(ieee80211_wx_set_encode);
879EXPORT_SYMBOL(ieee80211_wx_get_encode);