2 * Intel Wireless Multicomm 3200 WiFi driver
4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
5 * Samuel Ortiz <samuel.ortiz@intel.com>
6 * Zhu Yi <yi.zhu@intel.com>
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.
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.
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
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>
37 static struct iw_statistics
*iwm_get_wireless_stats(struct net_device
*dev
)
39 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
40 struct iw_statistics
*wstats
= &iwm
->wstats
;
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
;
50 static int iwm_wext_siwfreq(struct net_device
*dev
,
51 struct iw_request_info
*info
,
52 struct iw_freq
*freq
, char *extra
)
54 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
56 if (freq
->flags
== IW_FREQ_AUTO
)
59 /* frequency/channel can only be set in IBSS mode */
60 if (iwm
->conf
.mode
!= UMAC_MODE_IBSS
)
63 return cfg80211_ibss_wext_siwfreq(dev
, info
, freq
, extra
);
66 static int iwm_wext_giwfreq(struct net_device
*dev
,
67 struct iw_request_info
*info
,
68 struct iw_freq
*freq
, char *extra
)
70 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
72 if (iwm
->conf
.mode
== UMAC_MODE_IBSS
)
73 return cfg80211_ibss_wext_giwfreq(dev
, info
, freq
, extra
);
76 freq
->m
= iwm
->channel
;
81 static int iwm_wext_siwap(struct net_device
*dev
, struct iw_request_info
*info
,
82 struct sockaddr
*ap_addr
, char *extra
)
84 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
87 IWM_DBG_WEXT(iwm
, DBG
, "Set BSSID: %pM\n", ap_addr
->sa_data
);
89 if (iwm
->conf
.mode
== UMAC_MODE_IBSS
)
90 return cfg80211_ibss_wext_siwap(dev
, info
, ap_addr
, extra
);
92 if (!test_bit(IWM_STATUS_READY
, &iwm
->status
))
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;
102 IWM_DBG_WEXT(iwm
, DBG
, "add mandatory bssid %pM\n",
104 memcpy(&iwm
->umac_profile
->bssid
[0], ap_addr
->sa_data
,
106 iwm
->umac_profile
->bss_num
= 1;
109 if (iwm
->umac_profile_active
) {
112 if (!memcmp(&iwm
->umac_profile
->bssid
[0], iwm
->bssid
, ETH_ALEN
))
116 * If we're clearing the BSSID, and we're associated,
117 * we have to clear the keys as they're no longer valid.
119 if (is_zero_ether_addr(ap_addr
->sa_data
)) {
120 for (i
= 0; i
< IWM_NUM_KEYS
; i
++)
121 iwm
->keys
[i
].key_len
= 0;
124 ret
= iwm_invalidate_mlme_profile(iwm
);
126 IWM_ERR(iwm
, "Couldn't invalidate profile\n");
131 if (iwm
->umac_profile
->ssid
.ssid_len
)
132 return iwm_send_mlme_profile(iwm
);
137 static int iwm_wext_giwap(struct net_device
*dev
, struct iw_request_info
*info
,
138 struct sockaddr
*ap_addr
, char *extra
)
140 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
142 switch (iwm
->conf
.mode
) {
144 return cfg80211_ibss_wext_giwap(dev
, info
, ap_addr
, extra
);
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
);
150 memset(&ap_addr
->sa_data
, 0, ETH_ALEN
);
159 static int iwm_wext_siwessid(struct net_device
*dev
,
160 struct iw_request_info
*info
,
161 struct iw_point
*data
, char *ssid
)
163 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
164 size_t len
= data
->length
;
167 IWM_DBG_WEXT(iwm
, DBG
, "Set ESSID: >%s<\n", ssid
);
169 if (iwm
->conf
.mode
== UMAC_MODE_IBSS
)
170 return cfg80211_ibss_wext_siwessid(dev
, info
, data
, ssid
);
172 if (!test_bit(IWM_STATUS_READY
, &iwm
->status
))
175 if (len
> 0 && ssid
[len
- 1] == '\0')
178 if (iwm
->umac_profile_active
) {
179 if (iwm
->umac_profile
->ssid
.ssid_len
== len
&&
180 !memcmp(iwm
->umac_profile
->ssid
.ssid
, ssid
, len
))
183 ret
= iwm_invalidate_mlme_profile(iwm
);
185 IWM_ERR(iwm
, "Couldn't invalidate profile\n");
190 iwm
->umac_profile
->ssid
.ssid_len
= len
;
191 memcpy(iwm
->umac_profile
->ssid
.ssid
, ssid
, len
);
193 return iwm_send_mlme_profile(iwm
);
196 static int iwm_wext_giwessid(struct net_device
*dev
,
197 struct iw_request_info
*info
,
198 struct iw_point
*data
, char *ssid
)
200 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
202 if (iwm
->conf
.mode
== UMAC_MODE_IBSS
)
203 return cfg80211_ibss_wext_giwessid(dev
, info
, data
, ssid
);
205 if (!test_bit(IWM_STATUS_READY
, &iwm
->status
))
208 data
->length
= iwm
->umac_profile
->ssid
.ssid_len
;
210 memcpy(ssid
, iwm
->umac_profile
->ssid
.ssid
, data
->length
);
218 static int iwm_wext_giwrate(struct net_device
*dev
,
219 struct iw_request_info
*info
,
220 struct iw_param
*rate
, char *extra
)
222 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
224 rate
->value
= iwm
->rate
* 1000000;
229 static int iwm_set_wpa_version(struct iwm_priv
*iwm
, u8 wpa_version
)
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
;
236 iwm
->umac_profile
->sec
.flags
= UMAC_SEC_FLG_LEGACY_PROFILE
;
241 static int iwm_set_key_mgt(struct iwm_priv
*iwm
, u8 key_mgt
)
243 u8
*auth_type
= &iwm
->umac_profile
->sec
.auth_type
;
245 IWM_DBG_WEXT(iwm
, DBG
, "key_mgt: 0x%x\n", key_mgt
);
247 if (key_mgt
== IW_AUTH_KEY_MGMT_802_1X
)
248 *auth_type
= UMAC_AUTH_TYPE_8021X
;
249 else if (key_mgt
== IW_AUTH_KEY_MGMT_PSK
) {
250 if (iwm
->umac_profile
->sec
.flags
&
251 (UMAC_SEC_FLG_WPA_ON_MSK
| UMAC_SEC_FLG_RSNA_ON_MSK
))
252 *auth_type
= UMAC_AUTH_TYPE_RSNA_PSK
;
254 *auth_type
= UMAC_AUTH_TYPE_LEGACY_PSK
;
256 IWM_ERR(iwm
, "Invalid key mgt: 0x%x\n", key_mgt
);
263 static int iwm_set_cipher(struct iwm_priv
*iwm
, u8 cipher
, u8 ucast
)
265 u8
*profile_cipher
= ucast
? &iwm
->umac_profile
->sec
.ucast_cipher
:
266 &iwm
->umac_profile
->sec
.mcast_cipher
;
269 case IW_AUTH_CIPHER_NONE
:
270 *profile_cipher
= UMAC_CIPHER_TYPE_NONE
;
272 case IW_AUTH_CIPHER_WEP40
:
273 *profile_cipher
= UMAC_CIPHER_TYPE_WEP_40
;
275 case IW_AUTH_CIPHER_TKIP
:
276 *profile_cipher
= UMAC_CIPHER_TYPE_TKIP
;
278 case IW_AUTH_CIPHER_CCMP
:
279 *profile_cipher
= UMAC_CIPHER_TYPE_CCMP
;
281 case IW_AUTH_CIPHER_WEP104
:
282 *profile_cipher
= UMAC_CIPHER_TYPE_WEP_104
;
285 IWM_ERR(iwm
, "Unsupported cipher: 0x%x\n", cipher
);
292 static int iwm_set_auth_alg(struct iwm_priv
*iwm
, u8 auth_alg
)
294 u8
*auth_type
= &iwm
->umac_profile
->sec
.auth_type
;
296 IWM_DBG_WEXT(iwm
, DBG
, "auth_alg: 0x%x\n", auth_alg
);
299 case IW_AUTH_ALG_OPEN_SYSTEM
:
300 *auth_type
= UMAC_AUTH_TYPE_OPEN
;
302 case IW_AUTH_ALG_SHARED_KEY
:
303 if (iwm
->umac_profile
->sec
.flags
&
304 (UMAC_SEC_FLG_WPA_ON_MSK
| UMAC_SEC_FLG_RSNA_ON_MSK
)) {
305 if (*auth_type
== UMAC_AUTH_TYPE_8021X
)
307 *auth_type
= UMAC_AUTH_TYPE_RSNA_PSK
;
309 IWM_DBG_WEXT(iwm
, DBG
, "WEP shared key\n");
310 *auth_type
= UMAC_AUTH_TYPE_LEGACY_PSK
;
313 case IW_AUTH_ALG_LEAP
:
315 IWM_ERR(iwm
, "Unsupported auth alg: 0x%x\n", auth_alg
);
322 static int iwm_wext_siwauth(struct net_device
*dev
,
323 struct iw_request_info
*info
,
324 struct iw_param
*data
, char *extra
)
326 struct iwm_priv
*iwm
= ndev_to_iwm(dev
);
330 (IW_AUTH_WPA_VERSION
| IW_AUTH_KEY_MGMT
|
331 IW_AUTH_WPA_ENABLED
| IW_AUTH_80211_AUTH_ALG
)) {
332 /* We need to invalidate the current profile */
333 if (iwm
->umac_profile_active
) {
334 ret
= iwm_invalidate_mlme_profile(iwm
);
336 IWM_ERR(iwm
, "Couldn't invalidate profile\n");
342 switch (data
->flags
& IW_AUTH_INDEX
) {
343 case IW_AUTH_WPA_VERSION
:
344 return iwm_set_wpa_version(iwm
, data
->value
);
346 case IW_AUTH_CIPHER_PAIRWISE
:
347 return iwm_set_cipher(iwm
, data
->value
, 1);
349 case IW_AUTH_CIPHER_GROUP
:
350 return iwm_set_cipher(iwm
, data
->value
, 0);
352 case IW_AUTH_KEY_MGMT
:
353 return iwm_set_key_mgt(iwm
, data
->value
);
355 case IW_AUTH_80211_AUTH_ALG
:
356 return iwm_set_auth_alg(iwm
, data
->value
);
365 static int iwm_wext_giwauth(struct net_device
*dev
,
366 struct iw_request_info
*info
,
367 struct iw_param
*data
, char *extra
)
372 static const iw_handler iwm_handlers
[] =
374 (iw_handler
) NULL
, /* SIOCSIWCOMMIT */
375 (iw_handler
) cfg80211_wext_giwname
, /* SIOCGIWNAME */
376 (iw_handler
) NULL
, /* SIOCSIWNWID */
377 (iw_handler
) NULL
, /* SIOCGIWNWID */
378 (iw_handler
) iwm_wext_siwfreq
, /* SIOCSIWFREQ */
379 (iw_handler
) iwm_wext_giwfreq
, /* SIOCGIWFREQ */
380 (iw_handler
) cfg80211_wext_siwmode
, /* SIOCSIWMODE */
381 (iw_handler
) cfg80211_wext_giwmode
, /* SIOCGIWMODE */
382 (iw_handler
) NULL
, /* SIOCSIWSENS */
383 (iw_handler
) NULL
, /* SIOCGIWSENS */
384 (iw_handler
) NULL
/* not used */, /* SIOCSIWRANGE */
385 (iw_handler
) cfg80211_wext_giwrange
, /* SIOCGIWRANGE */
386 (iw_handler
) NULL
/* not used */, /* SIOCSIWPRIV */
387 (iw_handler
) NULL
/* kernel code */, /* SIOCGIWPRIV */
388 (iw_handler
) NULL
/* not used */, /* SIOCSIWSTATS */
389 (iw_handler
) NULL
/* kernel code */, /* SIOCGIWSTATS */
390 (iw_handler
) NULL
, /* SIOCSIWSPY */
391 (iw_handler
) NULL
, /* SIOCGIWSPY */
392 (iw_handler
) NULL
, /* SIOCSIWTHRSPY */
393 (iw_handler
) NULL
, /* SIOCGIWTHRSPY */
394 (iw_handler
) iwm_wext_siwap
, /* SIOCSIWAP */
395 (iw_handler
) iwm_wext_giwap
, /* SIOCGIWAP */
396 (iw_handler
) NULL
, /* SIOCSIWMLME */
397 (iw_handler
) NULL
, /* SIOCGIWAPLIST */
398 (iw_handler
) cfg80211_wext_siwscan
, /* SIOCSIWSCAN */
399 (iw_handler
) cfg80211_wext_giwscan
, /* SIOCGIWSCAN */
400 (iw_handler
) iwm_wext_siwessid
, /* SIOCSIWESSID */
401 (iw_handler
) iwm_wext_giwessid
, /* SIOCGIWESSID */
402 (iw_handler
) NULL
, /* SIOCSIWNICKN */
403 (iw_handler
) NULL
, /* SIOCGIWNICKN */
404 (iw_handler
) NULL
, /* -- hole -- */
405 (iw_handler
) NULL
, /* -- hole -- */
406 (iw_handler
) NULL
, /* SIOCSIWRATE */
407 (iw_handler
) iwm_wext_giwrate
, /* SIOCGIWRATE */
408 (iw_handler
) cfg80211_wext_siwrts
, /* SIOCSIWRTS */
409 (iw_handler
) cfg80211_wext_giwrts
, /* SIOCGIWRTS */
410 (iw_handler
) cfg80211_wext_siwfrag
, /* SIOCSIWFRAG */
411 (iw_handler
) cfg80211_wext_giwfrag
, /* SIOCGIWFRAG */
412 (iw_handler
) cfg80211_wext_siwtxpower
, /* SIOCSIWTXPOW */
413 (iw_handler
) cfg80211_wext_giwtxpower
, /* SIOCGIWTXPOW */
414 (iw_handler
) NULL
, /* SIOCSIWRETRY */
415 (iw_handler
) NULL
, /* SIOCGIWRETRY */
416 (iw_handler
) cfg80211_wext_siwencode
, /* SIOCSIWENCODE */
417 (iw_handler
) cfg80211_wext_giwencode
, /* SIOCGIWENCODE */
418 (iw_handler
) cfg80211_wext_siwpower
, /* SIOCSIWPOWER */
419 (iw_handler
) cfg80211_wext_giwpower
, /* SIOCGIWPOWER */
420 (iw_handler
) NULL
, /* -- hole -- */
421 (iw_handler
) NULL
, /* -- hole -- */
422 (iw_handler
) NULL
, /* SIOCSIWGENIE */
423 (iw_handler
) NULL
, /* SIOCGIWGENIE */
424 (iw_handler
) iwm_wext_siwauth
, /* SIOCSIWAUTH */
425 (iw_handler
) iwm_wext_giwauth
, /* SIOCGIWAUTH */
426 (iw_handler
) cfg80211_wext_siwencodeext
, /* SIOCSIWENCODEEXT */
427 (iw_handler
) NULL
, /* SIOCGIWENCODEEXT */
428 (iw_handler
) NULL
, /* SIOCSIWPMKSA */
429 (iw_handler
) NULL
, /* -- hole -- */
432 const struct iw_handler_def iwm_iw_handler_def
= {
433 .num_standard
= ARRAY_SIZE(iwm_handlers
),
434 .standard
= (iw_handler
*) iwm_handlers
,
435 .get_wireless_stats
= iwm_get_wireless_stats
,