Merge git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6
[linux-2.6] / drivers / net / wireless / iwmc3200wifi / wext.c
1 /*
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
37 static 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
50 static 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
66 static 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
81 static 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);
85
86         if (iwm->conf.mode == UMAC_MODE_IBSS)
87                 return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
88
89         if (!test_bit(IWM_STATUS_READY, &iwm->status))
90                 return -EIO;
91
92         if (is_zero_ether_addr(ap_addr->sa_data) ||
93             is_broadcast_ether_addr(ap_addr->sa_data)) {
94                 IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
95                              iwm->umac_profile->bssid[0]);
96                 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
97                 iwm->umac_profile->bss_num = 0;
98         } else {
99                 IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
100                              ap_addr->sa_data);
101                 memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
102                        ETH_ALEN);
103                 iwm->umac_profile->bss_num = 1;
104         }
105
106         if (iwm->umac_profile_active) {
107                 if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
108                         return 0;
109
110                 iwm_invalidate_mlme_profile(iwm);
111         }
112
113         if (iwm->umac_profile->ssid.ssid_len)
114                 return iwm_send_mlme_profile(iwm);
115
116         return 0;
117 }
118
119 static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
120                           struct sockaddr *ap_addr, char *extra)
121 {
122         struct iwm_priv *iwm = ndev_to_iwm(dev);
123
124         switch (iwm->conf.mode) {
125         case UMAC_MODE_IBSS:
126                 return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
127         case UMAC_MODE_BSS:
128                 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
129                         ap_addr->sa_family = ARPHRD_ETHER;
130                         memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
131                 } else
132                         memset(&ap_addr->sa_data, 0, ETH_ALEN);
133                 break;
134         default:
135                 return -EOPNOTSUPP;
136         }
137
138         return 0;
139 }
140
141 static int iwm_wext_siwessid(struct net_device *dev,
142                              struct iw_request_info *info,
143                              struct iw_point *data, char *ssid)
144 {
145         struct iwm_priv *iwm = ndev_to_iwm(dev);
146         size_t len = data->length;
147         int ret;
148
149         if (iwm->conf.mode == UMAC_MODE_IBSS)
150                 return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
151
152         if (!test_bit(IWM_STATUS_READY, &iwm->status))
153                 return -EIO;
154
155         if (len > 0 && ssid[len - 1] == '\0')
156                 len--;
157
158         if (iwm->umac_profile_active) {
159                 if (iwm->umac_profile->ssid.ssid_len == len &&
160                     !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
161                         return 0;
162
163                 ret = iwm_invalidate_mlme_profile(iwm);
164                 if (ret < 0) {
165                         IWM_ERR(iwm, "Couldn't invalidate profile\n");
166                         return ret;
167                 }
168         }
169
170         iwm->umac_profile->ssid.ssid_len = len;
171         memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
172
173         return iwm_send_mlme_profile(iwm);
174 }
175
176 static int iwm_wext_giwessid(struct net_device *dev,
177                              struct iw_request_info *info,
178                              struct iw_point *data, char *ssid)
179 {
180         struct iwm_priv *iwm = ndev_to_iwm(dev);
181
182         if (iwm->conf.mode == UMAC_MODE_IBSS)
183                 return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
184
185         if (!test_bit(IWM_STATUS_READY, &iwm->status))
186                 return -EIO;
187
188         data->length = iwm->umac_profile->ssid.ssid_len;
189         if (data->length) {
190                 memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
191                 data->flags = 1;
192         } else
193                 data->flags = 0;
194
195         return 0;
196 }
197
198 static struct iwm_key *
199 iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
200              struct iw_encode_ext *ext, u8 alg)
201 {
202         struct iwm_key *key = &iwm->keys[key_idx];
203
204         memset(key, 0, sizeof(struct iwm_key));
205         memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
206         key->hdr.key_idx = key_idx;
207         if (is_broadcast_ether_addr(ext->addr.sa_data))
208                 key->hdr.multicast = 1;
209
210         key->in_use = in_use;
211         key->flags = ext->ext_flags;
212         key->alg = alg;
213         key->key_len = ext->key_len;
214         memcpy(key->key, ext->key, ext->key_len);
215
216         return key;
217 }
218
219 static int iwm_wext_giwrate(struct net_device *dev,
220                             struct iw_request_info *info,
221                             struct iw_param *rate, char *extra)
222 {
223         struct iwm_priv *iwm = ndev_to_iwm(dev);
224
225         rate->value = iwm->rate * 1000000;
226
227         return 0;
228 }
229
230 static int iwm_wext_siwencode(struct net_device *dev,
231                               struct iw_request_info *info,
232                               struct iw_point *erq, char *key_buf)
233 {
234         struct iwm_priv *iwm = ndev_to_iwm(dev);
235         struct iwm_key *uninitialized_var(key);
236         int idx, i, uninitialized_var(alg), remove = 0, ret;
237
238         IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
239         IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
240
241         if (!iwm->umac_profile) {
242                 IWM_ERR(iwm, "UMAC profile not allocated yet\n");
243                 return -ENODEV;
244         }
245
246         if (erq->length == WLAN_KEY_LEN_WEP40) {
247                 alg = UMAC_CIPHER_TYPE_WEP_40;
248                 iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
249                 iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
250         } else if (erq->length == WLAN_KEY_LEN_WEP104) {
251                 alg = UMAC_CIPHER_TYPE_WEP_104;
252                 iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
253                 iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
254         }
255
256         if (erq->flags & IW_ENCODE_RESTRICTED)
257                 iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
258         else
259                 iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
260
261         idx = erq->flags & IW_ENCODE_INDEX;
262         if (idx == 0) {
263                 if (iwm->default_key)
264                         for (i = 0; i < IWM_NUM_KEYS; i++) {
265                                 if (iwm->default_key == &iwm->keys[i]) {
266                                         idx = i;
267                                         break;
268                                 }
269                         }
270                 else
271                         iwm->default_key = &iwm->keys[idx];
272         } else if (idx < 1 || idx > 4) {
273                 return -EINVAL;
274         } else
275                 idx--;
276
277         if (erq->flags & IW_ENCODE_DISABLED)
278                 remove = 1;
279         else if (erq->length == 0) {
280                 if (!iwm->keys[idx].in_use)
281                         return -EINVAL;
282                 iwm->default_key = &iwm->keys[idx];
283         }
284
285         if (erq->length) {
286                 key = &iwm->keys[idx];
287                 memset(key, 0, sizeof(struct iwm_key));
288                 memset(key->hdr.mac, 0xff, ETH_ALEN);
289                 key->hdr.key_idx = idx;
290                 key->hdr.multicast = 1;
291                 key->in_use = !remove;
292                 key->alg = alg;
293                 key->key_len = erq->length;
294                 memcpy(key->key, key_buf, erq->length);
295
296                 IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
297                              idx, !!iwm->default_key);
298         }
299
300         if (remove) {
301                 if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
302                         int j;
303                         for (j = 0; j < IWM_NUM_KEYS; j++)
304                                 if (iwm->keys[j].in_use) {
305                                         struct iwm_key *k = &iwm->keys[j];
306
307                                         k->in_use = 0;
308                                         ret = iwm_set_key(iwm, remove, 0, k);
309                                         if (ret < 0)
310                                                 return ret;
311                                 }
312
313                         iwm->umac_profile->sec.ucast_cipher =
314                                                         UMAC_CIPHER_TYPE_NONE;
315                         iwm->umac_profile->sec.mcast_cipher =
316                                                         UMAC_CIPHER_TYPE_NONE;
317                         iwm->umac_profile->sec.auth_type =
318                                                         UMAC_AUTH_TYPE_OPEN;
319
320                         return 0;
321                 } else {
322                         key->in_use = 0;
323                         return iwm_set_key(iwm, remove, 0, key);
324                 }
325         }
326
327         /*
328          * If we havent set a profile yet, we cant set keys.
329          * Keys will be pushed after we're associated.
330          */
331         if (!iwm->umac_profile_active)
332                 return 0;
333
334         /*
335          * If there is a current active profile, but no
336          * default key, it's not worth trying to associate again.
337          */
338         if (!iwm->default_key)
339                 return 0;
340
341         /*
342          * Here we have an active profile, but a key setting changed.
343          * We thus have to invalidate the current profile, and push the
344          * new one. Keys will be pushed when association takes place.
345          */
346         ret = iwm_invalidate_mlme_profile(iwm);
347         if (ret < 0) {
348                 IWM_ERR(iwm, "Couldn't invalidate profile\n");
349                 return ret;
350         }
351
352         return iwm_send_mlme_profile(iwm);
353 }
354
355 static int iwm_wext_giwencode(struct net_device *dev,
356                               struct iw_request_info *info,
357                               struct iw_point *erq, char *key)
358 {
359         struct iwm_priv *iwm = ndev_to_iwm(dev);
360         int idx, i;
361
362         idx = erq->flags & IW_ENCODE_INDEX;
363         if (idx < 1 || idx > 4) {
364                 idx = -1;
365                 if (!iwm->default_key) {
366                         erq->length = 0;
367                         erq->flags |= IW_ENCODE_NOKEY;
368                         return 0;
369                 } else
370                         for (i = 0; i < IWM_NUM_KEYS; i++) {
371                                 if (iwm->default_key == &iwm->keys[i]) {
372                                         idx = i;
373                                         break;
374                                 }
375                         }
376                 if (idx < 0)
377                         return -EINVAL;
378         } else
379                 idx--;
380
381         erq->flags = idx + 1;
382
383         if (!iwm->keys[idx].in_use) {
384                 erq->length = 0;
385                 erq->flags |= IW_ENCODE_DISABLED;
386                 return 0;
387         }
388
389         memcpy(key, iwm->keys[idx].key,
390                min_t(int, erq->length, iwm->keys[idx].key_len));
391         erq->length = iwm->keys[idx].key_len;
392         erq->flags |= IW_ENCODE_ENABLED;
393
394         if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
395                 switch (iwm->umac_profile->sec.auth_type) {
396                 case UMAC_AUTH_TYPE_OPEN:
397                         erq->flags |= IW_ENCODE_OPEN;
398                         break;
399                 default:
400                         erq->flags |= IW_ENCODE_RESTRICTED;
401                         break;
402                 }
403         }
404
405         return 0;
406 }
407
408 static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
409 {
410         if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
411                 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
412         else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
413                 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
414         else
415                 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
416
417         return 0;
418 }
419
420 static int iwm_wext_siwpower(struct net_device *dev,
421                              struct iw_request_info *info,
422                              struct iw_param *wrq, char *extra)
423 {
424         struct iwm_priv *iwm = ndev_to_iwm(dev);
425         u32 power_index;
426
427         if (wrq->disabled) {
428                 power_index = IWM_POWER_INDEX_MIN;
429                 goto set;
430         } else
431                 power_index = IWM_POWER_INDEX_DEFAULT;
432
433         switch (wrq->flags & IW_POWER_MODE) {
434         case IW_POWER_ON:
435         case IW_POWER_MODE:
436         case IW_POWER_ALL_R:
437                 break;
438         default:
439                 return -EINVAL;
440         }
441
442  set:
443         if (power_index == iwm->conf.power_index)
444                 return 0;
445
446         iwm->conf.power_index = power_index;
447
448         return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
449                                        CFG_POWER_INDEX, iwm->conf.power_index);
450 }
451
452 static int iwm_wext_giwpower(struct net_device *dev,
453                              struct iw_request_info *info,
454                              union iwreq_data *wrqu, char *extra)
455 {
456         struct iwm_priv *iwm = ndev_to_iwm(dev);
457
458         wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
459
460         return 0;
461 }
462
463 static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
464 {
465         u8 *auth_type = &iwm->umac_profile->sec.auth_type;
466
467         if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
468                 *auth_type = UMAC_AUTH_TYPE_8021X;
469         else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
470                 if (iwm->umac_profile->sec.flags &
471                     (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
472                         *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
473                 else
474                         *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
475         } else {
476                 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
477                 return -EINVAL;
478         }
479
480         return 0;
481 }
482
483 static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
484 {
485         u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
486                 &iwm->umac_profile->sec.mcast_cipher;
487
488         switch (cipher) {
489         case IW_AUTH_CIPHER_NONE:
490                 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
491                 break;
492         case IW_AUTH_CIPHER_WEP40:
493                 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
494                 break;
495         case IW_AUTH_CIPHER_TKIP:
496                 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
497                 break;
498         case IW_AUTH_CIPHER_CCMP:
499                 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
500                 break;
501         case IW_AUTH_CIPHER_WEP104:
502                 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
503                 break;
504         default:
505                 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
506                 return -ENOTSUPP;
507         }
508
509         return 0;
510 }
511
512 static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
513 {
514         u8 *auth_type = &iwm->umac_profile->sec.auth_type;
515
516         switch (auth_alg) {
517         case IW_AUTH_ALG_OPEN_SYSTEM:
518                 *auth_type = UMAC_AUTH_TYPE_OPEN;
519                 break;
520         case IW_AUTH_ALG_SHARED_KEY:
521                 if (iwm->umac_profile->sec.flags &
522                     (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
523                         if (*auth_type == UMAC_AUTH_TYPE_8021X)
524                                 return -EINVAL;
525                         *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
526                 } else {
527                         *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
528                 }
529                 break;
530         case IW_AUTH_ALG_LEAP:
531         default:
532                 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
533                 return -ENOTSUPP;
534         }
535
536         return 0;
537 }
538
539 static int iwm_wext_siwauth(struct net_device *dev,
540                             struct iw_request_info *info,
541                             struct iw_param *data, char *extra)
542 {
543         struct iwm_priv *iwm = ndev_to_iwm(dev);
544         int ret;
545
546         if ((data->flags) &
547             (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
548              IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
549                 /* We need to invalidate the current profile */
550                 if (iwm->umac_profile_active) {
551                         ret = iwm_invalidate_mlme_profile(iwm);
552                         if (ret < 0) {
553                                 IWM_ERR(iwm, "Couldn't invalidate profile\n");
554                                 return ret;
555                         }
556                 }
557         }
558
559         switch (data->flags & IW_AUTH_INDEX) {
560         case IW_AUTH_WPA_VERSION:
561                 return iwm_set_wpa_version(iwm, data->value);
562                 break;
563         case IW_AUTH_CIPHER_PAIRWISE:
564                 return iwm_set_cipher(iwm, data->value, 1);
565                 break;
566         case IW_AUTH_CIPHER_GROUP:
567                 return iwm_set_cipher(iwm, data->value, 0);
568                 break;
569         case IW_AUTH_KEY_MGMT:
570                 return iwm_set_key_mgt(iwm, data->value);
571                 break;
572         case IW_AUTH_80211_AUTH_ALG:
573                 return iwm_set_auth_alg(iwm, data->value);
574                 break;
575         default:
576                 return -ENOTSUPP;
577         }
578
579         return 0;
580 }
581
582 static int iwm_wext_giwauth(struct net_device *dev,
583                             struct iw_request_info *info,
584                             struct iw_param *data, char *extra)
585 {
586         return 0;
587 }
588
589 static int iwm_wext_siwencodeext(struct net_device *dev,
590                                  struct iw_request_info *info,
591                                  struct iw_point *erq, char *extra)
592 {
593         struct iwm_priv *iwm = ndev_to_iwm(dev);
594         struct iwm_key *key;
595         struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
596         int uninitialized_var(alg), idx, i, remove = 0;
597
598         IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
599         IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
600         IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
601         IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
602         IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
603
604         switch (ext->alg) {
605         case IW_ENCODE_ALG_NONE:
606                 remove = 1;
607                 break;
608         case IW_ENCODE_ALG_WEP:
609                 if (ext->key_len == WLAN_KEY_LEN_WEP40)
610                         alg = UMAC_CIPHER_TYPE_WEP_40;
611                 else if (ext->key_len == WLAN_KEY_LEN_WEP104)
612                         alg = UMAC_CIPHER_TYPE_WEP_104;
613                 else {
614                         IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
615                         return -EINVAL;
616                 }
617
618                 break;
619         case IW_ENCODE_ALG_TKIP:
620                 alg = UMAC_CIPHER_TYPE_TKIP;
621                 break;
622         case IW_ENCODE_ALG_CCMP:
623                 alg = UMAC_CIPHER_TYPE_CCMP;
624                 break;
625         default:
626                 return -EOPNOTSUPP;
627         }
628
629         idx = erq->flags & IW_ENCODE_INDEX;
630
631         if (idx == 0) {
632                 if (iwm->default_key)
633                         for (i = 0; i < IWM_NUM_KEYS; i++) {
634                                 if (iwm->default_key == &iwm->keys[i]) {
635                                         idx = i;
636                                         break;
637                                 }
638                         }
639         } else if (idx < 1 || idx > 4) {
640                 return -EINVAL;
641         } else
642                 idx--;
643
644         if (erq->flags & IW_ENCODE_DISABLED)
645                 remove = 1;
646         else if ((erq->length == 0) ||
647                  (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
648                 iwm->default_key = &iwm->keys[idx];
649                 if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
650                         return iwm_set_tx_key(iwm, idx);
651         }
652
653         key = iwm_key_init(iwm, idx, !remove, ext, alg);
654
655         return iwm_set_key(iwm, remove, !iwm->default_key, key);
656 }
657
658 static const iw_handler iwm_handlers[] =
659 {
660         (iw_handler) NULL,                              /* SIOCSIWCOMMIT */
661         (iw_handler) cfg80211_wext_giwname,             /* SIOCGIWNAME */
662         (iw_handler) NULL,                              /* SIOCSIWNWID */
663         (iw_handler) NULL,                              /* SIOCGIWNWID */
664         (iw_handler) iwm_wext_siwfreq,                  /* SIOCSIWFREQ */
665         (iw_handler) iwm_wext_giwfreq,                  /* SIOCGIWFREQ */
666         (iw_handler) cfg80211_wext_siwmode,             /* SIOCSIWMODE */
667         (iw_handler) cfg80211_wext_giwmode,             /* SIOCGIWMODE */
668         (iw_handler) NULL,                              /* SIOCSIWSENS */
669         (iw_handler) NULL,                              /* SIOCGIWSENS */
670         (iw_handler) NULL /* not used */,               /* SIOCSIWRANGE */
671         (iw_handler) cfg80211_wext_giwrange,            /* SIOCGIWRANGE */
672         (iw_handler) NULL /* not used */,               /* SIOCSIWPRIV */
673         (iw_handler) NULL /* kernel code */,            /* SIOCGIWPRIV */
674         (iw_handler) NULL /* not used */,               /* SIOCSIWSTATS */
675         (iw_handler) NULL /* kernel code */,            /* SIOCGIWSTATS */
676         (iw_handler) NULL,                              /* SIOCSIWSPY */
677         (iw_handler) NULL,                              /* SIOCGIWSPY */
678         (iw_handler) NULL,                              /* SIOCSIWTHRSPY */
679         (iw_handler) NULL,                              /* SIOCGIWTHRSPY */
680         (iw_handler) iwm_wext_siwap,                    /* SIOCSIWAP */
681         (iw_handler) iwm_wext_giwap,                    /* SIOCGIWAP */
682         (iw_handler) NULL,                              /* SIOCSIWMLME */
683         (iw_handler) NULL,                              /* SIOCGIWAPLIST */
684         (iw_handler) cfg80211_wext_siwscan,             /* SIOCSIWSCAN */
685         (iw_handler) cfg80211_wext_giwscan,             /* SIOCGIWSCAN */
686         (iw_handler) iwm_wext_siwessid,                 /* SIOCSIWESSID */
687         (iw_handler) iwm_wext_giwessid,                 /* SIOCGIWESSID */
688         (iw_handler) NULL,                              /* SIOCSIWNICKN */
689         (iw_handler) NULL,                              /* SIOCGIWNICKN */
690         (iw_handler) NULL,                              /* -- hole -- */
691         (iw_handler) NULL,                              /* -- hole -- */
692         (iw_handler) NULL,                              /* SIOCSIWRATE */
693         (iw_handler) iwm_wext_giwrate,                  /* SIOCGIWRATE */
694         (iw_handler) cfg80211_wext_siwrts,              /* SIOCSIWRTS */
695         (iw_handler) cfg80211_wext_giwrts,              /* SIOCGIWRTS */
696         (iw_handler) cfg80211_wext_siwfrag,             /* SIOCSIWFRAG */
697         (iw_handler) cfg80211_wext_giwfrag,             /* SIOCGIWFRAG */
698         (iw_handler) NULL,                              /* SIOCSIWTXPOW */
699         (iw_handler) NULL,                              /* SIOCGIWTXPOW */
700         (iw_handler) NULL,                              /* SIOCSIWRETRY */
701         (iw_handler) NULL,                              /* SIOCGIWRETRY */
702         (iw_handler) iwm_wext_siwencode,                /* SIOCSIWENCODE */
703         (iw_handler) iwm_wext_giwencode,                /* SIOCGIWENCODE */
704         (iw_handler) iwm_wext_siwpower,                 /* SIOCSIWPOWER */
705         (iw_handler) iwm_wext_giwpower,                 /* SIOCGIWPOWER */
706         (iw_handler) NULL,                              /* -- hole -- */
707         (iw_handler) NULL,                              /* -- hole -- */
708         (iw_handler) NULL,                              /* SIOCSIWGENIE */
709         (iw_handler) NULL,                              /* SIOCGIWGENIE */
710         (iw_handler) iwm_wext_siwauth,                  /* SIOCSIWAUTH */
711         (iw_handler) iwm_wext_giwauth,                  /* SIOCGIWAUTH */
712         (iw_handler) iwm_wext_siwencodeext,             /* SIOCSIWENCODEEXT */
713         (iw_handler) NULL,                              /* SIOCGIWENCODEEXT */
714         (iw_handler) NULL,                              /* SIOCSIWPMKSA */
715         (iw_handler) NULL,                              /* -- hole -- */
716 };
717
718 const struct iw_handler_def iwm_iw_handler_def = {
719         .num_standard   = ARRAY_SIZE(iwm_handlers),
720         .standard       = (iw_handler *) iwm_handlers,
721         .get_wireless_stats = iwm_get_wireless_stats,
722 };
723