blob: ee403176438960bdc3fb53ecc59586956b55405e [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
161static int iwm_reset_profile(struct iwm_priv *iwm)
162{
163 int ret;
164
165 if (!iwm->umac_profile_active)
166 return 0;
167
168 /*
169 * If there is a current active profile, but no
170 * default key, it's not worth trying to associate again.
171 */
172 if (iwm->default_key < 0)
173 return 0;
174
175 /*
176 * Here we have an active profile, but a key setting changed.
177 * We thus have to invalidate the current profile, and push the
178 * new one. Keys will be pushed when association takes place.
179 */
180 ret = iwm_invalidate_mlme_profile(iwm);
181 if (ret < 0) {
182 IWM_ERR(iwm, "Couldn't invalidate profile\n");
183 return ret;
184 }
185
186 return iwm_send_mlme_profile(iwm);
187}
188
189static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
190 u8 key_index, const u8 *mac_addr,
191 struct key_params *params)
192{
193 struct iwm_priv *iwm = ndev_to_iwm(ndev);
194 struct iwm_key *key = &iwm->keys[key_index];
195 int ret;
196
197 IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
198
199 memset(key, 0, sizeof(struct iwm_key));
200 ret = iwm_key_init(key, key_index, mac_addr, params);
201 if (ret < 0) {
202 IWM_ERR(iwm, "Invalid key_params\n");
203 return ret;
204 }
205
206 /*
207 * The WEP keys can be set before or after setting the essid.
208 * We need to handle both cases by simply pushing the keys after
209 * we send the profile.
210 * If the profile is not set yet (i.e. we're pushing keys before
211 * the essid), we set the cipher appropriately.
212 * If the profile is set, we havent associated yet because our
213 * cipher was incorrectly set. So we invalidate and send the
214 * profile again.
215 */
216 if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
217 key->cipher == WLAN_CIPHER_SUITE_WEP104) {
218 u8 *ucast_cipher = &iwm->umac_profile->sec.ucast_cipher;
219 u8 *mcast_cipher = &iwm->umac_profile->sec.mcast_cipher;
220
221 IWM_DBG_WEXT(iwm, DBG, "WEP key\n");
222
223 if (key->cipher == WLAN_CIPHER_SUITE_WEP40)
224 *ucast_cipher = *mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
225 if (key->cipher == WLAN_CIPHER_SUITE_WEP104)
226 *ucast_cipher = *mcast_cipher =
227 UMAC_CIPHER_TYPE_WEP_104;
228
229 return iwm_reset_profile(iwm);
230 }
231
232 return iwm_set_key(iwm, 0, key);
233}
234
235static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
236 u8 key_index, const u8 *mac_addr, void *cookie,
237 void (*callback)(void *cookie,
238 struct key_params*))
239{
240 struct iwm_priv *iwm = ndev_to_iwm(ndev);
241 struct iwm_key *key = &iwm->keys[key_index];
242 struct key_params params;
243
244 IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
245
246 memset(&params, 0, sizeof(params));
247
248 params.cipher = key->cipher;
249 params.key_len = key->key_len;
250 params.seq_len = key->seq_len;
251 params.seq = key->seq;
252 params.key = key->key;
253
254 callback(cookie, &params);
255
256 return key->key_len ? 0 : -ENOENT;
257}
258
259
260static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
261 u8 key_index, const u8 *mac_addr)
262{
263 struct iwm_priv *iwm = ndev_to_iwm(ndev);
264 struct iwm_key *key = &iwm->keys[key_index];
265
266 if (!iwm->keys[key_index].key_len) {
267 IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
268 return 0;
269 }
270
271 if (key_index == iwm->default_key)
272 iwm->default_key = -1;
273
Samuel Ortiz35497162009-06-15 21:59:54 +0200274 /* If the interface is down, we just cache this */
275 if (!test_bit(IWM_STATUS_READY, &iwm->status))
276 return 0;
277
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200278 return iwm_set_key(iwm, 1, key);
279}
280
281static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
282 struct net_device *ndev,
283 u8 key_index)
284{
285 struct iwm_priv *iwm = ndev_to_iwm(ndev);
286 int ret;
287
288 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
289
290 if (!iwm->keys[key_index].key_len) {
291 IWM_ERR(iwm, "Key %d not used\n", key_index);
292 return -EINVAL;
293 }
294
Samuel Ortiz35497162009-06-15 21:59:54 +0200295 iwm->default_key = key_index;
296
297 /* If the interface is down, we just cache this */
298 if (!test_bit(IWM_STATUS_READY, &iwm->status))
299 return 0;
300
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200301 ret = iwm_set_tx_key(iwm, key_index);
302 if (ret < 0)
303 return ret;
304
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200305 return iwm_reset_profile(iwm);
306}
307
Samuel Ortiz9967d462009-07-16 17:34:10 +0800308int iwm_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
309 u8 *mac, struct station_info *sinfo)
310{
311 struct iwm_priv *iwm = ndev_to_iwm(ndev);
312
313 if (memcmp(mac, iwm->bssid, ETH_ALEN))
314 return -ENOENT;
315
316 sinfo->filled |= STATION_INFO_TX_BITRATE;
317 sinfo->txrate.legacy = iwm->rate * 10;
318
319 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
320 sinfo->filled |= STATION_INFO_SIGNAL;
321 sinfo->signal = iwm->wstats.qual.level;
322 }
323
324 return 0;
325}
326
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200327
Zhu Yibb9f8692009-05-21 21:20:45 +0800328int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
329{
330 struct wiphy *wiphy = iwm_to_wiphy(iwm);
331 struct iwm_bss_info *bss, *next;
332 struct iwm_umac_notif_bss_info *umac_bss;
333 struct ieee80211_mgmt *mgmt;
334 struct ieee80211_channel *channel;
335 struct ieee80211_supported_band *band;
336 s32 signal;
337 int freq;
338
339 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
340 umac_bss = bss->bss;
341 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
342
343 if (umac_bss->band == UMAC_BAND_2GHZ)
344 band = wiphy->bands[IEEE80211_BAND_2GHZ];
345 else if (umac_bss->band == UMAC_BAND_5GHZ)
346 band = wiphy->bands[IEEE80211_BAND_5GHZ];
347 else {
348 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
349 return -EINVAL;
350 }
351
352 freq = ieee80211_channel_to_frequency(umac_bss->channel);
353 channel = ieee80211_get_channel(wiphy, freq);
354 signal = umac_bss->rssi * 100;
355
356 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
357 le16_to_cpu(umac_bss->frame_len),
358 signal, GFP_KERNEL))
359 return -EINVAL;
360 }
361
362 return 0;
363}
364
Johannes Berge36d56b2009-06-09 21:04:43 +0200365static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
366 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800367 enum nl80211_iftype type, u32 *flags,
368 struct vif_params *params)
369{
Zhu Yibb9f8692009-05-21 21:20:45 +0800370 struct wireless_dev *wdev;
371 struct iwm_priv *iwm;
372 u32 old_mode;
373
Zhu Yibb9f8692009-05-21 21:20:45 +0800374 wdev = ndev->ieee80211_ptr;
375 iwm = ndev_to_iwm(ndev);
376 old_mode = iwm->conf.mode;
377
378 switch (type) {
379 case NL80211_IFTYPE_STATION:
380 iwm->conf.mode = UMAC_MODE_BSS;
381 break;
382 case NL80211_IFTYPE_ADHOC:
383 iwm->conf.mode = UMAC_MODE_IBSS;
384 break;
385 default:
386 return -EOPNOTSUPP;
387 }
388
389 wdev->iftype = type;
390
391 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
392 return 0;
393
394 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
395
396 if (iwm->umac_profile_active) {
397 int ret = iwm_invalidate_mlme_profile(iwm);
398 if (ret < 0)
399 IWM_ERR(iwm, "Couldn't invalidate profile\n");
400 }
401
402 return 0;
403}
404
405static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
406 struct cfg80211_scan_request *request)
407{
408 struct iwm_priv *iwm = ndev_to_iwm(ndev);
409 int ret;
410
411 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
412 IWM_ERR(iwm, "Scan while device is not ready\n");
413 return -EIO;
414 }
415
416 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
417 IWM_ERR(iwm, "Scanning already\n");
418 return -EAGAIN;
419 }
420
421 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
422 IWM_ERR(iwm, "Scanning being aborted\n");
423 return -EAGAIN;
424 }
425
426 set_bit(IWM_STATUS_SCANNING, &iwm->status);
427
428 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
429 if (ret) {
430 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
431 return ret;
432 }
433
434 iwm->scan_request = request;
435 return 0;
436}
437
438static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
439{
440 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
441
442 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
443 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
444 int ret;
445
446 iwm->conf.rts_threshold = wiphy->rts_threshold;
447
448 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
449 CFG_RTS_THRESHOLD,
450 iwm->conf.rts_threshold);
451 if (ret < 0)
452 return ret;
453 }
454
455 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
456 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
457 int ret;
458
459 iwm->conf.frag_threshold = wiphy->frag_threshold;
460
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800461 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800462 CFG_FRAG_THRESHOLD,
463 iwm->conf.frag_threshold);
464 if (ret < 0)
465 return ret;
466 }
467
468 return 0;
469}
470
471static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
472 struct cfg80211_ibss_params *params)
473{
474 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
475 struct ieee80211_channel *chan = params->channel;
476 struct cfg80211_bss *bss;
477
478 if (!test_bit(IWM_STATUS_READY, &iwm->status))
479 return -EIO;
480
481 /* UMAC doesn't support creating IBSS network with specified bssid.
482 * This should be removed after we have join only mode supported. */
483 if (params->bssid)
484 return -EOPNOTSUPP;
485
486 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
487 params->ssid, params->ssid_len);
488 if (!bss) {
489 iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
490 schedule_timeout_interruptible(2 * HZ);
491 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
492 params->ssid, params->ssid_len);
493 }
494 /* IBSS join only mode is not supported by UMAC ATM */
495 if (bss) {
496 cfg80211_put_bss(bss);
497 return -EOPNOTSUPP;
498 }
499
500 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
501 iwm->umac_profile->ibss.band = chan->band;
502 iwm->umac_profile->ibss.channel = iwm->channel;
503 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
504 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
505
506 if (params->bssid)
507 memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
508
509 return iwm_send_mlme_profile(iwm);
510}
511
512static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
513{
514 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
515
516 if (iwm->umac_profile_active)
517 return iwm_invalidate_mlme_profile(iwm);
518
519 return 0;
520}
521
Samuel Ortiz9967d462009-07-16 17:34:10 +0800522static int iwm_set_auth_type(struct iwm_priv *iwm,
523 enum nl80211_auth_type sme_auth_type)
524{
525 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
526
527 switch (sme_auth_type) {
528 case NL80211_AUTHTYPE_AUTOMATIC:
529 case NL80211_AUTHTYPE_OPEN_SYSTEM:
530 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
531 *auth_type = UMAC_AUTH_TYPE_OPEN;
532 break;
533 case NL80211_AUTHTYPE_SHARED_KEY:
534 if (iwm->umac_profile->sec.flags &
535 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
536 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
537 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
538 } else {
539 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
540 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
541 }
542
543 break;
544 default:
545 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
546 return -ENOTSUPP;
547 }
548
549 return 0;
550}
551
552static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
553{
554 if (!wpa_version) {
555 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
556 return 0;
557 }
558
559 if (wpa_version & NL80211_WPA_VERSION_2)
560 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
561
562 if (wpa_version & NL80211_WPA_VERSION_1)
563 iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK;
564
565 return 0;
566}
567
568static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
569{
570 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
571 &iwm->umac_profile->sec.mcast_cipher;
572
573 if (!cipher) {
574 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
575 return 0;
576 }
577
578 switch (cipher) {
579 case IW_AUTH_CIPHER_NONE:
580 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
581 break;
582 case WLAN_CIPHER_SUITE_WEP40:
583 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
584 break;
585 case WLAN_CIPHER_SUITE_WEP104:
586 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
587 break;
588 case WLAN_CIPHER_SUITE_TKIP:
589 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
590 break;
591 case WLAN_CIPHER_SUITE_CCMP:
592 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
593 break;
594 default:
595 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
596 return -ENOTSUPP;
597 }
598
599 return 0;
600}
601
602static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
603{
604 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
605
606 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
607
608 if (key_mgt == WLAN_AKM_SUITE_8021X)
609 *auth_type = UMAC_AUTH_TYPE_8021X;
610 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
611 if (iwm->umac_profile->sec.flags &
612 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
613 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
614 else
615 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
616 } else {
617 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
618 return -EINVAL;
619 }
620
621 return 0;
622}
623
624
625static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
626 struct cfg80211_connect_params *sme)
627{
628 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
629 struct ieee80211_channel *chan = sme->channel;
630 int ret;
631
632 if (!test_bit(IWM_STATUS_READY, &iwm->status))
633 return -EIO;
634
635 if (!sme->ssid)
636 return -EINVAL;
637
638 if (chan)
639 iwm->channel =
640 ieee80211_frequency_to_channel(chan->center_freq);
641
642 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
643 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
644
645 if (sme->bssid) {
646 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
647 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
648 iwm->umac_profile->bss_num = 1;
649 } else {
650 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
651 iwm->umac_profile->bss_num = 0;
652 }
653
654 ret = iwm_set_auth_type(iwm, sme->auth_type);
655 if (ret < 0)
656 return ret;
657
658 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
659 if (ret < 0)
660 return ret;
661
662 if (sme->crypto.n_ciphers_pairwise) {
663 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
664 true);
665 if (ret < 0)
666 return ret;
667 }
668
669 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
670 if (ret < 0)
671 return ret;
672
673 if (sme->crypto.n_akm_suites) {
674 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
675 if (ret < 0)
676 return ret;
677 }
678
679 return iwm_send_mlme_profile(iwm);
680}
681
682static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
683 u16 reason_code)
684{
685 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
686
687 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
688
689 if (iwm->umac_profile_active)
690 return iwm_invalidate_mlme_profile(iwm);
691
692 return 0;
693}
694
Zhu Yi257862f2009-06-15 21:59:56 +0200695static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
696 enum tx_power_setting type, int dbm)
697{
698 switch (type) {
699 case TX_POWER_AUTOMATIC:
700 return 0;
701 default:
702 return -EOPNOTSUPP;
703 }
704
705 return 0;
706}
707
708static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
709{
710 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
711
712 *dbm = iwm->txpower;
713
714 return 0;
715}
716
Johannes Bergbc92afd2009-07-01 21:26:57 +0200717static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
718 struct net_device *dev,
719 bool enabled, int timeout)
720{
721 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
722 u32 power_index;
723
724 if (enabled)
725 power_index = IWM_POWER_INDEX_DEFAULT;
726 else
727 power_index = IWM_POWER_INDEX_MIN;
728
729 if (power_index == iwm->conf.power_index)
730 return 0;
731
732 iwm->conf.power_index = power_index;
733
734 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
735 CFG_POWER_INDEX, iwm->conf.power_index);
736}
737
Zhu Yibb9f8692009-05-21 21:20:45 +0800738static struct cfg80211_ops iwm_cfg80211_ops = {
739 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200740 .add_key = iwm_cfg80211_add_key,
741 .get_key = iwm_cfg80211_get_key,
742 .del_key = iwm_cfg80211_del_key,
743 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800744 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800745 .scan = iwm_cfg80211_scan,
746 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800747 .connect = iwm_cfg80211_connect,
748 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800749 .join_ibss = iwm_cfg80211_join_ibss,
750 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200751 .set_tx_power = iwm_cfg80211_set_txpower,
752 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200753 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Zhu Yibb9f8692009-05-21 21:20:45 +0800754};
755
Zhu Yi49b77722009-07-16 17:34:08 +0800756static const u32 cipher_suites[] = {
757 WLAN_CIPHER_SUITE_WEP40,
758 WLAN_CIPHER_SUITE_WEP104,
759 WLAN_CIPHER_SUITE_TKIP,
760 WLAN_CIPHER_SUITE_CCMP,
761};
762
Zhu Yibb9f8692009-05-21 21:20:45 +0800763struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
764{
765 int ret = 0;
766 struct wireless_dev *wdev;
767
768 /*
769 * We're trying to have the following memory
770 * layout:
771 *
772 * +-------------------------+
773 * | struct wiphy |
774 * +-------------------------+
775 * | struct iwm_priv |
776 * +-------------------------+
777 * | bus private data |
778 * | (e.g. iwm_priv_sdio) |
779 * +-------------------------+
780 *
781 */
782
783 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
784 if (!wdev) {
785 dev_err(dev, "Couldn't allocate wireless device\n");
786 return ERR_PTR(-ENOMEM);
787 }
788
789 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
790 sizeof(struct iwm_priv) + sizeof_bus);
791 if (!wdev->wiphy) {
792 dev_err(dev, "Couldn't allocate wiphy device\n");
793 ret = -ENOMEM;
794 goto out_err_new;
795 }
796
797 set_wiphy_dev(wdev->wiphy, dev);
798 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
799 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
800 BIT(NL80211_IFTYPE_ADHOC);
801 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
802 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
803 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
804
Zhu Yi49b77722009-07-16 17:34:08 +0800805 wdev->wiphy->cipher_suites = cipher_suites;
806 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
807
Zhu Yibb9f8692009-05-21 21:20:45 +0800808 ret = wiphy_register(wdev->wiphy);
809 if (ret < 0) {
810 dev_err(dev, "Couldn't register wiphy device\n");
811 goto out_err_register;
812 }
813
814 return wdev;
815
816 out_err_register:
817 wiphy_free(wdev->wiphy);
818
819 out_err_new:
820 kfree(wdev);
821
822 return ERR_PTR(ret);
823}
824
825void iwm_wdev_free(struct iwm_priv *iwm)
826{
827 struct wireless_dev *wdev = iwm_to_wdev(iwm);
828
829 if (!wdev)
830 return;
831
832 wiphy_unregister(wdev->wiphy);
833 wiphy_free(wdev->wiphy);
834 kfree(wdev);
835}