blob: a1d45cce0ebcab1f7b92568cafacf0dea4537014 [file] [log] [blame]
Zhu Yibb9f8692009-05-21 21:20:45 +08001/*
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>
Alexey Dobriyand43c36d2009-10-07 17:09:06 +040026#include <linux/sched.h>
Samuel Ortiz13e0fe702009-06-15 21:59:52 +020027#include <linux/etherdevice.h>
Zhu Yibb9f8692009-05-21 21:20:45 +080028#include <linux/wireless.h>
29#include <linux/ieee80211.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Zhu Yibb9f8692009-05-21 21:20:45 +080031#include <net/cfg80211.h>
32
33#include "iwm.h"
34#include "commands.h"
35#include "cfg80211.h"
36#include "debug.h"
37
38#define RATETAB_ENT(_rate, _rateid, _flags) \
39 { \
40 .bitrate = (_rate), \
41 .hw_value = (_rateid), \
42 .flags = (_flags), \
43 }
44
45#define CHAN2G(_channel, _freq, _flags) { \
46 .band = IEEE80211_BAND_2GHZ, \
47 .center_freq = (_freq), \
48 .hw_value = (_channel), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
54#define CHAN5G(_channel, _flags) { \
55 .band = IEEE80211_BAND_5GHZ, \
56 .center_freq = 5000 + (5 * (_channel)), \
57 .hw_value = (_channel), \
58 .flags = (_flags), \
59 .max_antenna_gain = 0, \
60 .max_power = 30, \
61}
62
63static struct ieee80211_rate iwm_rates[] = {
64 RATETAB_ENT(10, 0x1, 0),
65 RATETAB_ENT(20, 0x2, 0),
66 RATETAB_ENT(55, 0x4, 0),
67 RATETAB_ENT(110, 0x8, 0),
68 RATETAB_ENT(60, 0x10, 0),
69 RATETAB_ENT(90, 0x20, 0),
70 RATETAB_ENT(120, 0x40, 0),
71 RATETAB_ENT(180, 0x80, 0),
72 RATETAB_ENT(240, 0x100, 0),
73 RATETAB_ENT(360, 0x200, 0),
74 RATETAB_ENT(480, 0x400, 0),
75 RATETAB_ENT(540, 0x800, 0),
76};
77
78#define iwm_a_rates (iwm_rates + 4)
79#define iwm_a_rates_size 8
80#define iwm_g_rates (iwm_rates + 0)
81#define iwm_g_rates_size 12
82
83static struct ieee80211_channel iwm_2ghz_channels[] = {
84 CHAN2G(1, 2412, 0),
85 CHAN2G(2, 2417, 0),
86 CHAN2G(3, 2422, 0),
87 CHAN2G(4, 2427, 0),
88 CHAN2G(5, 2432, 0),
89 CHAN2G(6, 2437, 0),
90 CHAN2G(7, 2442, 0),
91 CHAN2G(8, 2447, 0),
92 CHAN2G(9, 2452, 0),
93 CHAN2G(10, 2457, 0),
94 CHAN2G(11, 2462, 0),
95 CHAN2G(12, 2467, 0),
96 CHAN2G(13, 2472, 0),
97 CHAN2G(14, 2484, 0),
98};
99
100static struct ieee80211_channel iwm_5ghz_a_channels[] = {
101 CHAN5G(34, 0), CHAN5G(36, 0),
102 CHAN5G(38, 0), CHAN5G(40, 0),
103 CHAN5G(42, 0), CHAN5G(44, 0),
104 CHAN5G(46, 0), CHAN5G(48, 0),
105 CHAN5G(52, 0), CHAN5G(56, 0),
106 CHAN5G(60, 0), CHAN5G(64, 0),
107 CHAN5G(100, 0), CHAN5G(104, 0),
108 CHAN5G(108, 0), CHAN5G(112, 0),
109 CHAN5G(116, 0), CHAN5G(120, 0),
110 CHAN5G(124, 0), CHAN5G(128, 0),
111 CHAN5G(132, 0), CHAN5G(136, 0),
112 CHAN5G(140, 0), CHAN5G(149, 0),
113 CHAN5G(153, 0), CHAN5G(157, 0),
114 CHAN5G(161, 0), CHAN5G(165, 0),
115 CHAN5G(184, 0), CHAN5G(188, 0),
116 CHAN5G(192, 0), CHAN5G(196, 0),
117 CHAN5G(200, 0), CHAN5G(204, 0),
118 CHAN5G(208, 0), CHAN5G(212, 0),
119 CHAN5G(216, 0),
120};
121
122static struct ieee80211_supported_band iwm_band_2ghz = {
123 .channels = iwm_2ghz_channels,
124 .n_channels = ARRAY_SIZE(iwm_2ghz_channels),
125 .bitrates = iwm_g_rates,
126 .n_bitrates = iwm_g_rates_size,
127};
128
129static struct ieee80211_supported_band iwm_band_5ghz = {
130 .channels = iwm_5ghz_a_channels,
131 .n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
132 .bitrates = iwm_a_rates,
133 .n_bitrates = iwm_a_rates_size,
134};
135
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200136static int iwm_key_init(struct iwm_key *key, u8 key_index,
137 const u8 *mac_addr, struct key_params *params)
138{
139 key->hdr.key_idx = key_index;
140 if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
141 key->hdr.multicast = 1;
142 memset(key->hdr.mac, 0xff, ETH_ALEN);
143 } else {
144 key->hdr.multicast = 0;
145 memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
146 }
147
148 if (params) {
149 if (params->key_len > WLAN_MAX_KEY_LEN ||
150 params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
151 return -EINVAL;
152
153 key->cipher = params->cipher;
154 key->key_len = params->key_len;
155 key->seq_len = params->seq_len;
156 memcpy(key->key, params->key, key->key_len);
157 memcpy(key->seq, params->seq, key->seq_len);
158 }
159
160 return 0;
161}
162
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200163static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
164 u8 key_index, const u8 *mac_addr,
165 struct key_params *params)
166{
167 struct iwm_priv *iwm = ndev_to_iwm(ndev);
168 struct iwm_key *key = &iwm->keys[key_index];
169 int ret;
170
171 IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
172
173 memset(key, 0, sizeof(struct iwm_key));
174 ret = iwm_key_init(key, key_index, mac_addr, params);
175 if (ret < 0) {
176 IWM_ERR(iwm, "Invalid key_params\n");
177 return ret;
178 }
179
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200180 return iwm_set_key(iwm, 0, key);
181}
182
183static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
184 u8 key_index, const u8 *mac_addr, void *cookie,
185 void (*callback)(void *cookie,
186 struct key_params*))
187{
188 struct iwm_priv *iwm = ndev_to_iwm(ndev);
189 struct iwm_key *key = &iwm->keys[key_index];
190 struct key_params params;
191
192 IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
193
194 memset(&params, 0, sizeof(params));
195
196 params.cipher = key->cipher;
197 params.key_len = key->key_len;
198 params.seq_len = key->seq_len;
199 params.seq = key->seq;
200 params.key = key->key;
201
202 callback(cookie, &params);
203
204 return key->key_len ? 0 : -ENOENT;
205}
206
207
208static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
209 u8 key_index, const u8 *mac_addr)
210{
211 struct iwm_priv *iwm = ndev_to_iwm(ndev);
212 struct iwm_key *key = &iwm->keys[key_index];
213
214 if (!iwm->keys[key_index].key_len) {
215 IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
216 return 0;
217 }
218
219 if (key_index == iwm->default_key)
220 iwm->default_key = -1;
221
222 return iwm_set_key(iwm, 1, key);
223}
224
225static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
226 struct net_device *ndev,
227 u8 key_index)
228{
229 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200230
231 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
232
233 if (!iwm->keys[key_index].key_len) {
234 IWM_ERR(iwm, "Key %d not used\n", key_index);
235 return -EINVAL;
236 }
237
Samuel Ortiz35497162009-06-15 21:59:54 +0200238 iwm->default_key = key_index;
239
Zhu Yi6e5db0a2009-07-16 17:34:13 +0800240 return iwm_set_tx_key(iwm, key_index);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200241}
242
Samuel Ortizd0418112009-09-01 15:14:00 +0200243static int iwm_cfg80211_get_station(struct wiphy *wiphy,
244 struct net_device *ndev,
245 u8 *mac, struct station_info *sinfo)
Samuel Ortiz9967d462009-07-16 17:34:10 +0800246{
247 struct iwm_priv *iwm = ndev_to_iwm(ndev);
248
249 if (memcmp(mac, iwm->bssid, ETH_ALEN))
250 return -ENOENT;
251
252 sinfo->filled |= STATION_INFO_TX_BITRATE;
253 sinfo->txrate.legacy = iwm->rate * 10;
254
255 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
256 sinfo->filled |= STATION_INFO_SIGNAL;
257 sinfo->signal = iwm->wstats.qual.level;
258 }
259
260 return 0;
261}
262
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200263
Zhu Yibb9f8692009-05-21 21:20:45 +0800264int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
265{
266 struct wiphy *wiphy = iwm_to_wiphy(iwm);
267 struct iwm_bss_info *bss, *next;
268 struct iwm_umac_notif_bss_info *umac_bss;
269 struct ieee80211_mgmt *mgmt;
270 struct ieee80211_channel *channel;
271 struct ieee80211_supported_band *band;
272 s32 signal;
273 int freq;
274
275 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
276 umac_bss = bss->bss;
277 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
278
279 if (umac_bss->band == UMAC_BAND_2GHZ)
280 band = wiphy->bands[IEEE80211_BAND_2GHZ];
281 else if (umac_bss->band == UMAC_BAND_5GHZ)
282 band = wiphy->bands[IEEE80211_BAND_5GHZ];
283 else {
284 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
285 return -EINVAL;
286 }
287
288 freq = ieee80211_channel_to_frequency(umac_bss->channel);
289 channel = ieee80211_get_channel(wiphy, freq);
290 signal = umac_bss->rssi * 100;
291
292 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
293 le16_to_cpu(umac_bss->frame_len),
294 signal, GFP_KERNEL))
295 return -EINVAL;
296 }
297
298 return 0;
299}
300
Johannes Berge36d56b2009-06-09 21:04:43 +0200301static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
302 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800303 enum nl80211_iftype type, u32 *flags,
304 struct vif_params *params)
305{
Zhu Yibb9f8692009-05-21 21:20:45 +0800306 struct wireless_dev *wdev;
307 struct iwm_priv *iwm;
308 u32 old_mode;
309
Zhu Yibb9f8692009-05-21 21:20:45 +0800310 wdev = ndev->ieee80211_ptr;
311 iwm = ndev_to_iwm(ndev);
312 old_mode = iwm->conf.mode;
313
314 switch (type) {
315 case NL80211_IFTYPE_STATION:
316 iwm->conf.mode = UMAC_MODE_BSS;
317 break;
318 case NL80211_IFTYPE_ADHOC:
319 iwm->conf.mode = UMAC_MODE_IBSS;
320 break;
321 default:
322 return -EOPNOTSUPP;
323 }
324
325 wdev->iftype = type;
326
327 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
328 return 0;
329
330 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
331
Zhu Yiae73abf2009-09-01 15:13:58 +0200332 if (iwm->umac_profile_active)
333 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800334
335 return 0;
336}
337
338static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
339 struct cfg80211_scan_request *request)
340{
341 struct iwm_priv *iwm = ndev_to_iwm(ndev);
342 int ret;
343
344 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
345 IWM_ERR(iwm, "Scan while device is not ready\n");
346 return -EIO;
347 }
348
349 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
350 IWM_ERR(iwm, "Scanning already\n");
351 return -EAGAIN;
352 }
353
354 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
355 IWM_ERR(iwm, "Scanning being aborted\n");
356 return -EAGAIN;
357 }
358
359 set_bit(IWM_STATUS_SCANNING, &iwm->status);
360
361 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
362 if (ret) {
363 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
364 return ret;
365 }
366
367 iwm->scan_request = request;
368 return 0;
369}
370
371static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
372{
373 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
374
375 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
376 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
377 int ret;
378
379 iwm->conf.rts_threshold = wiphy->rts_threshold;
380
381 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
382 CFG_RTS_THRESHOLD,
383 iwm->conf.rts_threshold);
384 if (ret < 0)
385 return ret;
386 }
387
388 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
389 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
390 int ret;
391
392 iwm->conf.frag_threshold = wiphy->frag_threshold;
393
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800394 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800395 CFG_FRAG_THRESHOLD,
396 iwm->conf.frag_threshold);
397 if (ret < 0)
398 return ret;
399 }
400
401 return 0;
402}
403
404static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
405 struct cfg80211_ibss_params *params)
406{
407 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
408 struct ieee80211_channel *chan = params->channel;
Zhu Yibb9f8692009-05-21 21:20:45 +0800409
410 if (!test_bit(IWM_STATUS_READY, &iwm->status))
411 return -EIO;
412
Zhu Yi03d1a622009-10-16 13:18:46 +0800413 /* UMAC doesn't support creating or joining an IBSS network
414 * with specified bssid. */
Zhu Yibb9f8692009-05-21 21:20:45 +0800415 if (params->bssid)
416 return -EOPNOTSUPP;
417
Zhu Yibb9f8692009-05-21 21:20:45 +0800418 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
419 iwm->umac_profile->ibss.band = chan->band;
420 iwm->umac_profile->ibss.channel = iwm->channel;
421 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
422 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
423
Zhu Yibb9f8692009-05-21 21:20:45 +0800424 return iwm_send_mlme_profile(iwm);
425}
426
427static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
428{
429 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
430
431 if (iwm->umac_profile_active)
432 return iwm_invalidate_mlme_profile(iwm);
433
434 return 0;
435}
436
Samuel Ortiz9967d462009-07-16 17:34:10 +0800437static int iwm_set_auth_type(struct iwm_priv *iwm,
438 enum nl80211_auth_type sme_auth_type)
439{
440 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
441
442 switch (sme_auth_type) {
443 case NL80211_AUTHTYPE_AUTOMATIC:
444 case NL80211_AUTHTYPE_OPEN_SYSTEM:
445 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
446 *auth_type = UMAC_AUTH_TYPE_OPEN;
447 break;
448 case NL80211_AUTHTYPE_SHARED_KEY:
449 if (iwm->umac_profile->sec.flags &
450 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
451 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
452 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
453 } else {
454 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
455 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
456 }
457
458 break;
459 default:
460 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
461 return -ENOTSUPP;
462 }
463
464 return 0;
465}
466
467static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
468{
Zhu Yi554503f2009-08-03 14:37:01 +0800469 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
470
Samuel Ortiz9967d462009-07-16 17:34:10 +0800471 if (!wpa_version) {
472 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
473 return 0;
474 }
475
Samuel Ortiz6a79c9f2009-10-16 13:18:49 +0800476 if (wpa_version & NL80211_WPA_VERSION_1)
477 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
478
Samuel Ortiz9967d462009-07-16 17:34:10 +0800479 if (wpa_version & NL80211_WPA_VERSION_2)
480 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
481
Samuel Ortiz9967d462009-07-16 17:34:10 +0800482 return 0;
483}
484
485static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
486{
487 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
488 &iwm->umac_profile->sec.mcast_cipher;
489
490 if (!cipher) {
491 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
492 return 0;
493 }
494
Zhu Yi554503f2009-08-03 14:37:01 +0800495 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
496 cipher);
497
Samuel Ortiz9967d462009-07-16 17:34:10 +0800498 switch (cipher) {
499 case IW_AUTH_CIPHER_NONE:
500 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
501 break;
502 case WLAN_CIPHER_SUITE_WEP40:
503 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
504 break;
505 case WLAN_CIPHER_SUITE_WEP104:
506 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
507 break;
508 case WLAN_CIPHER_SUITE_TKIP:
509 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
510 break;
511 case WLAN_CIPHER_SUITE_CCMP:
512 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
513 break;
514 default:
515 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
516 return -ENOTSUPP;
517 }
518
519 return 0;
520}
521
522static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
523{
524 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
525
526 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
527
528 if (key_mgt == WLAN_AKM_SUITE_8021X)
529 *auth_type = UMAC_AUTH_TYPE_8021X;
530 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
531 if (iwm->umac_profile->sec.flags &
532 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
533 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
534 else
535 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
536 } else {
537 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
538 return -EINVAL;
539 }
540
541 return 0;
542}
543
544
545static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
546 struct cfg80211_connect_params *sme)
547{
548 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
549 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200550 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800551 int ret;
552
553 if (!test_bit(IWM_STATUS_READY, &iwm->status))
554 return -EIO;
555
556 if (!sme->ssid)
557 return -EINVAL;
558
Zhu Yiae73abf2009-09-01 15:13:58 +0200559 if (iwm->umac_profile_active) {
560 ret = iwm_invalidate_mlme_profile(iwm);
561 if (ret) {
562 IWM_ERR(iwm, "Couldn't invalidate profile\n");
563 return ret;
564 }
565 }
566
Samuel Ortiz9967d462009-07-16 17:34:10 +0800567 if (chan)
568 iwm->channel =
569 ieee80211_frequency_to_channel(chan->center_freq);
570
571 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
572 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
573
574 if (sme->bssid) {
575 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
576 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
577 iwm->umac_profile->bss_num = 1;
578 } else {
579 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
580 iwm->umac_profile->bss_num = 0;
581 }
582
Zhu Yi554503f2009-08-03 14:37:01 +0800583 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800584 if (ret < 0)
585 return ret;
586
Zhu Yi554503f2009-08-03 14:37:01 +0800587 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800588 if (ret < 0)
589 return ret;
590
591 if (sme->crypto.n_ciphers_pairwise) {
592 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
593 true);
594 if (ret < 0)
595 return ret;
596 }
597
598 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
599 if (ret < 0)
600 return ret;
601
602 if (sme->crypto.n_akm_suites) {
603 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
604 if (ret < 0)
605 return ret;
606 }
607
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200608 /*
609 * We save the WEP key in case we want to do shared authentication.
610 * We have to do it so because UMAC will assert whenever it gets a
611 * key before a profile.
612 */
613 if (sme->key) {
614 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
615 if (key_param.key == NULL)
616 return -ENOMEM;
617 key_param.key_len = sme->key_len;
618 key_param.seq_len = 0;
619 key_param.cipher = sme->crypto.ciphers_pairwise[0];
620
621 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
622 NULL, &key_param);
623 kfree(key_param.key);
624 if (ret < 0) {
625 IWM_ERR(iwm, "Invalid key_params\n");
626 return ret;
627 }
628
629 iwm->default_key = sme->key_idx;
630 }
631
Samuel Ortiza82aedb2009-10-16 13:18:47 +0800632 /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
633 if ((iwm->umac_profile->sec.flags &
634 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
635 iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
636 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
637 }
638
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200639 ret = iwm_send_mlme_profile(iwm);
640
641 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
642 sme->key == NULL)
643 return ret;
644
645 /*
646 * We want to do shared auth.
647 * We need to actually set the key we previously cached,
648 * and then tell the UMAC it's the default one.
649 * That will trigger the auth+assoc UMAC machinery, and again,
650 * this must be done after setting the profile.
651 */
652 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
653 if (ret < 0)
654 return ret;
655
656 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800657}
658
659static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
660 u16 reason_code)
661{
662 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
663
664 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
665
666 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200667 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800668
669 return 0;
670}
671
Zhu Yi257862f2009-06-15 21:59:56 +0200672static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
673 enum tx_power_setting type, int dbm)
674{
Samuel Ortiz88e61952009-10-16 13:18:53 +0800675 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
676 int ret;
677
Zhu Yi257862f2009-06-15 21:59:56 +0200678 switch (type) {
679 case TX_POWER_AUTOMATIC:
680 return 0;
Samuel Ortiz88e61952009-10-16 13:18:53 +0800681 case TX_POWER_FIXED:
Samuel Ortizfe191762009-11-24 11:33:28 +0800682 if (!test_bit(IWM_STATUS_READY, &iwm->status))
683 return 0;
684
Samuel Ortiz88e61952009-10-16 13:18:53 +0800685 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
686 CFG_TX_PWR_LIMIT_USR, dbm * 2);
687 if (ret < 0)
688 return ret;
689
690 return iwm_tx_power_trigger(iwm);
Zhu Yi257862f2009-06-15 21:59:56 +0200691 default:
Samuel Ortizfe191762009-11-24 11:33:28 +0800692 IWM_ERR(iwm, "Unsupported power type: %d\n", type);
Zhu Yi257862f2009-06-15 21:59:56 +0200693 return -EOPNOTSUPP;
694 }
695
696 return 0;
697}
698
699static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
700{
701 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
702
Samuel Ortiz88e61952009-10-16 13:18:53 +0800703 *dbm = iwm->txpower >> 1;
Zhu Yi257862f2009-06-15 21:59:56 +0200704
705 return 0;
706}
707
Johannes Bergbc92afd2009-07-01 21:26:57 +0200708static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
709 struct net_device *dev,
710 bool enabled, int timeout)
711{
712 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
713 u32 power_index;
714
715 if (enabled)
716 power_index = IWM_POWER_INDEX_DEFAULT;
717 else
718 power_index = IWM_POWER_INDEX_MIN;
719
720 if (power_index == iwm->conf.power_index)
721 return 0;
722
723 iwm->conf.power_index = power_index;
724
725 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
726 CFG_POWER_INDEX, iwm->conf.power_index);
727}
728
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100729int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
730 struct cfg80211_pmksa *pmksa)
731{
732 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
733
734 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
735}
736
737int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
738 struct cfg80211_pmksa *pmksa)
739{
740 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
741
742 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
743}
744
745int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
746{
747 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
748 struct cfg80211_pmksa pmksa;
749
750 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
751
752 return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
753}
754
755
Zhu Yibb9f8692009-05-21 21:20:45 +0800756static struct cfg80211_ops iwm_cfg80211_ops = {
757 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200758 .add_key = iwm_cfg80211_add_key,
759 .get_key = iwm_cfg80211_get_key,
760 .del_key = iwm_cfg80211_del_key,
761 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800762 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800763 .scan = iwm_cfg80211_scan,
764 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800765 .connect = iwm_cfg80211_connect,
766 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800767 .join_ibss = iwm_cfg80211_join_ibss,
768 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200769 .set_tx_power = iwm_cfg80211_set_txpower,
770 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200771 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100772 .set_pmksa = iwm_cfg80211_set_pmksa,
773 .del_pmksa = iwm_cfg80211_del_pmksa,
774 .flush_pmksa = iwm_cfg80211_flush_pmksa,
Zhu Yibb9f8692009-05-21 21:20:45 +0800775};
776
Zhu Yi49b77722009-07-16 17:34:08 +0800777static const u32 cipher_suites[] = {
778 WLAN_CIPHER_SUITE_WEP40,
779 WLAN_CIPHER_SUITE_WEP104,
780 WLAN_CIPHER_SUITE_TKIP,
781 WLAN_CIPHER_SUITE_CCMP,
782};
783
Zhu Yibb9f8692009-05-21 21:20:45 +0800784struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
785{
786 int ret = 0;
787 struct wireless_dev *wdev;
788
789 /*
790 * We're trying to have the following memory
791 * layout:
792 *
793 * +-------------------------+
794 * | struct wiphy |
795 * +-------------------------+
796 * | struct iwm_priv |
797 * +-------------------------+
798 * | bus private data |
799 * | (e.g. iwm_priv_sdio) |
800 * +-------------------------+
801 *
802 */
803
804 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
805 if (!wdev) {
806 dev_err(dev, "Couldn't allocate wireless device\n");
807 return ERR_PTR(-ENOMEM);
808 }
809
810 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
811 sizeof(struct iwm_priv) + sizeof_bus);
812 if (!wdev->wiphy) {
813 dev_err(dev, "Couldn't allocate wiphy device\n");
814 ret = -ENOMEM;
815 goto out_err_new;
816 }
817
818 set_wiphy_dev(wdev->wiphy, dev);
819 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100820 wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
Zhu Yibb9f8692009-05-21 21:20:45 +0800821 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
822 BIT(NL80211_IFTYPE_ADHOC);
823 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
824 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
825 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
826
Zhu Yi49b77722009-07-16 17:34:08 +0800827 wdev->wiphy->cipher_suites = cipher_suites;
828 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
829
Zhu Yibb9f8692009-05-21 21:20:45 +0800830 ret = wiphy_register(wdev->wiphy);
831 if (ret < 0) {
832 dev_err(dev, "Couldn't register wiphy device\n");
833 goto out_err_register;
834 }
835
836 return wdev;
837
838 out_err_register:
839 wiphy_free(wdev->wiphy);
840
841 out_err_new:
842 kfree(wdev);
843
844 return ERR_PTR(ret);
845}
846
847void iwm_wdev_free(struct iwm_priv *iwm)
848{
849 struct wireless_dev *wdev = iwm_to_wdev(iwm);
850
851 if (!wdev)
852 return;
853
854 wiphy_unregister(wdev->wiphy);
855 wiphy_free(wdev->wiphy);
856 kfree(wdev);
857}