123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- /*
- * Intel Wireless Multicomm 3200 WiFi driver
- *
- * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
- * Samuel Ortiz <samuel.ortiz@intel.com>
- * Zhu Yi <yi.zhu@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- */
- #include <linux/kernel.h>
- #include <linux/netdevice.h>
- #include <linux/wireless.h>
- #include <linux/if_arp.h>
- #include <linux/etherdevice.h>
- #include <net/cfg80211.h>
- #include <net/iw_handler.h>
- #include "iwm.h"
- #include "umac.h"
- #include "commands.h"
- #include "debug.h"
- static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- struct iw_statistics *wstats = &iwm->wstats;
- if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
- memset(wstats, 0, sizeof(struct iw_statistics));
- wstats->qual.updated = IW_QUAL_ALL_INVALID;
- }
- return wstats;
- }
- static int iwm_wext_siwfreq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *freq, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- if (freq->flags == IW_FREQ_AUTO)
- return 0;
- /* frequency/channel can only be set in IBSS mode */
- if (iwm->conf.mode != UMAC_MODE_IBSS)
- return -EOPNOTSUPP;
- return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
- }
- static int iwm_wext_giwfreq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *freq, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- if (iwm->conf.mode == UMAC_MODE_IBSS)
- return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
- freq->e = 0;
- freq->m = iwm->channel;
- return 0;
- }
- static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
- struct sockaddr *ap_addr, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- int ret;
- IWM_DBG_WEXT(iwm, DBG, "Set BSSID: %pM\n", ap_addr->sa_data);
- if (iwm->conf.mode == UMAC_MODE_IBSS)
- return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
- if (!test_bit(IWM_STATUS_READY, &iwm->status))
- return -EIO;
- if (is_zero_ether_addr(ap_addr->sa_data) ||
- is_broadcast_ether_addr(ap_addr->sa_data)) {
- IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
- iwm->umac_profile->bssid[0]);
- memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
- iwm->umac_profile->bss_num = 0;
- } else {
- IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
- ap_addr->sa_data);
- memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
- ETH_ALEN);
- iwm->umac_profile->bss_num = 1;
- }
- if (iwm->umac_profile_active) {
- int i;
- if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
- return 0;
- /*
- * If we're clearing the BSSID, and we're associated,
- * we have to clear the keys as they're no longer valid.
- */
- if (is_zero_ether_addr(ap_addr->sa_data)) {
- for (i = 0; i < IWM_NUM_KEYS; i++)
- iwm->keys[i].key_len = 0;
- }
- ret = iwm_invalidate_mlme_profile(iwm);
- if (ret < 0) {
- IWM_ERR(iwm, "Couldn't invalidate profile\n");
- return ret;
- }
- }
- if (iwm->umac_profile->ssid.ssid_len)
- return iwm_send_mlme_profile(iwm);
- return 0;
- }
- static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
- struct sockaddr *ap_addr, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- switch (iwm->conf.mode) {
- case UMAC_MODE_IBSS:
- return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
- case UMAC_MODE_BSS:
- if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
- ap_addr->sa_family = ARPHRD_ETHER;
- memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
- } else
- memset(&ap_addr->sa_data, 0, ETH_ALEN);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return 0;
- }
- static int iwm_wext_siwessid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *data, char *ssid)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- size_t len = data->length;
- int ret;
- IWM_DBG_WEXT(iwm, DBG, "Set ESSID: >%s<\n", ssid);
- if (iwm->conf.mode == UMAC_MODE_IBSS)
- return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
- if (!test_bit(IWM_STATUS_READY, &iwm->status))
- return -EIO;
- if (len > 0 && ssid[len - 1] == '\0')
- len--;
- if (iwm->umac_profile_active) {
- if (iwm->umac_profile->ssid.ssid_len == len &&
- !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
- return 0;
- ret = iwm_invalidate_mlme_profile(iwm);
- if (ret < 0) {
- IWM_ERR(iwm, "Couldn't invalidate profile\n");
- return ret;
- }
- }
- iwm->umac_profile->ssid.ssid_len = len;
- memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
- return iwm_send_mlme_profile(iwm);
- }
- static int iwm_wext_giwessid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *data, char *ssid)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- if (iwm->conf.mode == UMAC_MODE_IBSS)
- return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
- if (!test_bit(IWM_STATUS_READY, &iwm->status))
- return -EIO;
- data->length = iwm->umac_profile->ssid.ssid_len;
- if (data->length) {
- memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
- data->flags = 1;
- } else
- data->flags = 0;
- return 0;
- }
- static int iwm_wext_giwrate(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rate, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- rate->value = iwm->rate * 1000000;
- return 0;
- }
- static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
- {
- if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
- iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
- else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
- iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
- else
- iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
- return 0;
- }
- static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
- {
- u8 *auth_type = &iwm->umac_profile->sec.auth_type;
- IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
- if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
- *auth_type = UMAC_AUTH_TYPE_8021X;
- else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
- if (iwm->umac_profile->sec.flags &
- (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
- *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
- else
- *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
- } else {
- IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
- return -EINVAL;
- }
- return 0;
- }
- static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
- {
- u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
- &iwm->umac_profile->sec.mcast_cipher;
- switch (cipher) {
- case IW_AUTH_CIPHER_NONE:
- *profile_cipher = UMAC_CIPHER_TYPE_NONE;
- break;
- case IW_AUTH_CIPHER_WEP40:
- *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
- break;
- case IW_AUTH_CIPHER_TKIP:
- *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
- break;
- case IW_AUTH_CIPHER_CCMP:
- *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
- break;
- case IW_AUTH_CIPHER_WEP104:
- *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
- break;
- default:
- IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
- return -ENOTSUPP;
- }
- return 0;
- }
- static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
- {
- u8 *auth_type = &iwm->umac_profile->sec.auth_type;
- IWM_DBG_WEXT(iwm, DBG, "auth_alg: 0x%x\n", auth_alg);
- switch (auth_alg) {
- case IW_AUTH_ALG_OPEN_SYSTEM:
- *auth_type = UMAC_AUTH_TYPE_OPEN;
- break;
- case IW_AUTH_ALG_SHARED_KEY:
- if (iwm->umac_profile->sec.flags &
- (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
- if (*auth_type == UMAC_AUTH_TYPE_8021X)
- return -EINVAL;
- *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
- } else {
- IWM_DBG_WEXT(iwm, DBG, "WEP shared key\n");
- *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
- }
- break;
- case IW_AUTH_ALG_LEAP:
- default:
- IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
- return -ENOTSUPP;
- }
- return 0;
- }
- static int iwm_wext_siwauth(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *data, char *extra)
- {
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- int ret;
- if ((data->flags) &
- (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
- IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
- /* We need to invalidate the current profile */
- if (iwm->umac_profile_active) {
- ret = iwm_invalidate_mlme_profile(iwm);
- if (ret < 0) {
- IWM_ERR(iwm, "Couldn't invalidate profile\n");
- return ret;
- }
- }
- }
- switch (data->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- return iwm_set_wpa_version(iwm, data->value);
- break;
- case IW_AUTH_CIPHER_PAIRWISE:
- return iwm_set_cipher(iwm, data->value, 1);
- break;
- case IW_AUTH_CIPHER_GROUP:
- return iwm_set_cipher(iwm, data->value, 0);
- break;
- case IW_AUTH_KEY_MGMT:
- return iwm_set_key_mgt(iwm, data->value);
- break;
- case IW_AUTH_80211_AUTH_ALG:
- return iwm_set_auth_alg(iwm, data->value);
- break;
- default:
- return -ENOTSUPP;
- }
- return 0;
- }
- static int iwm_wext_giwauth(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *data, char *extra)
- {
- return 0;
- }
- static const iw_handler iwm_handlers[] =
- {
- (iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
- (iw_handler) NULL, /* SIOCSIWNWID */
- (iw_handler) NULL, /* SIOCGIWNWID */
- (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */
- (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */
- (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
- (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
- (iw_handler) NULL, /* SIOCSIWSENS */
- (iw_handler) NULL, /* SIOCGIWSENS */
- (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
- (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
- (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
- (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
- (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
- (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
- (iw_handler) NULL, /* SIOCSIWSPY */
- (iw_handler) NULL, /* SIOCGIWSPY */
- (iw_handler) NULL, /* SIOCSIWTHRSPY */
- (iw_handler) NULL, /* SIOCGIWTHRSPY */
- (iw_handler) iwm_wext_siwap, /* SIOCSIWAP */
- (iw_handler) iwm_wext_giwap, /* SIOCGIWAP */
- (iw_handler) NULL, /* SIOCSIWMLME */
- (iw_handler) NULL, /* SIOCGIWAPLIST */
- (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
- (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
- (iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */
- (iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */
- (iw_handler) NULL, /* SIOCSIWNICKN */
- (iw_handler) NULL, /* SIOCGIWNICKN */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* SIOCSIWRATE */
- (iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */
- (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
- (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
- (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
- (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
- (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */
- (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */
- (iw_handler) NULL, /* SIOCSIWRETRY */
- (iw_handler) NULL, /* SIOCGIWRETRY */
- (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */
- (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */
- (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */
- (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* SIOCSIWGENIE */
- (iw_handler) NULL, /* SIOCGIWGENIE */
- (iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
- (iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
- (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */
- (iw_handler) NULL, /* SIOCGIWENCODEEXT */
- (iw_handler) NULL, /* SIOCSIWPMKSA */
- (iw_handler) NULL, /* -- hole -- */
- };
- const struct iw_handler_def iwm_iw_handler_def = {
- .num_standard = ARRAY_SIZE(iwm_handlers),
- .standard = (iw_handler *) iwm_handlers,
- .get_wireless_stats = iwm_get_wireless_stats,
- };
|