973457383c113af04e81687fcfe86a1f97555be0
[pandora-kernel.git] / 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         int ret;
86
87         IWM_DBG_WEXT(iwm, DBG, "Set BSSID: %pM\n", ap_addr->sa_data);
88
89         if (iwm->conf.mode == UMAC_MODE_IBSS)
90                 return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
91
92         if (!test_bit(IWM_STATUS_READY, &iwm->status))
93                 return -EIO;
94
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;
101         } else {
102                 IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
103                              ap_addr->sa_data);
104                 memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
105                        ETH_ALEN);
106                 iwm->umac_profile->bss_num = 1;
107         }
108
109         if (iwm->umac_profile_active) {
110                 int i;
111
112                 if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
113                         return 0;
114
115                 /*
116                  * If we're clearing the BSSID, and we're associated,
117                  * we have to clear the keys as they're no longer valid.
118                  */
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;
122                 }
123
124                 ret = iwm_invalidate_mlme_profile(iwm);
125                 if (ret < 0) {
126                         IWM_ERR(iwm, "Couldn't invalidate profile\n");
127                         return ret;
128                 }
129         }
130
131         if (iwm->umac_profile->ssid.ssid_len)
132                 return iwm_send_mlme_profile(iwm);
133
134         return 0;
135 }
136
137 static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
138                           struct sockaddr *ap_addr, char *extra)
139 {
140         struct iwm_priv *iwm = ndev_to_iwm(dev);
141
142         switch (iwm->conf.mode) {
143         case UMAC_MODE_IBSS:
144                 return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
145         case UMAC_MODE_BSS:
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);
149                 } else
150                         memset(&ap_addr->sa_data, 0, ETH_ALEN);
151                 break;
152         default:
153                 return -EOPNOTSUPP;
154         }
155
156         return 0;
157 }
158
159 static int iwm_wext_siwessid(struct net_device *dev,
160                              struct iw_request_info *info,
161                              struct iw_point *data, char *ssid)
162 {
163         struct iwm_priv *iwm = ndev_to_iwm(dev);
164         size_t len = data->length;
165         int ret;
166
167         IWM_DBG_WEXT(iwm, DBG, "Set ESSID: >%s<\n", ssid);
168
169         if (iwm->conf.mode == UMAC_MODE_IBSS)
170                 return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
171
172         if (!test_bit(IWM_STATUS_READY, &iwm->status))
173                 return -EIO;
174
175         if (len > 0 && ssid[len - 1] == '\0')
176                 len--;
177
178         if (iwm->umac_profile_active) {
179                 if (iwm->umac_profile->ssid.ssid_len == len &&
180                     !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
181                         return 0;
182
183                 ret = iwm_invalidate_mlme_profile(iwm);
184                 if (ret < 0) {
185                         IWM_ERR(iwm, "Couldn't invalidate profile\n");
186                         return ret;
187                 }
188         }
189
190         iwm->umac_profile->ssid.ssid_len = len;
191         memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
192
193         return iwm_send_mlme_profile(iwm);
194 }
195
196 static int iwm_wext_giwessid(struct net_device *dev,
197                              struct iw_request_info *info,
198                              struct iw_point *data, char *ssid)
199 {
200         struct iwm_priv *iwm = ndev_to_iwm(dev);
201
202         if (iwm->conf.mode == UMAC_MODE_IBSS)
203                 return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
204
205         if (!test_bit(IWM_STATUS_READY, &iwm->status))
206                 return -EIO;
207
208         data->length = iwm->umac_profile->ssid.ssid_len;
209         if (data->length) {
210                 memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
211                 data->flags = 1;
212         } else
213                 data->flags = 0;
214
215         return 0;
216 }
217
218 static int iwm_wext_giwrate(struct net_device *dev,
219                             struct iw_request_info *info,
220                             struct iw_param *rate, char *extra)
221 {
222         struct iwm_priv *iwm = ndev_to_iwm(dev);
223
224         rate->value = iwm->rate * 1000000;
225
226         return 0;
227 }
228
229 static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
230 {
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;
235         else
236                 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
237
238         return 0;
239 }
240
241 static int iwm_wext_siwpower(struct net_device *dev,
242                              struct iw_request_info *info,
243                              struct iw_param *wrq, char *extra)
244 {
245         struct iwm_priv *iwm = ndev_to_iwm(dev);
246         u32 power_index;
247
248         if (wrq->disabled) {
249                 power_index = IWM_POWER_INDEX_MIN;
250                 goto set;
251         } else
252                 power_index = IWM_POWER_INDEX_DEFAULT;
253
254         switch (wrq->flags & IW_POWER_MODE) {
255         case IW_POWER_ON:
256         case IW_POWER_MODE:
257         case IW_POWER_ALL_R:
258                 break;
259         default:
260                 return -EINVAL;
261         }
262
263  set:
264         if (power_index == iwm->conf.power_index)
265                 return 0;
266
267         iwm->conf.power_index = power_index;
268
269         return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
270                                        CFG_POWER_INDEX, iwm->conf.power_index);
271 }
272
273 static int iwm_wext_giwpower(struct net_device *dev,
274                              struct iw_request_info *info,
275                              union iwreq_data *wrqu, char *extra)
276 {
277         struct iwm_priv *iwm = ndev_to_iwm(dev);
278
279         wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
280
281         return 0;
282 }
283
284 static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
285 {
286         u8 *auth_type = &iwm->umac_profile->sec.auth_type;
287
288         IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
289
290         if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
291                 *auth_type = UMAC_AUTH_TYPE_8021X;
292         else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
293                 if (iwm->umac_profile->sec.flags &
294                     (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
295                         *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
296                 else
297                         *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
298         } else {
299                 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
300                 return -EINVAL;
301         }
302
303         return 0;
304 }
305
306 static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
307 {
308         u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
309                 &iwm->umac_profile->sec.mcast_cipher;
310
311         switch (cipher) {
312         case IW_AUTH_CIPHER_NONE:
313                 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
314                 break;
315         case IW_AUTH_CIPHER_WEP40:
316                 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
317                 break;
318         case IW_AUTH_CIPHER_TKIP:
319                 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
320                 break;
321         case IW_AUTH_CIPHER_CCMP:
322                 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
323                 break;
324         case IW_AUTH_CIPHER_WEP104:
325                 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
326                 break;
327         default:
328                 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
329                 return -ENOTSUPP;
330         }
331
332         return 0;
333 }
334
335 static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
336 {
337         u8 *auth_type = &iwm->umac_profile->sec.auth_type;
338
339         IWM_DBG_WEXT(iwm, DBG, "auth_alg: 0x%x\n", auth_alg);
340
341         switch (auth_alg) {
342         case IW_AUTH_ALG_OPEN_SYSTEM:
343                 *auth_type = UMAC_AUTH_TYPE_OPEN;
344                 break;
345         case IW_AUTH_ALG_SHARED_KEY:
346                 if (iwm->umac_profile->sec.flags &
347                     (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
348                         if (*auth_type == UMAC_AUTH_TYPE_8021X)
349                                 return -EINVAL;
350                         *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
351                 } else {
352                         IWM_DBG_WEXT(iwm, DBG, "WEP shared key\n");
353                         *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
354                 }
355                 break;
356         case IW_AUTH_ALG_LEAP:
357         default:
358                 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
359                 return -ENOTSUPP;
360         }
361
362         return 0;
363 }
364
365 static int iwm_wext_siwauth(struct net_device *dev,
366                             struct iw_request_info *info,
367                             struct iw_param *data, char *extra)
368 {
369         struct iwm_priv *iwm = ndev_to_iwm(dev);
370         int ret;
371
372         if ((data->flags) &
373             (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
374              IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
375                 /* We need to invalidate the current profile */
376                 if (iwm->umac_profile_active) {
377                         ret = iwm_invalidate_mlme_profile(iwm);
378                         if (ret < 0) {
379                                 IWM_ERR(iwm, "Couldn't invalidate profile\n");
380                                 return ret;
381                         }
382                 }
383         }
384
385         switch (data->flags & IW_AUTH_INDEX) {
386         case IW_AUTH_WPA_VERSION:
387                 return iwm_set_wpa_version(iwm, data->value);
388                 break;
389         case IW_AUTH_CIPHER_PAIRWISE:
390                 return iwm_set_cipher(iwm, data->value, 1);
391                 break;
392         case IW_AUTH_CIPHER_GROUP:
393                 return iwm_set_cipher(iwm, data->value, 0);
394                 break;
395         case IW_AUTH_KEY_MGMT:
396                 return iwm_set_key_mgt(iwm, data->value);
397                 break;
398         case IW_AUTH_80211_AUTH_ALG:
399                 return iwm_set_auth_alg(iwm, data->value);
400                 break;
401         default:
402                 return -ENOTSUPP;
403         }
404
405         return 0;
406 }
407
408 static int iwm_wext_giwauth(struct net_device *dev,
409                             struct iw_request_info *info,
410                             struct iw_param *data, char *extra)
411 {
412         return 0;
413 }
414
415 static const iw_handler iwm_handlers[] =
416 {
417         (iw_handler) NULL,                              /* SIOCSIWCOMMIT */
418         (iw_handler) cfg80211_wext_giwname,             /* SIOCGIWNAME */
419         (iw_handler) NULL,                              /* SIOCSIWNWID */
420         (iw_handler) NULL,                              /* SIOCGIWNWID */
421         (iw_handler) iwm_wext_siwfreq,                  /* SIOCSIWFREQ */
422         (iw_handler) iwm_wext_giwfreq,                  /* SIOCGIWFREQ */
423         (iw_handler) cfg80211_wext_siwmode,             /* SIOCSIWMODE */
424         (iw_handler) cfg80211_wext_giwmode,             /* SIOCGIWMODE */
425         (iw_handler) NULL,                              /* SIOCSIWSENS */
426         (iw_handler) NULL,                              /* SIOCGIWSENS */
427         (iw_handler) NULL /* not used */,               /* SIOCSIWRANGE */
428         (iw_handler) cfg80211_wext_giwrange,            /* SIOCGIWRANGE */
429         (iw_handler) NULL /* not used */,               /* SIOCSIWPRIV */
430         (iw_handler) NULL /* kernel code */,            /* SIOCGIWPRIV */
431         (iw_handler) NULL /* not used */,               /* SIOCSIWSTATS */
432         (iw_handler) NULL /* kernel code */,            /* SIOCGIWSTATS */
433         (iw_handler) NULL,                              /* SIOCSIWSPY */
434         (iw_handler) NULL,                              /* SIOCGIWSPY */
435         (iw_handler) NULL,                              /* SIOCSIWTHRSPY */
436         (iw_handler) NULL,                              /* SIOCGIWTHRSPY */
437         (iw_handler) iwm_wext_siwap,                    /* SIOCSIWAP */
438         (iw_handler) iwm_wext_giwap,                    /* SIOCGIWAP */
439         (iw_handler) NULL,                              /* SIOCSIWMLME */
440         (iw_handler) NULL,                              /* SIOCGIWAPLIST */
441         (iw_handler) cfg80211_wext_siwscan,             /* SIOCSIWSCAN */
442         (iw_handler) cfg80211_wext_giwscan,             /* SIOCGIWSCAN */
443         (iw_handler) iwm_wext_siwessid,                 /* SIOCSIWESSID */
444         (iw_handler) iwm_wext_giwessid,                 /* SIOCGIWESSID */
445         (iw_handler) NULL,                              /* SIOCSIWNICKN */
446         (iw_handler) NULL,                              /* SIOCGIWNICKN */
447         (iw_handler) NULL,                              /* -- hole -- */
448         (iw_handler) NULL,                              /* -- hole -- */
449         (iw_handler) NULL,                              /* SIOCSIWRATE */
450         (iw_handler) iwm_wext_giwrate,                  /* SIOCGIWRATE */
451         (iw_handler) cfg80211_wext_siwrts,              /* SIOCSIWRTS */
452         (iw_handler) cfg80211_wext_giwrts,              /* SIOCGIWRTS */
453         (iw_handler) cfg80211_wext_siwfrag,             /* SIOCSIWFRAG */
454         (iw_handler) cfg80211_wext_giwfrag,             /* SIOCGIWFRAG */
455         (iw_handler) cfg80211_wext_siwtxpower,          /* SIOCSIWTXPOW */
456         (iw_handler) cfg80211_wext_giwtxpower,          /* SIOCGIWTXPOW */
457         (iw_handler) NULL,                              /* SIOCSIWRETRY */
458         (iw_handler) NULL,                              /* SIOCGIWRETRY */
459         (iw_handler) cfg80211_wext_siwencode,           /* SIOCSIWENCODE */
460         (iw_handler) cfg80211_wext_giwencode,           /* SIOCGIWENCODE */
461         (iw_handler) iwm_wext_siwpower,                 /* SIOCSIWPOWER */
462         (iw_handler) iwm_wext_giwpower,                 /* SIOCGIWPOWER */
463         (iw_handler) NULL,                              /* -- hole -- */
464         (iw_handler) NULL,                              /* -- hole -- */
465         (iw_handler) NULL,                              /* SIOCSIWGENIE */
466         (iw_handler) NULL,                              /* SIOCGIWGENIE */
467         (iw_handler) iwm_wext_siwauth,                  /* SIOCSIWAUTH */
468         (iw_handler) iwm_wext_giwauth,                  /* SIOCGIWAUTH */
469         (iw_handler) cfg80211_wext_siwencodeext,        /* SIOCSIWENCODEEXT */
470         (iw_handler) NULL,                              /* SIOCGIWENCODEEXT */
471         (iw_handler) NULL,                              /* SIOCSIWPMKSA */
472         (iw_handler) NULL,                              /* -- hole -- */
473 };
474
475 const struct iw_handler_def iwm_iw_handler_def = {
476         .num_standard   = ARRAY_SIZE(iwm_handlers),
477         .standard       = (iw_handler *) iwm_handlers,
478         .get_wireless_stats = iwm_get_wireless_stats,
479 };
480