aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwmc3200wifi/wext.c
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2009-05-21 21:20:45 +0800
committerJohn W. Linville <linville@tuxdriver.com>2009-05-22 14:06:02 -0400
commitbb9f8692f5043efef0dcef048cdd1db68299c2cb (patch)
treede0eade3ea6e40341727789a3a91e5c506b68759 /drivers/net/wireless/iwmc3200wifi/wext.c
parente31a16d6f64ef0e324c6f54d5112703c3f13a9c4 (diff)
iwmc3200wifi: Add new Intel Wireless Multicomm 802.11 driver
This driver supports Intel's full MAC wireless multicomm 802.11 hardware. Although the hardware is a 802.11agn device, we currently only support 802.11ag, in managed and ad-hoc mode (no AP mode for now). Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/wext.c')
-rw-r--r--drivers/net/wireless/iwmc3200wifi/wext.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
new file mode 100644
index 00000000000..584c94d0f39
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -0,0 +1,723 @@
+/*
+ * 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,
+};
+