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