blob: f3c55658225bb97951a5ab9055030b84f9ed6389 [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 Ortiz13e0fe72009-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>
30#include <net/cfg80211.h>
31
32#include "iwm.h"
33#include "commands.h"
34#include "cfg80211.h"
35#include "debug.h"
36
37#define RATETAB_ENT(_rate, _rateid, _flags) \
38 { \
39 .bitrate = (_rate), \
40 .hw_value = (_rateid), \
41 .flags = (_flags), \
42 }
43
44#define CHAN2G(_channel, _freq, _flags) { \
45 .band = IEEE80211_BAND_2GHZ, \
46 .center_freq = (_freq), \
47 .hw_value = (_channel), \
48 .flags = (_flags), \
49 .max_antenna_gain = 0, \
50 .max_power = 30, \
51}
52
53#define CHAN5G(_channel, _flags) { \
54 .band = IEEE80211_BAND_5GHZ, \
55 .center_freq = 5000 + (5 * (_channel)), \
56 .hw_value = (_channel), \
57 .flags = (_flags), \
58 .max_antenna_gain = 0, \
59 .max_power = 30, \
60}
61
62static struct ieee80211_rate iwm_rates[] = {
63 RATETAB_ENT(10, 0x1, 0),
64 RATETAB_ENT(20, 0x2, 0),
65 RATETAB_ENT(55, 0x4, 0),
66 RATETAB_ENT(110, 0x8, 0),
67 RATETAB_ENT(60, 0x10, 0),
68 RATETAB_ENT(90, 0x20, 0),
69 RATETAB_ENT(120, 0x40, 0),
70 RATETAB_ENT(180, 0x80, 0),
71 RATETAB_ENT(240, 0x100, 0),
72 RATETAB_ENT(360, 0x200, 0),
73 RATETAB_ENT(480, 0x400, 0),
74 RATETAB_ENT(540, 0x800, 0),
75};
76
77#define iwm_a_rates (iwm_rates + 4)
78#define iwm_a_rates_size 8
79#define iwm_g_rates (iwm_rates + 0)
80#define iwm_g_rates_size 12
81
82static struct ieee80211_channel iwm_2ghz_channels[] = {
83 CHAN2G(1, 2412, 0),
84 CHAN2G(2, 2417, 0),
85 CHAN2G(3, 2422, 0),
86 CHAN2G(4, 2427, 0),
87 CHAN2G(5, 2432, 0),
88 CHAN2G(6, 2437, 0),
89 CHAN2G(7, 2442, 0),
90 CHAN2G(8, 2447, 0),
91 CHAN2G(9, 2452, 0),
92 CHAN2G(10, 2457, 0),
93 CHAN2G(11, 2462, 0),
94 CHAN2G(12, 2467, 0),
95 CHAN2G(13, 2472, 0),
96 CHAN2G(14, 2484, 0),
97};
98
99static struct ieee80211_channel iwm_5ghz_a_channels[] = {
100 CHAN5G(34, 0), CHAN5G(36, 0),
101 CHAN5G(38, 0), CHAN5G(40, 0),
102 CHAN5G(42, 0), CHAN5G(44, 0),
103 CHAN5G(46, 0), CHAN5G(48, 0),
104 CHAN5G(52, 0), CHAN5G(56, 0),
105 CHAN5G(60, 0), CHAN5G(64, 0),
106 CHAN5G(100, 0), CHAN5G(104, 0),
107 CHAN5G(108, 0), CHAN5G(112, 0),
108 CHAN5G(116, 0), CHAN5G(120, 0),
109 CHAN5G(124, 0), CHAN5G(128, 0),
110 CHAN5G(132, 0), CHAN5G(136, 0),
111 CHAN5G(140, 0), CHAN5G(149, 0),
112 CHAN5G(153, 0), CHAN5G(157, 0),
113 CHAN5G(161, 0), CHAN5G(165, 0),
114 CHAN5G(184, 0), CHAN5G(188, 0),
115 CHAN5G(192, 0), CHAN5G(196, 0),
116 CHAN5G(200, 0), CHAN5G(204, 0),
117 CHAN5G(208, 0), CHAN5G(212, 0),
118 CHAN5G(216, 0),
119};
120
121static struct ieee80211_supported_band iwm_band_2ghz = {
122 .channels = iwm_2ghz_channels,
123 .n_channels = ARRAY_SIZE(iwm_2ghz_channels),
124 .bitrates = iwm_g_rates,
125 .n_bitrates = iwm_g_rates_size,
126};
127
128static struct ieee80211_supported_band iwm_band_5ghz = {
129 .channels = iwm_5ghz_a_channels,
130 .n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
131 .bitrates = iwm_a_rates,
132 .n_bitrates = iwm_a_rates_size,
133};
134
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200135static int iwm_key_init(struct iwm_key *key, u8 key_index,
136 const u8 *mac_addr, struct key_params *params)
137{
138 key->hdr.key_idx = key_index;
139 if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
140 key->hdr.multicast = 1;
141 memset(key->hdr.mac, 0xff, ETH_ALEN);
142 } else {
143 key->hdr.multicast = 0;
144 memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
145 }
146
147 if (params) {
148 if (params->key_len > WLAN_MAX_KEY_LEN ||
149 params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
150 return -EINVAL;
151
152 key->cipher = params->cipher;
153 key->key_len = params->key_len;
154 key->seq_len = params->seq_len;
155 memcpy(key->key, params->key, key->key_len);
156 memcpy(key->seq, params->seq, key->seq_len);
157 }
158
159 return 0;
160}
161
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200162static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
163 u8 key_index, const u8 *mac_addr,
164 struct key_params *params)
165{
166 struct iwm_priv *iwm = ndev_to_iwm(ndev);
167 struct iwm_key *key = &iwm->keys[key_index];
168 int ret;
169
170 IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
171
172 memset(key, 0, sizeof(struct iwm_key));
173 ret = iwm_key_init(key, key_index, mac_addr, params);
174 if (ret < 0) {
175 IWM_ERR(iwm, "Invalid key_params\n");
176 return ret;
177 }
178
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200179 return iwm_set_key(iwm, 0, key);
180}
181
182static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
183 u8 key_index, const u8 *mac_addr, void *cookie,
184 void (*callback)(void *cookie,
185 struct key_params*))
186{
187 struct iwm_priv *iwm = ndev_to_iwm(ndev);
188 struct iwm_key *key = &iwm->keys[key_index];
189 struct key_params params;
190
191 IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
192
193 memset(&params, 0, sizeof(params));
194
195 params.cipher = key->cipher;
196 params.key_len = key->key_len;
197 params.seq_len = key->seq_len;
198 params.seq = key->seq;
199 params.key = key->key;
200
201 callback(cookie, &params);
202
203 return key->key_len ? 0 : -ENOENT;
204}
205
206
207static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
208 u8 key_index, const u8 *mac_addr)
209{
210 struct iwm_priv *iwm = ndev_to_iwm(ndev);
211 struct iwm_key *key = &iwm->keys[key_index];
212
213 if (!iwm->keys[key_index].key_len) {
214 IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
215 return 0;
216 }
217
218 if (key_index == iwm->default_key)
219 iwm->default_key = -1;
220
221 return iwm_set_key(iwm, 1, key);
222}
223
224static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
225 struct net_device *ndev,
226 u8 key_index)
227{
228 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200229
230 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
231
232 if (!iwm->keys[key_index].key_len) {
233 IWM_ERR(iwm, "Key %d not used\n", key_index);
234 return -EINVAL;
235 }
236
Samuel Ortiz35497162009-06-15 21:59:54 +0200237 iwm->default_key = key_index;
238
Zhu Yi6e5db0a2009-07-16 17:34:13 +0800239 return iwm_set_tx_key(iwm, key_index);
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200240}
241
Samuel Ortizd0418112009-09-01 15:14:00 +0200242static int iwm_cfg80211_get_station(struct wiphy *wiphy,
243 struct net_device *ndev,
244 u8 *mac, struct station_info *sinfo)
Samuel Ortiz9967d462009-07-16 17:34:10 +0800245{
246 struct iwm_priv *iwm = ndev_to_iwm(ndev);
247
248 if (memcmp(mac, iwm->bssid, ETH_ALEN))
249 return -ENOENT;
250
251 sinfo->filled |= STATION_INFO_TX_BITRATE;
252 sinfo->txrate.legacy = iwm->rate * 10;
253
254 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
255 sinfo->filled |= STATION_INFO_SIGNAL;
256 sinfo->signal = iwm->wstats.qual.level;
257 }
258
259 return 0;
260}
261
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200262
Zhu Yibb9f8692009-05-21 21:20:45 +0800263int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
264{
265 struct wiphy *wiphy = iwm_to_wiphy(iwm);
266 struct iwm_bss_info *bss, *next;
267 struct iwm_umac_notif_bss_info *umac_bss;
268 struct ieee80211_mgmt *mgmt;
269 struct ieee80211_channel *channel;
270 struct ieee80211_supported_band *band;
271 s32 signal;
272 int freq;
273
274 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
275 umac_bss = bss->bss;
276 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
277
278 if (umac_bss->band == UMAC_BAND_2GHZ)
279 band = wiphy->bands[IEEE80211_BAND_2GHZ];
280 else if (umac_bss->band == UMAC_BAND_5GHZ)
281 band = wiphy->bands[IEEE80211_BAND_5GHZ];
282 else {
283 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
284 return -EINVAL;
285 }
286
287 freq = ieee80211_channel_to_frequency(umac_bss->channel);
288 channel = ieee80211_get_channel(wiphy, freq);
289 signal = umac_bss->rssi * 100;
290
291 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
292 le16_to_cpu(umac_bss->frame_len),
293 signal, GFP_KERNEL))
294 return -EINVAL;
295 }
296
297 return 0;
298}
299
Johannes Berge36d56b2009-06-09 21:04:43 +0200300static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
301 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800302 enum nl80211_iftype type, u32 *flags,
303 struct vif_params *params)
304{
Zhu Yibb9f8692009-05-21 21:20:45 +0800305 struct wireless_dev *wdev;
306 struct iwm_priv *iwm;
307 u32 old_mode;
308
Zhu Yibb9f8692009-05-21 21:20:45 +0800309 wdev = ndev->ieee80211_ptr;
310 iwm = ndev_to_iwm(ndev);
311 old_mode = iwm->conf.mode;
312
313 switch (type) {
314 case NL80211_IFTYPE_STATION:
315 iwm->conf.mode = UMAC_MODE_BSS;
316 break;
317 case NL80211_IFTYPE_ADHOC:
318 iwm->conf.mode = UMAC_MODE_IBSS;
319 break;
320 default:
321 return -EOPNOTSUPP;
322 }
323
324 wdev->iftype = type;
325
326 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
327 return 0;
328
329 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
330
Zhu Yiae73abf2009-09-01 15:13:58 +0200331 if (iwm->umac_profile_active)
332 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800333
334 return 0;
335}
336
337static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
338 struct cfg80211_scan_request *request)
339{
340 struct iwm_priv *iwm = ndev_to_iwm(ndev);
341 int ret;
342
343 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
344 IWM_ERR(iwm, "Scan while device is not ready\n");
345 return -EIO;
346 }
347
348 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
349 IWM_ERR(iwm, "Scanning already\n");
350 return -EAGAIN;
351 }
352
353 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
354 IWM_ERR(iwm, "Scanning being aborted\n");
355 return -EAGAIN;
356 }
357
358 set_bit(IWM_STATUS_SCANNING, &iwm->status);
359
360 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
361 if (ret) {
362 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
363 return ret;
364 }
365
366 iwm->scan_request = request;
367 return 0;
368}
369
370static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
371{
372 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
373
374 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
375 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
376 int ret;
377
378 iwm->conf.rts_threshold = wiphy->rts_threshold;
379
380 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
381 CFG_RTS_THRESHOLD,
382 iwm->conf.rts_threshold);
383 if (ret < 0)
384 return ret;
385 }
386
387 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
388 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
389 int ret;
390
391 iwm->conf.frag_threshold = wiphy->frag_threshold;
392
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800393 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800394 CFG_FRAG_THRESHOLD,
395 iwm->conf.frag_threshold);
396 if (ret < 0)
397 return ret;
398 }
399
400 return 0;
401}
402
403static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
404 struct cfg80211_ibss_params *params)
405{
406 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
407 struct ieee80211_channel *chan = params->channel;
408 struct cfg80211_bss *bss;
409
410 if (!test_bit(IWM_STATUS_READY, &iwm->status))
411 return -EIO;
412
413 /* UMAC doesn't support creating IBSS network with specified bssid.
414 * This should be removed after we have join only mode supported. */
415 if (params->bssid)
416 return -EOPNOTSUPP;
417
418 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
419 params->ssid, params->ssid_len);
420 if (!bss) {
421 iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
422 schedule_timeout_interruptible(2 * HZ);
423 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
424 params->ssid, params->ssid_len);
425 }
426 /* IBSS join only mode is not supported by UMAC ATM */
427 if (bss) {
428 cfg80211_put_bss(bss);
429 return -EOPNOTSUPP;
430 }
431
432 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
433 iwm->umac_profile->ibss.band = chan->band;
434 iwm->umac_profile->ibss.channel = iwm->channel;
435 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
436 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
437
438 if (params->bssid)
439 memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
440
441 return iwm_send_mlme_profile(iwm);
442}
443
444static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
445{
446 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
447
448 if (iwm->umac_profile_active)
449 return iwm_invalidate_mlme_profile(iwm);
450
451 return 0;
452}
453
Samuel Ortiz9967d462009-07-16 17:34:10 +0800454static int iwm_set_auth_type(struct iwm_priv *iwm,
455 enum nl80211_auth_type sme_auth_type)
456{
457 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
458
459 switch (sme_auth_type) {
460 case NL80211_AUTHTYPE_AUTOMATIC:
461 case NL80211_AUTHTYPE_OPEN_SYSTEM:
462 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
463 *auth_type = UMAC_AUTH_TYPE_OPEN;
464 break;
465 case NL80211_AUTHTYPE_SHARED_KEY:
466 if (iwm->umac_profile->sec.flags &
467 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
468 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
469 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
470 } else {
471 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
472 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
473 }
474
475 break;
476 default:
477 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
478 return -ENOTSUPP;
479 }
480
481 return 0;
482}
483
484static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
485{
Zhu Yi554503f2009-08-03 14:37:01 +0800486 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
487
Samuel Ortiz9967d462009-07-16 17:34:10 +0800488 if (!wpa_version) {
489 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
490 return 0;
491 }
492
493 if (wpa_version & NL80211_WPA_VERSION_2)
494 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
495
496 if (wpa_version & NL80211_WPA_VERSION_1)
497 iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK;
498
499 return 0;
500}
501
502static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
503{
504 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
505 &iwm->umac_profile->sec.mcast_cipher;
506
507 if (!cipher) {
508 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
509 return 0;
510 }
511
Zhu Yi554503f2009-08-03 14:37:01 +0800512 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
513 cipher);
514
Samuel Ortiz9967d462009-07-16 17:34:10 +0800515 switch (cipher) {
516 case IW_AUTH_CIPHER_NONE:
517 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
518 break;
519 case WLAN_CIPHER_SUITE_WEP40:
520 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
521 break;
522 case WLAN_CIPHER_SUITE_WEP104:
523 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
524 break;
525 case WLAN_CIPHER_SUITE_TKIP:
526 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
527 break;
528 case WLAN_CIPHER_SUITE_CCMP:
529 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
530 break;
531 default:
532 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
533 return -ENOTSUPP;
534 }
535
536 return 0;
537}
538
539static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
540{
541 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
542
543 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
544
545 if (key_mgt == WLAN_AKM_SUITE_8021X)
546 *auth_type = UMAC_AUTH_TYPE_8021X;
547 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
548 if (iwm->umac_profile->sec.flags &
549 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
550 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
551 else
552 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
553 } else {
554 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
555 return -EINVAL;
556 }
557
558 return 0;
559}
560
561
562static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
563 struct cfg80211_connect_params *sme)
564{
565 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
566 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200567 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800568 int ret;
569
570 if (!test_bit(IWM_STATUS_READY, &iwm->status))
571 return -EIO;
572
573 if (!sme->ssid)
574 return -EINVAL;
575
Zhu Yiae73abf2009-09-01 15:13:58 +0200576 if (iwm->umac_profile_active) {
577 ret = iwm_invalidate_mlme_profile(iwm);
578 if (ret) {
579 IWM_ERR(iwm, "Couldn't invalidate profile\n");
580 return ret;
581 }
582 }
583
Samuel Ortiz9967d462009-07-16 17:34:10 +0800584 if (chan)
585 iwm->channel =
586 ieee80211_frequency_to_channel(chan->center_freq);
587
588 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
589 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
590
591 if (sme->bssid) {
592 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
593 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
594 iwm->umac_profile->bss_num = 1;
595 } else {
596 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
597 iwm->umac_profile->bss_num = 0;
598 }
599
Zhu Yi554503f2009-08-03 14:37:01 +0800600 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800601 if (ret < 0)
602 return ret;
603
Zhu Yi554503f2009-08-03 14:37:01 +0800604 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800605 if (ret < 0)
606 return ret;
607
608 if (sme->crypto.n_ciphers_pairwise) {
609 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
610 true);
611 if (ret < 0)
612 return ret;
613 }
614
615 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
616 if (ret < 0)
617 return ret;
618
619 if (sme->crypto.n_akm_suites) {
620 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
621 if (ret < 0)
622 return ret;
623 }
624
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200625 /*
626 * We save the WEP key in case we want to do shared authentication.
627 * We have to do it so because UMAC will assert whenever it gets a
628 * key before a profile.
629 */
630 if (sme->key) {
631 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
632 if (key_param.key == NULL)
633 return -ENOMEM;
634 key_param.key_len = sme->key_len;
635 key_param.seq_len = 0;
636 key_param.cipher = sme->crypto.ciphers_pairwise[0];
637
638 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
639 NULL, &key_param);
640 kfree(key_param.key);
641 if (ret < 0) {
642 IWM_ERR(iwm, "Invalid key_params\n");
643 return ret;
644 }
645
646 iwm->default_key = sme->key_idx;
647 }
648
649 ret = iwm_send_mlme_profile(iwm);
650
651 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
652 sme->key == NULL)
653 return ret;
654
655 /*
656 * We want to do shared auth.
657 * We need to actually set the key we previously cached,
658 * and then tell the UMAC it's the default one.
659 * That will trigger the auth+assoc UMAC machinery, and again,
660 * this must be done after setting the profile.
661 */
662 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
663 if (ret < 0)
664 return ret;
665
666 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800667}
668
669static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
670 u16 reason_code)
671{
672 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
673
674 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
675
676 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200677 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800678
679 return 0;
680}
681
Zhu Yi257862f2009-06-15 21:59:56 +0200682static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
683 enum tx_power_setting type, int dbm)
684{
685 switch (type) {
686 case TX_POWER_AUTOMATIC:
687 return 0;
688 default:
689 return -EOPNOTSUPP;
690 }
691
692 return 0;
693}
694
695static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
696{
697 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
698
699 *dbm = iwm->txpower;
700
701 return 0;
702}
703
Johannes Bergbc92afd2009-07-01 21:26:57 +0200704static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
705 struct net_device *dev,
706 bool enabled, int timeout)
707{
708 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
709 u32 power_index;
710
711 if (enabled)
712 power_index = IWM_POWER_INDEX_DEFAULT;
713 else
714 power_index = IWM_POWER_INDEX_MIN;
715
716 if (power_index == iwm->conf.power_index)
717 return 0;
718
719 iwm->conf.power_index = power_index;
720
721 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
722 CFG_POWER_INDEX, iwm->conf.power_index);
723}
724
Zhu Yibb9f8692009-05-21 21:20:45 +0800725static struct cfg80211_ops iwm_cfg80211_ops = {
726 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200727 .add_key = iwm_cfg80211_add_key,
728 .get_key = iwm_cfg80211_get_key,
729 .del_key = iwm_cfg80211_del_key,
730 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800731 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800732 .scan = iwm_cfg80211_scan,
733 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800734 .connect = iwm_cfg80211_connect,
735 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800736 .join_ibss = iwm_cfg80211_join_ibss,
737 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200738 .set_tx_power = iwm_cfg80211_set_txpower,
739 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200740 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Zhu Yibb9f8692009-05-21 21:20:45 +0800741};
742
Zhu Yi49b77722009-07-16 17:34:08 +0800743static const u32 cipher_suites[] = {
744 WLAN_CIPHER_SUITE_WEP40,
745 WLAN_CIPHER_SUITE_WEP104,
746 WLAN_CIPHER_SUITE_TKIP,
747 WLAN_CIPHER_SUITE_CCMP,
748};
749
Zhu Yibb9f8692009-05-21 21:20:45 +0800750struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
751{
752 int ret = 0;
753 struct wireless_dev *wdev;
754
755 /*
756 * We're trying to have the following memory
757 * layout:
758 *
759 * +-------------------------+
760 * | struct wiphy |
761 * +-------------------------+
762 * | struct iwm_priv |
763 * +-------------------------+
764 * | bus private data |
765 * | (e.g. iwm_priv_sdio) |
766 * +-------------------------+
767 *
768 */
769
770 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
771 if (!wdev) {
772 dev_err(dev, "Couldn't allocate wireless device\n");
773 return ERR_PTR(-ENOMEM);
774 }
775
776 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
777 sizeof(struct iwm_priv) + sizeof_bus);
778 if (!wdev->wiphy) {
779 dev_err(dev, "Couldn't allocate wiphy device\n");
780 ret = -ENOMEM;
781 goto out_err_new;
782 }
783
784 set_wiphy_dev(wdev->wiphy, dev);
785 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
786 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
787 BIT(NL80211_IFTYPE_ADHOC);
788 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
789 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
790 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
791
Zhu Yi49b77722009-07-16 17:34:08 +0800792 wdev->wiphy->cipher_suites = cipher_suites;
793 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
794
Zhu Yibb9f8692009-05-21 21:20:45 +0800795 ret = wiphy_register(wdev->wiphy);
796 if (ret < 0) {
797 dev_err(dev, "Couldn't register wiphy device\n");
798 goto out_err_register;
799 }
800
801 return wdev;
802
803 out_err_register:
804 wiphy_free(wdev->wiphy);
805
806 out_err_new:
807 kfree(wdev);
808
809 return ERR_PTR(ret);
810}
811
812void iwm_wdev_free(struct iwm_priv *iwm)
813{
814 struct wireless_dev *wdev = iwm_to_wdev(iwm);
815
816 if (!wdev)
817 return;
818
819 wiphy_unregister(wdev->wiphy);
820 wiphy_free(wdev->wiphy);
821 kfree(wdev);
822}