blob: 973457383c113af04e81687fcfe86a1f97555be0 [file] [log] [blame]
Zhu Yibb9f8692009-05-21 21:20:45 +08001/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
5 * Samuel Ortiz <samuel.ortiz@intel.com>
6 * Zhu Yi <yi.zhu@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version
10 * 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 */
23
24#include <linux/kernel.h>
25#include <linux/netdevice.h>
26#include <linux/wireless.h>
27#include <linux/if_arp.h>
28#include <linux/etherdevice.h>
29#include <net/cfg80211.h>
30#include <net/iw_handler.h>
31
32#include "iwm.h"
33#include "umac.h"
34#include "commands.h"
35#include "debug.h"
36
37static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
38{
39 struct iwm_priv *iwm = ndev_to_iwm(dev);
40 struct iw_statistics *wstats = &iwm->wstats;
41
42 if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
43 memset(wstats, 0, sizeof(struct iw_statistics));
44 wstats->qual.updated = IW_QUAL_ALL_INVALID;
45 }
46
47 return wstats;
48}
49
50static int iwm_wext_siwfreq(struct net_device *dev,
51 struct iw_request_info *info,
52 struct iw_freq *freq, char *extra)
53{
54 struct iwm_priv *iwm = ndev_to_iwm(dev);
55
56 if (freq->flags == IW_FREQ_AUTO)
57 return 0;
58
59 /* frequency/channel can only be set in IBSS mode */
60 if (iwm->conf.mode != UMAC_MODE_IBSS)
61 return -EOPNOTSUPP;
62
63 return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
64}
65
66static int iwm_wext_giwfreq(struct net_device *dev,
67 struct iw_request_info *info,
68 struct iw_freq *freq, char *extra)
69{
70 struct iwm_priv *iwm = ndev_to_iwm(dev);
71
72 if (iwm->conf.mode == UMAC_MODE_IBSS)
73 return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
74
75 freq->e = 0;
76 freq->m = iwm->channel;
77
78 return 0;
79}
80
81static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
82 struct sockaddr *ap_addr, char *extra)
83{
84 struct iwm_priv *iwm = ndev_to_iwm(dev);
Samuel Ortiz0c5553b2009-06-15 21:59:50 +020085 int ret;
Zhu Yibb9f8692009-05-21 21:20:45 +080086
Samuel Ortiz13e0fe72009-06-15 21:59:52 +020087 IWM_DBG_WEXT(iwm, DBG, "Set BSSID: %pM\n", ap_addr->sa_data);
88
Zhu Yibb9f8692009-05-21 21:20:45 +080089 if (iwm->conf.mode == UMAC_MODE_IBSS)
90 return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
91
92 if (!test_bit(IWM_STATUS_READY, &iwm->status))
93 return -EIO;
94
95 if (is_zero_ether_addr(ap_addr->sa_data) ||
96 is_broadcast_ether_addr(ap_addr->sa_data)) {
97 IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
98 iwm->umac_profile->bssid[0]);
99 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
100 iwm->umac_profile->bss_num = 0;
101 } else {
102 IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
103 ap_addr->sa_data);
104 memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
105 ETH_ALEN);
106 iwm->umac_profile->bss_num = 1;
107 }
108
109 if (iwm->umac_profile_active) {
Samuel Ortiz0c5553b2009-06-15 21:59:50 +0200110 int i;
111
Zhu Yibb9f8692009-05-21 21:20:45 +0800112 if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
113 return 0;
114
Samuel Ortiz0c5553b2009-06-15 21:59:50 +0200115 /*
116 * If we're clearing the BSSID, and we're associated,
117 * we have to clear the keys as they're no longer valid.
118 */
119 if (is_zero_ether_addr(ap_addr->sa_data)) {
120 for (i = 0; i < IWM_NUM_KEYS; i++)
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200121 iwm->keys[i].key_len = 0;
Samuel Ortiz0c5553b2009-06-15 21:59:50 +0200122 }
123
124 ret = iwm_invalidate_mlme_profile(iwm);
125 if (ret < 0) {
126 IWM_ERR(iwm, "Couldn't invalidate profile\n");
127 return ret;
128 }
Zhu Yibb9f8692009-05-21 21:20:45 +0800129 }
130
131 if (iwm->umac_profile->ssid.ssid_len)
132 return iwm_send_mlme_profile(iwm);
133
134 return 0;
135}
136
137static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
138 struct sockaddr *ap_addr, char *extra)
139{
140 struct iwm_priv *iwm = ndev_to_iwm(dev);
141
142 switch (iwm->conf.mode) {
143 case UMAC_MODE_IBSS:
144 return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
145 case UMAC_MODE_BSS:
146 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
147 ap_addr->sa_family = ARPHRD_ETHER;
148 memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
149 } else
150 memset(&ap_addr->sa_data, 0, ETH_ALEN);
151 break;
152 default:
153 return -EOPNOTSUPP;
154 }
155
156 return 0;
157}
158
159static int iwm_wext_siwessid(struct net_device *dev,
160 struct iw_request_info *info,
161 struct iw_point *data, char *ssid)
162{
163 struct iwm_priv *iwm = ndev_to_iwm(dev);
164 size_t len = data->length;
165 int ret;
166
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200167 IWM_DBG_WEXT(iwm, DBG, "Set ESSID: >%s<\n", ssid);
168
Zhu Yibb9f8692009-05-21 21:20:45 +0800169 if (iwm->conf.mode == UMAC_MODE_IBSS)
170 return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
171
172 if (!test_bit(IWM_STATUS_READY, &iwm->status))
173 return -EIO;
174
175 if (len > 0 && ssid[len - 1] == '\0')
176 len--;
177
178 if (iwm->umac_profile_active) {
179 if (iwm->umac_profile->ssid.ssid_len == len &&
180 !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
181 return 0;
182
183 ret = iwm_invalidate_mlme_profile(iwm);
184 if (ret < 0) {
185 IWM_ERR(iwm, "Couldn't invalidate profile\n");
186 return ret;
187 }
188 }
189
190 iwm->umac_profile->ssid.ssid_len = len;
191 memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
192
193 return iwm_send_mlme_profile(iwm);
194}
195
196static int iwm_wext_giwessid(struct net_device *dev,
197 struct iw_request_info *info,
198 struct iw_point *data, char *ssid)
199{
200 struct iwm_priv *iwm = ndev_to_iwm(dev);
201
202 if (iwm->conf.mode == UMAC_MODE_IBSS)
203 return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
204
205 if (!test_bit(IWM_STATUS_READY, &iwm->status))
206 return -EIO;
207
208 data->length = iwm->umac_profile->ssid.ssid_len;
209 if (data->length) {
210 memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
211 data->flags = 1;
212 } else
213 data->flags = 0;
214
215 return 0;
216}
217
Zhu Yibb9f8692009-05-21 21:20:45 +0800218static int iwm_wext_giwrate(struct net_device *dev,
219 struct iw_request_info *info,
220 struct iw_param *rate, char *extra)
221{
222 struct iwm_priv *iwm = ndev_to_iwm(dev);
223
224 rate->value = iwm->rate * 1000000;
225
226 return 0;
227}
228
Zhu Yibb9f8692009-05-21 21:20:45 +0800229static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
230{
231 if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
232 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
233 else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
234 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
235 else
236 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
237
238 return 0;
239}
240
241static int iwm_wext_siwpower(struct net_device *dev,
242 struct iw_request_info *info,
243 struct iw_param *wrq, char *extra)
244{
245 struct iwm_priv *iwm = ndev_to_iwm(dev);
246 u32 power_index;
247
248 if (wrq->disabled) {
249 power_index = IWM_POWER_INDEX_MIN;
250 goto set;
251 } else
252 power_index = IWM_POWER_INDEX_DEFAULT;
253
254 switch (wrq->flags & IW_POWER_MODE) {
255 case IW_POWER_ON:
256 case IW_POWER_MODE:
257 case IW_POWER_ALL_R:
258 break;
259 default:
260 return -EINVAL;
261 }
262
263 set:
264 if (power_index == iwm->conf.power_index)
265 return 0;
266
267 iwm->conf.power_index = power_index;
268
269 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
270 CFG_POWER_INDEX, iwm->conf.power_index);
271}
272
273static int iwm_wext_giwpower(struct net_device *dev,
274 struct iw_request_info *info,
275 union iwreq_data *wrqu, char *extra)
276{
277 struct iwm_priv *iwm = ndev_to_iwm(dev);
278
279 wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
280
281 return 0;
282}
283
284static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
285{
286 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
287
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200288 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
289
Zhu Yibb9f8692009-05-21 21:20:45 +0800290 if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
291 *auth_type = UMAC_AUTH_TYPE_8021X;
292 else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
293 if (iwm->umac_profile->sec.flags &
294 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
295 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
296 else
297 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
298 } else {
299 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
300 return -EINVAL;
301 }
302
303 return 0;
304}
305
306static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
307{
308 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
309 &iwm->umac_profile->sec.mcast_cipher;
310
311 switch (cipher) {
312 case IW_AUTH_CIPHER_NONE:
313 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
314 break;
315 case IW_AUTH_CIPHER_WEP40:
316 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
317 break;
318 case IW_AUTH_CIPHER_TKIP:
319 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
320 break;
321 case IW_AUTH_CIPHER_CCMP:
322 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
323 break;
324 case IW_AUTH_CIPHER_WEP104:
325 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
326 break;
327 default:
328 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
329 return -ENOTSUPP;
330 }
331
332 return 0;
333}
334
335static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
336{
337 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
338
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200339 IWM_DBG_WEXT(iwm, DBG, "auth_alg: 0x%x\n", auth_alg);
340
Zhu Yibb9f8692009-05-21 21:20:45 +0800341 switch (auth_alg) {
342 case IW_AUTH_ALG_OPEN_SYSTEM:
343 *auth_type = UMAC_AUTH_TYPE_OPEN;
344 break;
345 case IW_AUTH_ALG_SHARED_KEY:
346 if (iwm->umac_profile->sec.flags &
347 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
348 if (*auth_type == UMAC_AUTH_TYPE_8021X)
349 return -EINVAL;
350 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
351 } else {
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200352 IWM_DBG_WEXT(iwm, DBG, "WEP shared key\n");
Zhu Yibb9f8692009-05-21 21:20:45 +0800353 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
354 }
355 break;
356 case IW_AUTH_ALG_LEAP:
357 default:
358 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
359 return -ENOTSUPP;
360 }
361
362 return 0;
363}
364
365static int iwm_wext_siwauth(struct net_device *dev,
366 struct iw_request_info *info,
367 struct iw_param *data, char *extra)
368{
369 struct iwm_priv *iwm = ndev_to_iwm(dev);
370 int ret;
371
372 if ((data->flags) &
373 (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
374 IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
375 /* We need to invalidate the current profile */
376 if (iwm->umac_profile_active) {
377 ret = iwm_invalidate_mlme_profile(iwm);
378 if (ret < 0) {
379 IWM_ERR(iwm, "Couldn't invalidate profile\n");
380 return ret;
381 }
382 }
383 }
384
385 switch (data->flags & IW_AUTH_INDEX) {
386 case IW_AUTH_WPA_VERSION:
387 return iwm_set_wpa_version(iwm, data->value);
388 break;
389 case IW_AUTH_CIPHER_PAIRWISE:
390 return iwm_set_cipher(iwm, data->value, 1);
391 break;
392 case IW_AUTH_CIPHER_GROUP:
393 return iwm_set_cipher(iwm, data->value, 0);
394 break;
395 case IW_AUTH_KEY_MGMT:
396 return iwm_set_key_mgt(iwm, data->value);
397 break;
398 case IW_AUTH_80211_AUTH_ALG:
399 return iwm_set_auth_alg(iwm, data->value);
400 break;
401 default:
402 return -ENOTSUPP;
403 }
404
405 return 0;
406}
407
408static int iwm_wext_giwauth(struct net_device *dev,
409 struct iw_request_info *info,
410 struct iw_param *data, char *extra)
411{
412 return 0;
413}
414
Zhu Yibb9f8692009-05-21 21:20:45 +0800415static const iw_handler iwm_handlers[] =
416{
417 (iw_handler) NULL, /* SIOCSIWCOMMIT */
418 (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
419 (iw_handler) NULL, /* SIOCSIWNWID */
420 (iw_handler) NULL, /* SIOCGIWNWID */
421 (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */
422 (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */
423 (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
424 (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
425 (iw_handler) NULL, /* SIOCSIWSENS */
426 (iw_handler) NULL, /* SIOCGIWSENS */
427 (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
428 (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
429 (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
430 (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
431 (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
432 (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
433 (iw_handler) NULL, /* SIOCSIWSPY */
434 (iw_handler) NULL, /* SIOCGIWSPY */
435 (iw_handler) NULL, /* SIOCSIWTHRSPY */
436 (iw_handler) NULL, /* SIOCGIWTHRSPY */
437 (iw_handler) iwm_wext_siwap, /* SIOCSIWAP */
438 (iw_handler) iwm_wext_giwap, /* SIOCGIWAP */
439 (iw_handler) NULL, /* SIOCSIWMLME */
440 (iw_handler) NULL, /* SIOCGIWAPLIST */
441 (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
442 (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
443 (iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */
444 (iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */
445 (iw_handler) NULL, /* SIOCSIWNICKN */
446 (iw_handler) NULL, /* SIOCGIWNICKN */
447 (iw_handler) NULL, /* -- hole -- */
448 (iw_handler) NULL, /* -- hole -- */
449 (iw_handler) NULL, /* SIOCSIWRATE */
450 (iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */
451 (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
452 (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
453 (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
454 (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
Zhu Yi257862f2009-06-15 21:59:56 +0200455 (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */
456 (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */
Zhu Yibb9f8692009-05-21 21:20:45 +0800457 (iw_handler) NULL, /* SIOCSIWRETRY */
458 (iw_handler) NULL, /* SIOCGIWRETRY */
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200459 (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */
460 (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */
Zhu Yibb9f8692009-05-21 21:20:45 +0800461 (iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */
462 (iw_handler) iwm_wext_giwpower, /* SIOCGIWPOWER */
463 (iw_handler) NULL, /* -- hole -- */
464 (iw_handler) NULL, /* -- hole -- */
465 (iw_handler) NULL, /* SIOCSIWGENIE */
466 (iw_handler) NULL, /* SIOCGIWGENIE */
467 (iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
468 (iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200469 (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */
Zhu Yibb9f8692009-05-21 21:20:45 +0800470 (iw_handler) NULL, /* SIOCGIWENCODEEXT */
471 (iw_handler) NULL, /* SIOCSIWPMKSA */
472 (iw_handler) NULL, /* -- hole -- */
473};
474
475const struct iw_handler_def iwm_iw_handler_def = {
476 .num_standard = ARRAY_SIZE(iwm_handlers),
477 .standard = (iw_handler *) iwm_handlers,
478 .get_wireless_stats = iwm_get_wireless_stats,
479};
480