724 lines
19 KiB
C
724 lines
19 KiB
C
/*
|
|
* 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);
|
|
|
|
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) {
|
|
if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
|
|
return 0;
|
|
|
|
iwm_invalidate_mlme_profile(iwm);
|
|
}
|
|
|
|
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;
|
|
|
|
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 struct iwm_key *
|
|
iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
|
|
struct iw_encode_ext *ext, u8 alg)
|
|
{
|
|
struct iwm_key *key = &iwm->keys[key_idx];
|
|
|
|
memset(key, 0, sizeof(struct iwm_key));
|
|
memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
|
|
key->hdr.key_idx = key_idx;
|
|
if (is_broadcast_ether_addr(ext->addr.sa_data))
|
|
key->hdr.multicast = 1;
|
|
|
|
key->in_use = in_use;
|
|
key->flags = ext->ext_flags;
|
|
key->alg = alg;
|
|
key->key_len = ext->key_len;
|
|
memcpy(key->key, ext->key, ext->key_len);
|
|
|
|
return key;
|
|
}
|
|
|
|
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_wext_siwencode(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *erq, char *key_buf)
|
|
{
|
|
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
|
struct iwm_key *uninitialized_var(key);
|
|
int idx, i, uninitialized_var(alg), remove = 0, ret;
|
|
|
|
IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
|
|
IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
|
|
|
|
if (!iwm->umac_profile) {
|
|
IWM_ERR(iwm, "UMAC profile not allocated yet\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (erq->length == WLAN_KEY_LEN_WEP40) {
|
|
alg = UMAC_CIPHER_TYPE_WEP_40;
|
|
iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
|
|
iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
|
|
} else if (erq->length == WLAN_KEY_LEN_WEP104) {
|
|
alg = UMAC_CIPHER_TYPE_WEP_104;
|
|
iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
|
|
iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
|
|
}
|
|
|
|
if (erq->flags & IW_ENCODE_RESTRICTED)
|
|
iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
|
|
else
|
|
iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
|
|
|
|
idx = erq->flags & IW_ENCODE_INDEX;
|
|
if (idx == 0) {
|
|
if (iwm->default_key)
|
|
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
|
if (iwm->default_key == &iwm->keys[i]) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
iwm->default_key = &iwm->keys[idx];
|
|
} else if (idx < 1 || idx > 4) {
|
|
return -EINVAL;
|
|
} else
|
|
idx--;
|
|
|
|
if (erq->flags & IW_ENCODE_DISABLED)
|
|
remove = 1;
|
|
else if (erq->length == 0) {
|
|
if (!iwm->keys[idx].in_use)
|
|
return -EINVAL;
|
|
iwm->default_key = &iwm->keys[idx];
|
|
}
|
|
|
|
if (erq->length) {
|
|
key = &iwm->keys[idx];
|
|
memset(key, 0, sizeof(struct iwm_key));
|
|
memset(key->hdr.mac, 0xff, ETH_ALEN);
|
|
key->hdr.key_idx = idx;
|
|
key->hdr.multicast = 1;
|
|
key->in_use = !remove;
|
|
key->alg = alg;
|
|
key->key_len = erq->length;
|
|
memcpy(key->key, key_buf, erq->length);
|
|
|
|
IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
|
|
idx, !!iwm->default_key);
|
|
}
|
|
|
|
if (remove) {
|
|
if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
|
|
int j;
|
|
for (j = 0; j < IWM_NUM_KEYS; j++)
|
|
if (iwm->keys[j].in_use) {
|
|
struct iwm_key *k = &iwm->keys[j];
|
|
|
|
k->in_use = 0;
|
|
ret = iwm_set_key(iwm, remove, 0, k);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
iwm->umac_profile->sec.ucast_cipher =
|
|
UMAC_CIPHER_TYPE_NONE;
|
|
iwm->umac_profile->sec.mcast_cipher =
|
|
UMAC_CIPHER_TYPE_NONE;
|
|
iwm->umac_profile->sec.auth_type =
|
|
UMAC_AUTH_TYPE_OPEN;
|
|
|
|
return 0;
|
|
} else {
|
|
key->in_use = 0;
|
|
return iwm_set_key(iwm, remove, 0, key);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we havent set a profile yet, we cant set keys.
|
|
* Keys will be pushed after we're associated.
|
|
*/
|
|
if (!iwm->umac_profile_active)
|
|
return 0;
|
|
|
|
/*
|
|
* If there is a current active profile, but no
|
|
* default key, it's not worth trying to associate again.
|
|
*/
|
|
if (!iwm->default_key)
|
|
return 0;
|
|
|
|
/*
|
|
* Here we have an active profile, but a key setting changed.
|
|
* We thus have to invalidate the current profile, and push the
|
|
* new one. Keys will be pushed when association takes place.
|
|
*/
|
|
ret = iwm_invalidate_mlme_profile(iwm);
|
|
if (ret < 0) {
|
|
IWM_ERR(iwm, "Couldn't invalidate profile\n");
|
|
return ret;
|
|
}
|
|
|
|
return iwm_send_mlme_profile(iwm);
|
|
}
|
|
|
|
static int iwm_wext_giwencode(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *erq, char *key)
|
|
{
|
|
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
|
int idx, i;
|
|
|
|
idx = erq->flags & IW_ENCODE_INDEX;
|
|
if (idx < 1 || idx > 4) {
|
|
idx = -1;
|
|
if (!iwm->default_key) {
|
|
erq->length = 0;
|
|
erq->flags |= IW_ENCODE_NOKEY;
|
|
return 0;
|
|
} else
|
|
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
|
if (iwm->default_key == &iwm->keys[i]) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
if (idx < 0)
|
|
return -EINVAL;
|
|
} else
|
|
idx--;
|
|
|
|
erq->flags = idx + 1;
|
|
|
|
if (!iwm->keys[idx].in_use) {
|
|
erq->length = 0;
|
|
erq->flags |= IW_ENCODE_DISABLED;
|
|
return 0;
|
|
}
|
|
|
|
memcpy(key, iwm->keys[idx].key,
|
|
min_t(int, erq->length, iwm->keys[idx].key_len));
|
|
erq->length = iwm->keys[idx].key_len;
|
|
erq->flags |= IW_ENCODE_ENABLED;
|
|
|
|
if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
|
|
switch (iwm->umac_profile->sec.auth_type) {
|
|
case UMAC_AUTH_TYPE_OPEN:
|
|
erq->flags |= IW_ENCODE_OPEN;
|
|
break;
|
|
default:
|
|
erq->flags |= IW_ENCODE_RESTRICTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_wext_siwpower(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_param *wrq, char *extra)
|
|
{
|
|
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
|
u32 power_index;
|
|
|
|
if (wrq->disabled) {
|
|
power_index = IWM_POWER_INDEX_MIN;
|
|
goto set;
|
|
} else
|
|
power_index = IWM_POWER_INDEX_DEFAULT;
|
|
|
|
switch (wrq->flags & IW_POWER_MODE) {
|
|
case IW_POWER_ON:
|
|
case IW_POWER_MODE:
|
|
case IW_POWER_ALL_R:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
set:
|
|
if (power_index == iwm->conf.power_index)
|
|
return 0;
|
|
|
|
iwm->conf.power_index = power_index;
|
|
|
|
return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
|
CFG_POWER_INDEX, iwm->conf.power_index);
|
|
}
|
|
|
|
static int iwm_wext_giwpower(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *wrqu, char *extra)
|
|
{
|
|
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
|
|
|
wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
|
|
{
|
|
u8 *auth_type = &iwm->umac_profile->sec.auth_type;
|
|
|
|
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;
|
|
|
|
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 {
|
|
*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 int iwm_wext_siwencodeext(struct net_device *dev,
|
|
struct iw_request_info *info,
|
|
struct iw_point *erq, char *extra)
|
|
{
|
|
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
|
struct iwm_key *key;
|
|
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
|
|
int uninitialized_var(alg), idx, i, remove = 0;
|
|
|
|
IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
|
|
IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
|
|
IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
|
|
IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
|
|
IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
|
|
|
|
switch (ext->alg) {
|
|
case IW_ENCODE_ALG_NONE:
|
|
remove = 1;
|
|
break;
|
|
case IW_ENCODE_ALG_WEP:
|
|
if (ext->key_len == WLAN_KEY_LEN_WEP40)
|
|
alg = UMAC_CIPHER_TYPE_WEP_40;
|
|
else if (ext->key_len == WLAN_KEY_LEN_WEP104)
|
|
alg = UMAC_CIPHER_TYPE_WEP_104;
|
|
else {
|
|
IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
case IW_ENCODE_ALG_TKIP:
|
|
alg = UMAC_CIPHER_TYPE_TKIP;
|
|
break;
|
|
case IW_ENCODE_ALG_CCMP:
|
|
alg = UMAC_CIPHER_TYPE_CCMP;
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
idx = erq->flags & IW_ENCODE_INDEX;
|
|
|
|
if (idx == 0) {
|
|
if (iwm->default_key)
|
|
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
|
if (iwm->default_key == &iwm->keys[i]) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
} else if (idx < 1 || idx > 4) {
|
|
return -EINVAL;
|
|
} else
|
|
idx--;
|
|
|
|
if (erq->flags & IW_ENCODE_DISABLED)
|
|
remove = 1;
|
|
else if ((erq->length == 0) ||
|
|
(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
|
|
iwm->default_key = &iwm->keys[idx];
|
|
if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
|
|
return iwm_set_tx_key(iwm, idx);
|
|
}
|
|
|
|
key = iwm_key_init(iwm, idx, !remove, ext, alg);
|
|
|
|
return iwm_set_key(iwm, remove, !iwm->default_key, key);
|
|
}
|
|
|
|
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) NULL, /* SIOCSIWTXPOW */
|
|
(iw_handler) NULL, /* SIOCGIWTXPOW */
|
|
(iw_handler) NULL, /* SIOCSIWRETRY */
|
|
(iw_handler) NULL, /* SIOCGIWRETRY */
|
|
(iw_handler) iwm_wext_siwencode, /* SIOCSIWENCODE */
|
|
(iw_handler) iwm_wext_giwencode, /* SIOCGIWENCODE */
|
|
(iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */
|
|
(iw_handler) iwm_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) iwm_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,
|
|
};
|
|
|