blob: d7bd6cf00a81611f04f32cb96cde0cac1c53586d [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
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
Jouni Malinen85d32e72007-03-24 17:15:30 -07008 <j@w1.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
Jeff Garzikb4538722005-05-12 22:48:20 -040010
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:
Reinette Chatrec1eb2c82009-08-21 13:34:26 -070028 Intel Linux Wireless <ilw@linux.intel.com>
Jeff Garzikb4538722005-05-12 22:48:20 -040029 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/kmod.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090034#include <linux/slab.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040035#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050036#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040037
John W. Linville9387b7c2008-09-30 20:59:05 -040038#include <net/lib80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040039#include <linux/wireless.h>
40
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040041#include "libipw.h"
Dan Williamsf3734ee2009-02-12 12:32:55 -050042
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040043static const char *libipw_modes[] = {
Jeff Garzikb4538722005-05-12 22:48:20 -040044 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
45};
46
Dan Williamsc3d72b92009-02-11 13:26:06 -050047static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
48{
49 unsigned long end = jiffies;
50
51 if (end >= start)
52 return jiffies_to_msecs(end - start);
53
54 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
55}
56
Jeff Garzikb4538722005-05-12 22:48:20 -040057#define MAX_CUSTOM_LEN 64
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040058static char *libipw_translate_scan(struct libipw_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070059 char *start, char *stop,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040060 struct libipw_network *network,
David S. Millerccc58052008-06-16 18:50:49 -070061 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040062{
63 char custom[MAX_CUSTOM_LEN];
64 char *p;
65 struct iw_event iwe;
66 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080067 char *current_val; /* For rates */
68 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040069
70 /* First entry *MUST* be the AP MAC address */
71 iwe.cmd = SIOCGIWAP;
72 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
73 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070074 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040075
76 /* Remaining entries will be displayed in the order we provide them */
77
78 /* Add the ESSID */
79 iwe.cmd = SIOCGIWESSID;
80 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040081 iwe.u.data.length = min(network->ssid_len, (u8) 32);
82 start = iwe_stream_add_point(info, start, stop,
83 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040084
85 /* Add the protocol name */
86 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040087 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040088 libipw_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070089 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040090
Jeff Garzik0edd5b42005-09-07 00:48:31 -040091 /* Add mode */
92 iwe.cmd = SIOCGIWMODE;
93 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040094 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040095 iwe.u.mode = IW_MODE_MASTER;
96 else
97 iwe.u.mode = IW_MODE_ADHOC;
98
David S. Millerccc58052008-06-16 18:50:49 -070099 start = iwe_stream_add_event(info, start, stop,
100 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400101 }
102
Larry Finger93afe3d2007-04-21 17:56:43 -0500103 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500104 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -0400105 iwe.cmd = SIOCGIWFREQ;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400106 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
Larry Finger93afe3d2007-04-21 17:56:43 -0500107 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500108 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700109 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -0500110
Jeff Garzikb4538722005-05-12 22:48:20 -0400111 /* Add encryption capability */
112 iwe.cmd = SIOCGIWENCODE;
113 if (network->capability & WLAN_CAPABILITY_PRIVACY)
114 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
115 else
116 iwe.u.data.flags = IW_ENCODE_DISABLED;
117 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700118 start = iwe_stream_add_point(info, start, stop,
119 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400120
121 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800122 /* Rate : stuffing multiple values in a single event require a bit
123 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700124 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800125 iwe.cmd = SIOCGIWRATE;
126 /* Those two flags are ignored... */
127 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
128
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400129 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400130 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;
Zhu Yi09593042006-04-13 17:17:26 +0800136 /* Bit rate given in 500 kb/s units (+ 0x80) */
137 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
138 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700139 current_val = iwe_stream_add_value(info, start, current_val,
140 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400141 }
142 for (; j < network->rates_ex_len; j++) {
143 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800144 /* Bit rate given in 500 kb/s units (+ 0x80) */
145 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
146 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700147 current_val = iwe_stream_add_value(info, start, current_val,
148 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400149 }
Zhu Yi09593042006-04-13 17:17:26 +0800150 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700151 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800152 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400153
154 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400155 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500156 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
157 IW_QUAL_NOISE_UPDATED;
158
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400159 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
James Ketrenosb1b508e2005-09-13 17:27:19 -0500160 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
161 IW_QUAL_LEVEL_INVALID;
162 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500163 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200164 if (ieee->perfect_rssi == ieee->worst_rssi)
165 iwe.u.qual.qual = 100;
166 else
167 iwe.u.qual.qual =
168 (100 *
169 (ieee->perfect_rssi - ieee->worst_rssi) *
170 (ieee->perfect_rssi - ieee->worst_rssi) -
171 (ieee->perfect_rssi - network->stats.rssi) *
172 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500173 62 * (ieee->perfect_rssi -
174 network->stats.rssi))) /
175 ((ieee->perfect_rssi -
176 ieee->worst_rssi) * (ieee->perfect_rssi -
177 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500178 if (iwe.u.qual.qual > 100)
179 iwe.u.qual.qual = 100;
180 else if (iwe.u.qual.qual < 1)
181 iwe.u.qual.qual = 0;
182 }
183
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400184 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400185 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500186 iwe.u.qual.noise = 0;
187 } else {
188 iwe.u.qual.noise = network->stats.noise;
189 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400190
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400191 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800192 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
193 iwe.u.qual.level = 0;
194 } else {
195 iwe.u.qual.level = network->stats.signal;
196 }
197
David S. Millerccc58052008-06-16 18:50:49 -0700198 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400199
200 iwe.cmd = IWEVCUSTOM;
201 p = custom;
202
203 iwe.u.data.length = p - custom;
204 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700205 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400206
Zhu Yi47168082006-02-13 13:37:03 +0800207 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500208 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800209 char buf[MAX_WPA_IE_LEN];
210 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
211 iwe.cmd = IWEVGENIE;
212 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700213 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400214 }
215
Zhu Yi47168082006-02-13 13:37:03 +0800216 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500217 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800218 char buf[MAX_WPA_IE_LEN];
219 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
220 iwe.cmd = IWEVGENIE;
221 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700222 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400223 }
224
225 /* Add EXTRA: Age to display seconds since last beacon/probe response
226 * for given network. */
227 iwe.cmd = IWEVCUSTOM;
228 p = custom;
229 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
Dan Williamsc3d72b92009-02-11 13:26:06 -0500230 " Last beacon: %ums ago",
231 elapsed_jiffies_msecs(network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400232 iwe.u.data.length = p - custom;
233 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700234 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400235
Zhu Yi7bd64362006-01-19 16:21:54 +0800236 /* Add spectrum management information */
237 iwe.cmd = -1;
238 p = custom;
239 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
240
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400241 if (libipw_get_channel_flags(ieee, network->channel) &
242 LIBIPW_CH_INVALID) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800243 iwe.cmd = IWEVCUSTOM;
244 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
245 }
246
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400247 if (libipw_get_channel_flags(ieee, network->channel) &
248 LIBIPW_CH_RADAR_DETECT) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800249 iwe.cmd = IWEVCUSTOM;
250 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
251 }
252
253 if (iwe.cmd == IWEVCUSTOM) {
254 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700255 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800256 }
257
Jeff Garzikb4538722005-05-12 22:48:20 -0400258 return start;
259}
260
Zhu Yi55cd94a2006-01-19 16:20:59 +0800261#define SCAN_ITEM_SIZE 128
262
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400263int libipw_wx_get_scan(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400264 struct iw_request_info *info,
265 union iwreq_data *wrqu, char *extra)
266{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400267 struct libipw_network *network;
Jeff Garzikb4538722005-05-12 22:48:20 -0400268 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800269 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400270
271 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800272 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400273 int i = 0;
John W. Linville9387b7c2008-09-30 20:59:05 -0400274 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400275
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400276 LIBIPW_DEBUG_WX("Getting scan\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400277
278 spin_lock_irqsave(&ieee->lock, flags);
279
280 list_for_each_entry(network, &ieee->network_list, list) {
281 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800282 if (stop - ev < SCAN_ITEM_SIZE) {
283 err = -E2BIG;
284 break;
285 }
286
Jeff Garzikb4538722005-05-12 22:48:20 -0400287 if (ieee->scan_age == 0 ||
288 time_after(network->last_scanned + ieee->scan_age, jiffies))
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400289 ev = libipw_translate_scan(ieee, ev, stop, network,
David S. Millerccc58052008-06-16 18:50:49 -0700290 info);
Dan Williamsc3d72b92009-02-11 13:26:06 -0500291 else {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400292 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
Dan Williamsc3d72b92009-02-11 13:26:06 -0500293 "%pM)' due to age (%ums).\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400294 print_ssid(ssid, network->ssid,
John W. Linville7e272fc2008-09-24 18:13:14 -0400295 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700296 network->bssid,
Dan Williamsc3d72b92009-02-11 13:26:06 -0500297 elapsed_jiffies_msecs(
298 network->last_scanned));
299 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400300 }
301
302 spin_unlock_irqrestore(&ieee->lock, flags);
303
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400304 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400305 wrqu->data.flags = 0;
306
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400307 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
Jeff Garzikb4538722005-05-12 22:48:20 -0400308
Zhu Yi55cd94a2006-01-19 16:20:59 +0800309 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400310}
311
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400312int libipw_wx_set_encode(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400313 struct iw_request_info *info,
314 union iwreq_data *wrqu, char *keybuf)
315{
316 struct iw_point *erq = &(wrqu->encoding);
317 struct net_device *dev = ieee->dev;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400318 struct libipw_security sec = {
Jeff Garzikb4538722005-05-12 22:48:20 -0400319 .flags = 0
320 };
321 int i, key, key_provided, len;
John W. Linville274bfb82008-10-29 11:35:05 -0400322 struct lib80211_crypt_data **crypt;
John W. Linville3289a832010-07-22 16:31:48 -0400323 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
John W. Linville9387b7c2008-09-30 20:59:05 -0400324 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400325
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400326 LIBIPW_DEBUG_WX("SET_ENCODE\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400327
328 key = erq->flags & IW_ENCODE_INDEX;
329 if (key) {
330 if (key > WEP_KEYS)
331 return -EINVAL;
332 key--;
333 key_provided = 1;
334 } else {
335 key_provided = 0;
John W. Linville274bfb82008-10-29 11:35:05 -0400336 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400337 }
338
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400339 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
Jeff Garzikb4538722005-05-12 22:48:20 -0400340 "provided" : "default");
341
John W. Linville274bfb82008-10-29 11:35:05 -0400342 crypt = &ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400343
344 if (erq->flags & IW_ENCODE_DISABLED) {
345 if (key_provided && *crypt) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400346 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
Jeff Garzikb4538722005-05-12 22:48:20 -0400347 key);
John W. Linville274bfb82008-10-29 11:35:05 -0400348 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400349 } else
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400350 LIBIPW_DEBUG_WX("Disabling encryption.\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400351
352 /* Check all the keys to see if any are still configured,
353 * and if no key index was provided, de-init them all */
354 for (i = 0; i < WEP_KEYS; i++) {
John W. Linville274bfb82008-10-29 11:35:05 -0400355 if (ieee->crypt_info.crypt[i] != NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400356 if (key_provided)
357 break;
John W. Linville274bfb82008-10-29 11:35:05 -0400358 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
359 &ieee->crypt_info.crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400360 }
361 }
362
363 if (i == WEP_KEYS) {
364 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500365 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400366 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500367 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400368 }
369
370 goto done;
371 }
372
Jeff Garzikb4538722005-05-12 22:48:20 -0400373 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500374 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500375 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400376
377 if (*crypt != NULL && (*crypt)->ops != NULL &&
378 strcmp((*crypt)->ops->name, "WEP") != 0) {
379 /* changing to use WEP; deinit previously used algorithm
380 * on this key */
John W. Linville274bfb82008-10-29 11:35:05 -0400381 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400382 }
383
James Ketrenosf1bf6632005-09-21 11:53:54 -0500384 if (*crypt == NULL && host_crypto) {
John W. Linville274bfb82008-10-29 11:35:05 -0400385 struct lib80211_crypt_data *new_crypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400386
387 /* take WEP into use */
John W. Linville274bfb82008-10-29 11:35:05 -0400388 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400389 GFP_KERNEL);
390 if (new_crypt == NULL)
391 return -ENOMEM;
John W. Linville274bfb82008-10-29 11:35:05 -0400392 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400393 if (!new_crypt->ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400394 request_module("lib80211_crypt_wep");
395 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400396 }
397
398 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000399 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400400
401 if (!new_crypt->ops || !new_crypt->priv) {
402 kfree(new_crypt);
403 new_crypt = NULL;
404
405 printk(KERN_WARNING "%s: could not initialize WEP: "
John W. Linville274bfb82008-10-29 11:35:05 -0400406 "load module lib80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400407 return -EOPNOTSUPP;
408 }
409 *crypt = new_crypt;
410 }
411
412 /* If a new key was provided, set it up */
413 if (erq->length > 0) {
414 len = erq->length <= 5 ? 5 : 13;
415 memcpy(sec.keys[key], keybuf, erq->length);
416 if (len > erq->length)
417 memset(sec.keys[key] + erq->length, 0,
418 len - erq->length);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400419 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400420 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400421 erq->length, len);
422 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500423 if (*crypt)
424 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
425 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400426 sec.flags |= (1 << key);
427 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200428 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400429 if (key == sec.active_key)
430 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400431
James Ketrenosf1bf6632005-09-21 11:53:54 -0500432 } else {
433 if (host_crypto) {
434 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
435 NULL, (*crypt)->priv);
436 if (len == 0) {
437 /* Set a default key of all 0 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400438 LIBIPW_DEBUG_WX("Setting key %d to all "
James Ketrenosf1bf6632005-09-21 11:53:54 -0500439 "zero.\n", key);
440 memset(sec.keys[key], 0, 13);
441 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
442 (*crypt)->priv);
443 sec.key_sizes[key] = 13;
444 sec.flags |= (1 << key);
445 }
446 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400447 /* No key data - just set the default TX key index */
448 if (key_provided) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400449 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
James Ketrenosf1bf6632005-09-21 11:53:54 -0500450 "key.\n", key);
John W. Linville274bfb82008-10-29 11:35:05 -0400451 ieee->crypt_info.tx_keyidx = key;
Jeff Garzikb4538722005-05-12 22:48:20 -0400452 sec.active_key = key;
453 sec.flags |= SEC_ACTIVE_KEY;
454 }
455 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500456 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
457 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
458 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
459 WLAN_AUTH_SHARED_KEY;
460 sec.flags |= SEC_AUTH_MODE;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400461 LIBIPW_DEBUG_WX("Auth: %s\n",
James Ketrenos7dc888f2005-09-21 11:58:38 -0500462 sec.auth_mode == WLAN_AUTH_OPEN ?
463 "OPEN" : "SHARED KEY");
464 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400465
466 /* For now we just support WEP, so only set that security level...
467 * TODO: When WPA is added this is one place that needs to change */
468 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400469 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500470 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400471
James Ketrenos259bf1f2005-09-21 11:54:22 -0500472 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400473 if (ieee->set_security)
474 ieee->set_security(dev, &sec);
475
476 /* Do not reset port if card is in Managed mode since resetting will
477 * generate new IEEE 802.11 authentication which may end up in looping
478 * with IEEE 802.1X. If your hardware requires a reset after WEP
479 * configuration (for example... Prism2), implement the reset_port in
480 * the callbacks structures used to initialize the 802.11 stack. */
481 if (ieee->reset_on_keychange &&
482 ieee->iw_mode != IW_MODE_INFRA &&
483 ieee->reset_port && ieee->reset_port(dev)) {
484 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
485 return -EINVAL;
486 }
487 return 0;
488}
489
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400490int libipw_wx_get_encode(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400491 struct iw_request_info *info,
492 union iwreq_data *wrqu, char *keybuf)
493{
494 struct iw_point *erq = &(wrqu->encoding);
495 int len, key;
John W. Linville274bfb82008-10-29 11:35:05 -0400496 struct lib80211_crypt_data *crypt;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400497 struct libipw_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400498
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400499 LIBIPW_DEBUG_WX("GET_ENCODE\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400500
501 key = erq->flags & IW_ENCODE_INDEX;
502 if (key) {
503 if (key > WEP_KEYS)
504 return -EINVAL;
505 key--;
506 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400507 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400508
John W. Linville274bfb82008-10-29 11:35:05 -0400509 crypt = ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400510 erq->flags = key + 1;
511
James Ketrenosf1bf6632005-09-21 11:53:54 -0500512 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400513 erq->length = 0;
514 erq->flags |= IW_ENCODE_DISABLED;
515 return 0;
516 }
517
James Ketrenosf1bf6632005-09-21 11:53:54 -0500518 len = sec->key_sizes[key];
519 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400520
Adrian Bunk62741152006-04-27 02:33:42 -0700521 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400522 erq->flags |= IW_ENCODE_ENABLED;
523
524 if (ieee->open_wep)
525 erq->flags |= IW_ENCODE_OPEN;
526 else
527 erq->flags |= IW_ENCODE_RESTRICTED;
528
529 return 0;
530}
531
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400532int libipw_wx_set_encodeext(struct libipw_device *ieee,
James Ketrenose0d369d2005-09-21 11:54:30 -0500533 struct iw_request_info *info,
534 union iwreq_data *wrqu, char *extra)
535{
536 struct net_device *dev = ieee->dev;
537 struct iw_point *encoding = &wrqu->encoding;
538 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
539 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500540 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500541 const char *alg, *module;
John W. Linville274bfb82008-10-29 11:35:05 -0400542 struct lib80211_crypto_ops *ops;
543 struct lib80211_crypt_data **crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500544
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400545 struct libipw_security sec = {
James Ketrenose0d369d2005-09-21 11:54:30 -0500546 .flags = 0,
547 };
548
549 idx = encoding->flags & IW_ENCODE_INDEX;
550 if (idx) {
551 if (idx < 1 || idx > WEP_KEYS)
552 return -EINVAL;
553 idx--;
554 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400555 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500556
James Ketrenosccd0fda2005-09-21 11:58:32 -0500557 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400558 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500559 group_key = 1;
560 } else {
Volker Braune1892772005-10-24 10:15:36 -0500561 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
562 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500563 return -EINVAL;
564 if (ieee->iw_mode == IW_MODE_INFRA)
John W. Linville274bfb82008-10-29 11:35:05 -0400565 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenose0d369d2005-09-21 11:54:30 -0500566 else
567 return -EINVAL;
568 }
569
570 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
571 if ((encoding->flags & IW_ENCODE_DISABLED) ||
572 ext->alg == IW_ENCODE_ALG_NONE) {
573 if (*crypt)
John W. Linville274bfb82008-10-29 11:35:05 -0400574 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500575
576 for (i = 0; i < WEP_KEYS; i++)
John W. Linville274bfb82008-10-29 11:35:05 -0400577 if (ieee->crypt_info.crypt[i] != NULL)
James Ketrenose0d369d2005-09-21 11:54:30 -0500578 break;
579
580 if (i == WEP_KEYS) {
581 sec.enabled = 0;
582 sec.encrypt = 0;
583 sec.level = SEC_LEVEL_0;
584 sec.flags |= SEC_LEVEL;
585 }
586 goto done;
587 }
588
589 sec.enabled = 1;
590 sec.encrypt = 1;
591
James Ketrenosccd0fda2005-09-21 11:58:32 -0500592 if (group_key ? !ieee->host_mc_decrypt :
593 !(ieee->host_encrypt || ieee->host_decrypt ||
594 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500595 goto skip_host_crypt;
596
597 switch (ext->alg) {
598 case IW_ENCODE_ALG_WEP:
599 alg = "WEP";
John W. Linville274bfb82008-10-29 11:35:05 -0400600 module = "lib80211_crypt_wep";
James Ketrenose0d369d2005-09-21 11:54:30 -0500601 break;
602 case IW_ENCODE_ALG_TKIP:
603 alg = "TKIP";
John W. Linville274bfb82008-10-29 11:35:05 -0400604 module = "lib80211_crypt_tkip";
James Ketrenose0d369d2005-09-21 11:54:30 -0500605 break;
606 case IW_ENCODE_ALG_CCMP:
607 alg = "CCMP";
John W. Linville274bfb82008-10-29 11:35:05 -0400608 module = "lib80211_crypt_ccmp";
James Ketrenose0d369d2005-09-21 11:54:30 -0500609 break;
610 default:
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400611 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
James Ketrenose0d369d2005-09-21 11:54:30 -0500612 dev->name, ext->alg);
613 ret = -EINVAL;
614 goto done;
615 }
616
John W. Linville274bfb82008-10-29 11:35:05 -0400617 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500618 if (ops == NULL) {
619 request_module(module);
John W. Linville274bfb82008-10-29 11:35:05 -0400620 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500621 }
622 if (ops == NULL) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400623 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
James Ketrenose0d369d2005-09-21 11:54:30 -0500624 dev->name, ext->alg);
625 ret = -EINVAL;
626 goto done;
627 }
628
629 if (*crypt == NULL || (*crypt)->ops != ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400630 struct lib80211_crypt_data *new_crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500631
John W. Linville274bfb82008-10-29 11:35:05 -0400632 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500633
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700634 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500635 if (new_crypt == NULL) {
636 ret = -ENOMEM;
637 goto done;
638 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500639 new_crypt->ops = ops;
640 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000641 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500642 if (new_crypt->priv == NULL) {
643 kfree(new_crypt);
644 ret = -EINVAL;
645 goto done;
646 }
647 *crypt = new_crypt;
648 }
649
650 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
651 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
652 (*crypt)->priv) < 0) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400653 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
James Ketrenose0d369d2005-09-21 11:54:30 -0500654 ret = -EINVAL;
655 goto done;
656 }
657
658 skip_host_crypt:
659 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400660 ieee->crypt_info.tx_keyidx = idx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500661 sec.active_key = idx;
662 sec.flags |= SEC_ACTIVE_KEY;
663 }
664
665 if (ext->alg != IW_ENCODE_ALG_NONE) {
666 memcpy(sec.keys[idx], ext->key, ext->key_len);
667 sec.key_sizes[idx] = ext->key_len;
668 sec.flags |= (1 << idx);
669 if (ext->alg == IW_ENCODE_ALG_WEP) {
670 sec.encode_alg[idx] = SEC_ALG_WEP;
671 sec.flags |= SEC_LEVEL;
672 sec.level = SEC_LEVEL_1;
673 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
674 sec.encode_alg[idx] = SEC_ALG_TKIP;
675 sec.flags |= SEC_LEVEL;
676 sec.level = SEC_LEVEL_2;
677 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
678 sec.encode_alg[idx] = SEC_ALG_CCMP;
679 sec.flags |= SEC_LEVEL;
680 sec.level = SEC_LEVEL_3;
681 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500682 /* Don't set sec level for group keys. */
683 if (group_key)
684 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500685 }
686 done:
687 if (ieee->set_security)
688 ieee->set_security(ieee->dev, &sec);
689
690 /*
691 * Do not reset port if card is in Managed mode since resetting will
692 * generate new IEEE 802.11 authentication which may end up in looping
693 * with IEEE 802.1X. If your hardware requires a reset after WEP
694 * configuration (for example... Prism2), implement the reset_port in
695 * the callbacks structures used to initialize the 802.11 stack.
696 */
697 if (ieee->reset_on_keychange &&
698 ieee->iw_mode != IW_MODE_INFRA &&
699 ieee->reset_port && ieee->reset_port(dev)) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400700 LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
James Ketrenose0d369d2005-09-21 11:54:30 -0500701 return -EINVAL;
702 }
703
704 return ret;
705}
706
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400707int libipw_wx_get_encodeext(struct libipw_device *ieee,
James Ketrenose0d369d2005-09-21 11:54:30 -0500708 struct iw_request_info *info,
709 union iwreq_data *wrqu, char *extra)
710{
711 struct iw_point *encoding = &wrqu->encoding;
712 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400713 struct libipw_security *sec = &ieee->sec;
James Ketrenose0d369d2005-09-21 11:54:30 -0500714 int idx, max_key_len;
715
716 max_key_len = encoding->length - sizeof(*ext);
717 if (max_key_len < 0)
718 return -EINVAL;
719
720 idx = encoding->flags & IW_ENCODE_INDEX;
721 if (idx) {
722 if (idx < 1 || idx > WEP_KEYS)
723 return -EINVAL;
724 idx--;
725 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400726 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500727
Roel Kluinf59d9782007-10-26 21:51:26 +0200728 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500729 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500730 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
731 return -EINVAL;
732
733 encoding->flags = idx + 1;
734 memset(ext, 0, sizeof(*ext));
735
736 if (!sec->enabled) {
737 ext->alg = IW_ENCODE_ALG_NONE;
738 ext->key_len = 0;
739 encoding->flags |= IW_ENCODE_DISABLED;
740 } else {
741 if (sec->encode_alg[idx] == SEC_ALG_WEP)
742 ext->alg = IW_ENCODE_ALG_WEP;
743 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
744 ext->alg = IW_ENCODE_ALG_TKIP;
745 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
746 ext->alg = IW_ENCODE_ALG_CCMP;
747 else
748 return -EINVAL;
749
750 ext->key_len = sec->key_sizes[idx];
751 memcpy(ext->key, sec->keys[idx], ext->key_len);
752 encoding->flags |= IW_ENCODE_ENABLED;
753 if (ext->key_len &&
754 (ext->alg == IW_ENCODE_ALG_TKIP ||
755 ext->alg == IW_ENCODE_ALG_CCMP))
756 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
757
758 }
759
760 return 0;
761}
762
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400763EXPORT_SYMBOL(libipw_wx_set_encodeext);
764EXPORT_SYMBOL(libipw_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500765
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400766EXPORT_SYMBOL(libipw_wx_get_scan);
767EXPORT_SYMBOL(libipw_wx_set_encode);
768EXPORT_SYMBOL(libipw_wx_get_encode);