blob: d8bb723d88d9c89a2ba4606efd0e10f6026c3665 [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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-06-15 21:59:52 +0200239}
240
Samuel Ortiz9967d462009-07-16 17:34:10 +0800241int iwm_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
242 u8 *mac, struct station_info *sinfo)
243{
244 struct iwm_priv *iwm = ndev_to_iwm(ndev);
245
246 if (memcmp(mac, iwm->bssid, ETH_ALEN))
247 return -ENOENT;
248
249 sinfo->filled |= STATION_INFO_TX_BITRATE;
250 sinfo->txrate.legacy = iwm->rate * 10;
251
252 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
253 sinfo->filled |= STATION_INFO_SIGNAL;
254 sinfo->signal = iwm->wstats.qual.level;
255 }
256
257 return 0;
258}
259
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200260
Zhu Yibb9f8692009-05-21 21:20:45 +0800261int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
262{
263 struct wiphy *wiphy = iwm_to_wiphy(iwm);
264 struct iwm_bss_info *bss, *next;
265 struct iwm_umac_notif_bss_info *umac_bss;
266 struct ieee80211_mgmt *mgmt;
267 struct ieee80211_channel *channel;
268 struct ieee80211_supported_band *band;
269 s32 signal;
270 int freq;
271
272 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
273 umac_bss = bss->bss;
274 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
275
276 if (umac_bss->band == UMAC_BAND_2GHZ)
277 band = wiphy->bands[IEEE80211_BAND_2GHZ];
278 else if (umac_bss->band == UMAC_BAND_5GHZ)
279 band = wiphy->bands[IEEE80211_BAND_5GHZ];
280 else {
281 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
282 return -EINVAL;
283 }
284
285 freq = ieee80211_channel_to_frequency(umac_bss->channel);
286 channel = ieee80211_get_channel(wiphy, freq);
287 signal = umac_bss->rssi * 100;
288
289 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
290 le16_to_cpu(umac_bss->frame_len),
291 signal, GFP_KERNEL))
292 return -EINVAL;
293 }
294
295 return 0;
296}
297
Johannes Berge36d56b2009-06-09 21:04:43 +0200298static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
299 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800300 enum nl80211_iftype type, u32 *flags,
301 struct vif_params *params)
302{
Zhu Yibb9f8692009-05-21 21:20:45 +0800303 struct wireless_dev *wdev;
304 struct iwm_priv *iwm;
305 u32 old_mode;
306
Zhu Yibb9f8692009-05-21 21:20:45 +0800307 wdev = ndev->ieee80211_ptr;
308 iwm = ndev_to_iwm(ndev);
309 old_mode = iwm->conf.mode;
310
311 switch (type) {
312 case NL80211_IFTYPE_STATION:
313 iwm->conf.mode = UMAC_MODE_BSS;
314 break;
315 case NL80211_IFTYPE_ADHOC:
316 iwm->conf.mode = UMAC_MODE_IBSS;
317 break;
318 default:
319 return -EOPNOTSUPP;
320 }
321
322 wdev->iftype = type;
323
324 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
325 return 0;
326
327 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
328
Zhu Yiae73abf2009-09-01 15:13:58 +0200329 if (iwm->umac_profile_active)
330 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800331
332 return 0;
333}
334
335static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
336 struct cfg80211_scan_request *request)
337{
338 struct iwm_priv *iwm = ndev_to_iwm(ndev);
339 int ret;
340
341 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
342 IWM_ERR(iwm, "Scan while device is not ready\n");
343 return -EIO;
344 }
345
346 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
347 IWM_ERR(iwm, "Scanning already\n");
348 return -EAGAIN;
349 }
350
351 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
352 IWM_ERR(iwm, "Scanning being aborted\n");
353 return -EAGAIN;
354 }
355
356 set_bit(IWM_STATUS_SCANNING, &iwm->status);
357
358 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
359 if (ret) {
360 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
361 return ret;
362 }
363
364 iwm->scan_request = request;
365 return 0;
366}
367
368static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
369{
370 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
371
372 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
373 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
374 int ret;
375
376 iwm->conf.rts_threshold = wiphy->rts_threshold;
377
378 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
379 CFG_RTS_THRESHOLD,
380 iwm->conf.rts_threshold);
381 if (ret < 0)
382 return ret;
383 }
384
385 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
386 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
387 int ret;
388
389 iwm->conf.frag_threshold = wiphy->frag_threshold;
390
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800391 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800392 CFG_FRAG_THRESHOLD,
393 iwm->conf.frag_threshold);
394 if (ret < 0)
395 return ret;
396 }
397
398 return 0;
399}
400
401static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
402 struct cfg80211_ibss_params *params)
403{
404 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
405 struct ieee80211_channel *chan = params->channel;
406 struct cfg80211_bss *bss;
407
408 if (!test_bit(IWM_STATUS_READY, &iwm->status))
409 return -EIO;
410
411 /* UMAC doesn't support creating IBSS network with specified bssid.
412 * This should be removed after we have join only mode supported. */
413 if (params->bssid)
414 return -EOPNOTSUPP;
415
416 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
417 params->ssid, params->ssid_len);
418 if (!bss) {
419 iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
420 schedule_timeout_interruptible(2 * HZ);
421 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
422 params->ssid, params->ssid_len);
423 }
424 /* IBSS join only mode is not supported by UMAC ATM */
425 if (bss) {
426 cfg80211_put_bss(bss);
427 return -EOPNOTSUPP;
428 }
429
430 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
431 iwm->umac_profile->ibss.band = chan->band;
432 iwm->umac_profile->ibss.channel = iwm->channel;
433 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
434 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
435
436 if (params->bssid)
437 memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
438
439 return iwm_send_mlme_profile(iwm);
440}
441
442static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
443{
444 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
445
446 if (iwm->umac_profile_active)
447 return iwm_invalidate_mlme_profile(iwm);
448
449 return 0;
450}
451
Samuel Ortiz9967d462009-07-16 17:34:10 +0800452static int iwm_set_auth_type(struct iwm_priv *iwm,
453 enum nl80211_auth_type sme_auth_type)
454{
455 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
456
457 switch (sme_auth_type) {
458 case NL80211_AUTHTYPE_AUTOMATIC:
459 case NL80211_AUTHTYPE_OPEN_SYSTEM:
460 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
461 *auth_type = UMAC_AUTH_TYPE_OPEN;
462 break;
463 case NL80211_AUTHTYPE_SHARED_KEY:
464 if (iwm->umac_profile->sec.flags &
465 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
466 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
467 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
468 } else {
469 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
470 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
471 }
472
473 break;
474 default:
475 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
476 return -ENOTSUPP;
477 }
478
479 return 0;
480}
481
482static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
483{
Zhu Yi554503f2009-08-03 14:37:01 +0800484 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
485
Samuel Ortiz9967d462009-07-16 17:34:10 +0800486 if (!wpa_version) {
487 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
488 return 0;
489 }
490
491 if (wpa_version & NL80211_WPA_VERSION_2)
492 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
493
494 if (wpa_version & NL80211_WPA_VERSION_1)
495 iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK;
496
497 return 0;
498}
499
500static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
501{
502 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
503 &iwm->umac_profile->sec.mcast_cipher;
504
505 if (!cipher) {
506 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
507 return 0;
508 }
509
Zhu Yi554503f2009-08-03 14:37:01 +0800510 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
511 cipher);
512
Samuel Ortiz9967d462009-07-16 17:34:10 +0800513 switch (cipher) {
514 case IW_AUTH_CIPHER_NONE:
515 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
516 break;
517 case WLAN_CIPHER_SUITE_WEP40:
518 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
519 break;
520 case WLAN_CIPHER_SUITE_WEP104:
521 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
522 break;
523 case WLAN_CIPHER_SUITE_TKIP:
524 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
525 break;
526 case WLAN_CIPHER_SUITE_CCMP:
527 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
528 break;
529 default:
530 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
531 return -ENOTSUPP;
532 }
533
534 return 0;
535}
536
537static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
538{
539 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
540
541 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
542
543 if (key_mgt == WLAN_AKM_SUITE_8021X)
544 *auth_type = UMAC_AUTH_TYPE_8021X;
545 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
546 if (iwm->umac_profile->sec.flags &
547 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
548 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
549 else
550 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
551 } else {
552 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
553 return -EINVAL;
554 }
555
556 return 0;
557}
558
559
560static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
561 struct cfg80211_connect_params *sme)
562{
563 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
564 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200565 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800566 int ret;
567
568 if (!test_bit(IWM_STATUS_READY, &iwm->status))
569 return -EIO;
570
571 if (!sme->ssid)
572 return -EINVAL;
573
Zhu Yiae73abf2009-09-01 15:13:58 +0200574 if (iwm->umac_profile_active) {
575 ret = iwm_invalidate_mlme_profile(iwm);
576 if (ret) {
577 IWM_ERR(iwm, "Couldn't invalidate profile\n");
578 return ret;
579 }
580 }
581
Samuel Ortiz9967d462009-07-16 17:34:10 +0800582 if (chan)
583 iwm->channel =
584 ieee80211_frequency_to_channel(chan->center_freq);
585
586 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
587 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
588
589 if (sme->bssid) {
590 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
591 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
592 iwm->umac_profile->bss_num = 1;
593 } else {
594 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
595 iwm->umac_profile->bss_num = 0;
596 }
597
Zhu Yi554503f2009-08-03 14:37:01 +0800598 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800599 if (ret < 0)
600 return ret;
601
Zhu Yi554503f2009-08-03 14:37:01 +0800602 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800603 if (ret < 0)
604 return ret;
605
606 if (sme->crypto.n_ciphers_pairwise) {
607 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
608 true);
609 if (ret < 0)
610 return ret;
611 }
612
613 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
614 if (ret < 0)
615 return ret;
616
617 if (sme->crypto.n_akm_suites) {
618 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
619 if (ret < 0)
620 return ret;
621 }
622
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200623 /*
624 * We save the WEP key in case we want to do shared authentication.
625 * We have to do it so because UMAC will assert whenever it gets a
626 * key before a profile.
627 */
628 if (sme->key) {
629 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
630 if (key_param.key == NULL)
631 return -ENOMEM;
632 key_param.key_len = sme->key_len;
633 key_param.seq_len = 0;
634 key_param.cipher = sme->crypto.ciphers_pairwise[0];
635
636 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
637 NULL, &key_param);
638 kfree(key_param.key);
639 if (ret < 0) {
640 IWM_ERR(iwm, "Invalid key_params\n");
641 return ret;
642 }
643
644 iwm->default_key = sme->key_idx;
645 }
646
647 ret = iwm_send_mlme_profile(iwm);
648
649 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
650 sme->key == NULL)
651 return ret;
652
653 /*
654 * We want to do shared auth.
655 * We need to actually set the key we previously cached,
656 * and then tell the UMAC it's the default one.
657 * That will trigger the auth+assoc UMAC machinery, and again,
658 * this must be done after setting the profile.
659 */
660 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
661 if (ret < 0)
662 return ret;
663
664 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800665}
666
667static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
668 u16 reason_code)
669{
670 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
671
672 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
673
674 if (iwm->umac_profile_active)
675 return iwm_invalidate_mlme_profile(iwm);
676
677 return 0;
678}
679
Zhu Yi257862f2009-06-15 21:59:56 +0200680static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
681 enum tx_power_setting type, int dbm)
682{
683 switch (type) {
684 case TX_POWER_AUTOMATIC:
685 return 0;
686 default:
687 return -EOPNOTSUPP;
688 }
689
690 return 0;
691}
692
693static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
694{
695 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
696
697 *dbm = iwm->txpower;
698
699 return 0;
700}
701
Johannes Bergbc92afd2009-07-01 21:26:57 +0200702static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
703 struct net_device *dev,
704 bool enabled, int timeout)
705{
706 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
707 u32 power_index;
708
709 if (enabled)
710 power_index = IWM_POWER_INDEX_DEFAULT;
711 else
712 power_index = IWM_POWER_INDEX_MIN;
713
714 if (power_index == iwm->conf.power_index)
715 return 0;
716
717 iwm->conf.power_index = power_index;
718
719 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
720 CFG_POWER_INDEX, iwm->conf.power_index);
721}
722
Zhu Yibb9f8692009-05-21 21:20:45 +0800723static struct cfg80211_ops iwm_cfg80211_ops = {
724 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200725 .add_key = iwm_cfg80211_add_key,
726 .get_key = iwm_cfg80211_get_key,
727 .del_key = iwm_cfg80211_del_key,
728 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800729 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800730 .scan = iwm_cfg80211_scan,
731 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800732 .connect = iwm_cfg80211_connect,
733 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800734 .join_ibss = iwm_cfg80211_join_ibss,
735 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200736 .set_tx_power = iwm_cfg80211_set_txpower,
737 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200738 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Zhu Yibb9f8692009-05-21 21:20:45 +0800739};
740
Zhu Yi49b77722009-07-16 17:34:08 +0800741static const u32 cipher_suites[] = {
742 WLAN_CIPHER_SUITE_WEP40,
743 WLAN_CIPHER_SUITE_WEP104,
744 WLAN_CIPHER_SUITE_TKIP,
745 WLAN_CIPHER_SUITE_CCMP,
746};
747
Zhu Yibb9f8692009-05-21 21:20:45 +0800748struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
749{
750 int ret = 0;
751 struct wireless_dev *wdev;
752
753 /*
754 * We're trying to have the following memory
755 * layout:
756 *
757 * +-------------------------+
758 * | struct wiphy |
759 * +-------------------------+
760 * | struct iwm_priv |
761 * +-------------------------+
762 * | bus private data |
763 * | (e.g. iwm_priv_sdio) |
764 * +-------------------------+
765 *
766 */
767
768 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
769 if (!wdev) {
770 dev_err(dev, "Couldn't allocate wireless device\n");
771 return ERR_PTR(-ENOMEM);
772 }
773
774 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
775 sizeof(struct iwm_priv) + sizeof_bus);
776 if (!wdev->wiphy) {
777 dev_err(dev, "Couldn't allocate wiphy device\n");
778 ret = -ENOMEM;
779 goto out_err_new;
780 }
781
782 set_wiphy_dev(wdev->wiphy, dev);
783 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
784 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
785 BIT(NL80211_IFTYPE_ADHOC);
786 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
787 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
788 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
789
Zhu Yi49b77722009-07-16 17:34:08 +0800790 wdev->wiphy->cipher_suites = cipher_suites;
791 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
792
Zhu Yibb9f8692009-05-21 21:20:45 +0800793 ret = wiphy_register(wdev->wiphy);
794 if (ret < 0) {
795 dev_err(dev, "Couldn't register wiphy device\n");
796 goto out_err_register;
797 }
798
799 return wdev;
800
801 out_err_register:
802 wiphy_free(wdev->wiphy);
803
804 out_err_new:
805 kfree(wdev);
806
807 return ERR_PTR(ret);
808}
809
810void iwm_wdev_free(struct iwm_priv *iwm)
811{
812 struct wireless_dev *wdev = iwm_to_wdev(iwm);
813
814 if (!wdev)
815 return;
816
817 wiphy_unregister(wdev->wiphy);
818 wiphy_free(wdev->wiphy);
819 kfree(wdev);
820}