blob: a6e852f4f92ce7c8e663ace8b441f1bada99a22a [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
329 if (iwm->umac_profile_active) {
330 int ret = iwm_invalidate_mlme_profile(iwm);
331 if (ret < 0)
332 IWM_ERR(iwm, "Couldn't invalidate profile\n");
333 }
334
335 return 0;
336}
337
338static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
339 struct cfg80211_scan_request *request)
340{
341 struct iwm_priv *iwm = ndev_to_iwm(ndev);
342 int ret;
343
344 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
345 IWM_ERR(iwm, "Scan while device is not ready\n");
346 return -EIO;
347 }
348
349 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
350 IWM_ERR(iwm, "Scanning already\n");
351 return -EAGAIN;
352 }
353
354 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
355 IWM_ERR(iwm, "Scanning being aborted\n");
356 return -EAGAIN;
357 }
358
359 set_bit(IWM_STATUS_SCANNING, &iwm->status);
360
361 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
362 if (ret) {
363 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
364 return ret;
365 }
366
367 iwm->scan_request = request;
368 return 0;
369}
370
371static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
372{
373 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
374
375 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
376 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
377 int ret;
378
379 iwm->conf.rts_threshold = wiphy->rts_threshold;
380
381 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
382 CFG_RTS_THRESHOLD,
383 iwm->conf.rts_threshold);
384 if (ret < 0)
385 return ret;
386 }
387
388 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
389 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
390 int ret;
391
392 iwm->conf.frag_threshold = wiphy->frag_threshold;
393
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800394 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800395 CFG_FRAG_THRESHOLD,
396 iwm->conf.frag_threshold);
397 if (ret < 0)
398 return ret;
399 }
400
401 return 0;
402}
403
404static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
405 struct cfg80211_ibss_params *params)
406{
407 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
408 struct ieee80211_channel *chan = params->channel;
409 struct cfg80211_bss *bss;
410
411 if (!test_bit(IWM_STATUS_READY, &iwm->status))
412 return -EIO;
413
414 /* UMAC doesn't support creating IBSS network with specified bssid.
415 * This should be removed after we have join only mode supported. */
416 if (params->bssid)
417 return -EOPNOTSUPP;
418
419 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
420 params->ssid, params->ssid_len);
421 if (!bss) {
422 iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
423 schedule_timeout_interruptible(2 * HZ);
424 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
425 params->ssid, params->ssid_len);
426 }
427 /* IBSS join only mode is not supported by UMAC ATM */
428 if (bss) {
429 cfg80211_put_bss(bss);
430 return -EOPNOTSUPP;
431 }
432
433 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
434 iwm->umac_profile->ibss.band = chan->band;
435 iwm->umac_profile->ibss.channel = iwm->channel;
436 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
437 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
438
439 if (params->bssid)
440 memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
441
442 return iwm_send_mlme_profile(iwm);
443}
444
445static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
446{
447 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
448
449 if (iwm->umac_profile_active)
450 return iwm_invalidate_mlme_profile(iwm);
451
452 return 0;
453}
454
Samuel Ortiz9967d462009-07-16 17:34:10 +0800455static int iwm_set_auth_type(struct iwm_priv *iwm,
456 enum nl80211_auth_type sme_auth_type)
457{
458 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
459
460 switch (sme_auth_type) {
461 case NL80211_AUTHTYPE_AUTOMATIC:
462 case NL80211_AUTHTYPE_OPEN_SYSTEM:
463 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
464 *auth_type = UMAC_AUTH_TYPE_OPEN;
465 break;
466 case NL80211_AUTHTYPE_SHARED_KEY:
467 if (iwm->umac_profile->sec.flags &
468 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
469 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
470 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
471 } else {
472 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
473 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
474 }
475
476 break;
477 default:
478 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
479 return -ENOTSUPP;
480 }
481
482 return 0;
483}
484
485static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
486{
Zhu Yi554503f2009-08-03 14:37:01 +0800487 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
488
Samuel Ortiz9967d462009-07-16 17:34:10 +0800489 if (!wpa_version) {
490 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
491 return 0;
492 }
493
494 if (wpa_version & NL80211_WPA_VERSION_2)
495 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
496
497 if (wpa_version & NL80211_WPA_VERSION_1)
498 iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK;
499
500 return 0;
501}
502
503static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
504{
505 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
506 &iwm->umac_profile->sec.mcast_cipher;
507
508 if (!cipher) {
509 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
510 return 0;
511 }
512
Zhu Yi554503f2009-08-03 14:37:01 +0800513 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
514 cipher);
515
Samuel Ortiz9967d462009-07-16 17:34:10 +0800516 switch (cipher) {
517 case IW_AUTH_CIPHER_NONE:
518 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
519 break;
520 case WLAN_CIPHER_SUITE_WEP40:
521 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
522 break;
523 case WLAN_CIPHER_SUITE_WEP104:
524 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
525 break;
526 case WLAN_CIPHER_SUITE_TKIP:
527 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
528 break;
529 case WLAN_CIPHER_SUITE_CCMP:
530 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
531 break;
532 default:
533 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
534 return -ENOTSUPP;
535 }
536
537 return 0;
538}
539
540static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
541{
542 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
543
544 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
545
546 if (key_mgt == WLAN_AKM_SUITE_8021X)
547 *auth_type = UMAC_AUTH_TYPE_8021X;
548 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
549 if (iwm->umac_profile->sec.flags &
550 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
551 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
552 else
553 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
554 } else {
555 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
556 return -EINVAL;
557 }
558
559 return 0;
560}
561
562
563static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
564 struct cfg80211_connect_params *sme)
565{
566 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
567 struct ieee80211_channel *chan = sme->channel;
568 int ret;
569
570 if (!test_bit(IWM_STATUS_READY, &iwm->status))
571 return -EIO;
572
573 if (!sme->ssid)
574 return -EINVAL;
575
576 if (chan)
577 iwm->channel =
578 ieee80211_frequency_to_channel(chan->center_freq);
579
580 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
581 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
582
583 if (sme->bssid) {
584 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
585 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
586 iwm->umac_profile->bss_num = 1;
587 } else {
588 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
589 iwm->umac_profile->bss_num = 0;
590 }
591
Zhu Yi554503f2009-08-03 14:37:01 +0800592 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800593 if (ret < 0)
594 return ret;
595
Zhu Yi554503f2009-08-03 14:37:01 +0800596 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800597 if (ret < 0)
598 return ret;
599
600 if (sme->crypto.n_ciphers_pairwise) {
601 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
602 true);
603 if (ret < 0)
604 return ret;
605 }
606
607 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
608 if (ret < 0)
609 return ret;
610
611 if (sme->crypto.n_akm_suites) {
612 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
613 if (ret < 0)
614 return ret;
615 }
616
617 return iwm_send_mlme_profile(iwm);
618}
619
620static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
621 u16 reason_code)
622{
623 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
624
625 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
626
627 if (iwm->umac_profile_active)
628 return iwm_invalidate_mlme_profile(iwm);
629
630 return 0;
631}
632
Zhu Yi257862f2009-06-15 21:59:56 +0200633static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
634 enum tx_power_setting type, int dbm)
635{
636 switch (type) {
637 case TX_POWER_AUTOMATIC:
638 return 0;
639 default:
640 return -EOPNOTSUPP;
641 }
642
643 return 0;
644}
645
646static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
647{
648 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
649
650 *dbm = iwm->txpower;
651
652 return 0;
653}
654
Johannes Bergbc92afd2009-07-01 21:26:57 +0200655static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
656 struct net_device *dev,
657 bool enabled, int timeout)
658{
659 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
660 u32 power_index;
661
662 if (enabled)
663 power_index = IWM_POWER_INDEX_DEFAULT;
664 else
665 power_index = IWM_POWER_INDEX_MIN;
666
667 if (power_index == iwm->conf.power_index)
668 return 0;
669
670 iwm->conf.power_index = power_index;
671
672 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
673 CFG_POWER_INDEX, iwm->conf.power_index);
674}
675
Zhu Yibb9f8692009-05-21 21:20:45 +0800676static struct cfg80211_ops iwm_cfg80211_ops = {
677 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200678 .add_key = iwm_cfg80211_add_key,
679 .get_key = iwm_cfg80211_get_key,
680 .del_key = iwm_cfg80211_del_key,
681 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800682 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800683 .scan = iwm_cfg80211_scan,
684 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800685 .connect = iwm_cfg80211_connect,
686 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800687 .join_ibss = iwm_cfg80211_join_ibss,
688 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200689 .set_tx_power = iwm_cfg80211_set_txpower,
690 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200691 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Zhu Yibb9f8692009-05-21 21:20:45 +0800692};
693
Zhu Yi49b77722009-07-16 17:34:08 +0800694static const u32 cipher_suites[] = {
695 WLAN_CIPHER_SUITE_WEP40,
696 WLAN_CIPHER_SUITE_WEP104,
697 WLAN_CIPHER_SUITE_TKIP,
698 WLAN_CIPHER_SUITE_CCMP,
699};
700
Zhu Yibb9f8692009-05-21 21:20:45 +0800701struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
702{
703 int ret = 0;
704 struct wireless_dev *wdev;
705
706 /*
707 * We're trying to have the following memory
708 * layout:
709 *
710 * +-------------------------+
711 * | struct wiphy |
712 * +-------------------------+
713 * | struct iwm_priv |
714 * +-------------------------+
715 * | bus private data |
716 * | (e.g. iwm_priv_sdio) |
717 * +-------------------------+
718 *
719 */
720
721 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
722 if (!wdev) {
723 dev_err(dev, "Couldn't allocate wireless device\n");
724 return ERR_PTR(-ENOMEM);
725 }
726
727 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
728 sizeof(struct iwm_priv) + sizeof_bus);
729 if (!wdev->wiphy) {
730 dev_err(dev, "Couldn't allocate wiphy device\n");
731 ret = -ENOMEM;
732 goto out_err_new;
733 }
734
735 set_wiphy_dev(wdev->wiphy, dev);
736 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
737 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
738 BIT(NL80211_IFTYPE_ADHOC);
739 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
740 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
741 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
742
Zhu Yi49b77722009-07-16 17:34:08 +0800743 wdev->wiphy->cipher_suites = cipher_suites;
744 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
745
Zhu Yibb9f8692009-05-21 21:20:45 +0800746 ret = wiphy_register(wdev->wiphy);
747 if (ret < 0) {
748 dev_err(dev, "Couldn't register wiphy device\n");
749 goto out_err_register;
750 }
751
752 return wdev;
753
754 out_err_register:
755 wiphy_free(wdev->wiphy);
756
757 out_err_new:
758 kfree(wdev);
759
760 return ERR_PTR(ret);
761}
762
763void iwm_wdev_free(struct iwm_priv *iwm)
764{
765 struct wireless_dev *wdev = iwm_to_wdev(iwm);
766
767 if (!wdev)
768 return;
769
770 wiphy_unregister(wdev->wiphy);
771 wiphy_free(wdev->wiphy);
772 kfree(wdev);
773}