blob: c6c0eff9b5ed33f32c51223629ddc1e2cf118d71 [file] [log] [blame]
Zhu Yibb9f8692009-05-21 21:20:45 +08001/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
5 * Samuel Ortiz <samuel.ortiz@intel.com>
6 * Zhu Yi <yi.zhu@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version
10 * 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 */
23
24#include <linux/kernel.h>
25#include <linux/netdevice.h>
Alexey Dobriyand43c36d2009-10-07 17:09:06 +040026#include <linux/sched.h>
Samuel Ortiz13e0fe702009-06-15 21:59:52 +020027#include <linux/etherdevice.h>
Zhu Yibb9f8692009-05-21 21:20:45 +080028#include <linux/wireless.h>
29#include <linux/ieee80211.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Zhu Yibb9f8692009-05-21 21:20:45 +080031#include <net/cfg80211.h>
32
33#include "iwm.h"
34#include "commands.h"
35#include "cfg80211.h"
36#include "debug.h"
37
38#define RATETAB_ENT(_rate, _rateid, _flags) \
39 { \
40 .bitrate = (_rate), \
41 .hw_value = (_rateid), \
42 .flags = (_flags), \
43 }
44
45#define CHAN2G(_channel, _freq, _flags) { \
46 .band = IEEE80211_BAND_2GHZ, \
47 .center_freq = (_freq), \
48 .hw_value = (_channel), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
54#define CHAN5G(_channel, _flags) { \
55 .band = IEEE80211_BAND_5GHZ, \
56 .center_freq = 5000 + (5 * (_channel)), \
57 .hw_value = (_channel), \
58 .flags = (_flags), \
59 .max_antenna_gain = 0, \
60 .max_power = 30, \
61}
62
63static struct ieee80211_rate iwm_rates[] = {
64 RATETAB_ENT(10, 0x1, 0),
65 RATETAB_ENT(20, 0x2, 0),
66 RATETAB_ENT(55, 0x4, 0),
67 RATETAB_ENT(110, 0x8, 0),
68 RATETAB_ENT(60, 0x10, 0),
69 RATETAB_ENT(90, 0x20, 0),
70 RATETAB_ENT(120, 0x40, 0),
71 RATETAB_ENT(180, 0x80, 0),
72 RATETAB_ENT(240, 0x100, 0),
73 RATETAB_ENT(360, 0x200, 0),
74 RATETAB_ENT(480, 0x400, 0),
75 RATETAB_ENT(540, 0x800, 0),
76};
77
78#define iwm_a_rates (iwm_rates + 4)
79#define iwm_a_rates_size 8
80#define iwm_g_rates (iwm_rates + 0)
81#define iwm_g_rates_size 12
82
83static struct ieee80211_channel iwm_2ghz_channels[] = {
84 CHAN2G(1, 2412, 0),
85 CHAN2G(2, 2417, 0),
86 CHAN2G(3, 2422, 0),
87 CHAN2G(4, 2427, 0),
88 CHAN2G(5, 2432, 0),
89 CHAN2G(6, 2437, 0),
90 CHAN2G(7, 2442, 0),
91 CHAN2G(8, 2447, 0),
92 CHAN2G(9, 2452, 0),
93 CHAN2G(10, 2457, 0),
94 CHAN2G(11, 2462, 0),
95 CHAN2G(12, 2467, 0),
96 CHAN2G(13, 2472, 0),
97 CHAN2G(14, 2484, 0),
98};
99
100static struct ieee80211_channel iwm_5ghz_a_channels[] = {
101 CHAN5G(34, 0), CHAN5G(36, 0),
102 CHAN5G(38, 0), CHAN5G(40, 0),
103 CHAN5G(42, 0), CHAN5G(44, 0),
104 CHAN5G(46, 0), CHAN5G(48, 0),
105 CHAN5G(52, 0), CHAN5G(56, 0),
106 CHAN5G(60, 0), CHAN5G(64, 0),
107 CHAN5G(100, 0), CHAN5G(104, 0),
108 CHAN5G(108, 0), CHAN5G(112, 0),
109 CHAN5G(116, 0), CHAN5G(120, 0),
110 CHAN5G(124, 0), CHAN5G(128, 0),
111 CHAN5G(132, 0), CHAN5G(136, 0),
112 CHAN5G(140, 0), CHAN5G(149, 0),
113 CHAN5G(153, 0), CHAN5G(157, 0),
114 CHAN5G(161, 0), CHAN5G(165, 0),
115 CHAN5G(184, 0), CHAN5G(188, 0),
116 CHAN5G(192, 0), CHAN5G(196, 0),
117 CHAN5G(200, 0), CHAN5G(204, 0),
118 CHAN5G(208, 0), CHAN5G(212, 0),
119 CHAN5G(216, 0),
120};
121
122static struct ieee80211_supported_band iwm_band_2ghz = {
123 .channels = iwm_2ghz_channels,
124 .n_channels = ARRAY_SIZE(iwm_2ghz_channels),
125 .bitrates = iwm_g_rates,
126 .n_bitrates = iwm_g_rates_size,
127};
128
129static struct ieee80211_supported_band iwm_band_5ghz = {
130 .channels = iwm_5ghz_a_channels,
131 .n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
132 .bitrates = iwm_a_rates,
133 .n_bitrates = iwm_a_rates_size,
134};
135
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200136static int iwm_key_init(struct iwm_key *key, u8 key_index,
137 const u8 *mac_addr, struct key_params *params)
138{
139 key->hdr.key_idx = key_index;
140 if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
141 key->hdr.multicast = 1;
142 memset(key->hdr.mac, 0xff, ETH_ALEN);
143 } else {
144 key->hdr.multicast = 0;
145 memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
146 }
147
148 if (params) {
149 if (params->key_len > WLAN_MAX_KEY_LEN ||
150 params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
151 return -EINVAL;
152
153 key->cipher = params->cipher;
154 key->key_len = params->key_len;
155 key->seq_len = params->seq_len;
156 memcpy(key->key, params->key, key->key_len);
157 memcpy(key->seq, params->seq, key->seq_len);
158 }
159
160 return 0;
161}
162
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200163static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berge31b8212010-10-05 19:39:30 +0200164 u8 key_index, bool pairwise, const u8 *mac_addr,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200165 struct key_params *params)
166{
167 struct iwm_priv *iwm = ndev_to_iwm(ndev);
168 struct iwm_key *key = &iwm->keys[key_index];
169 int ret;
170
171 IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
172
173 memset(key, 0, sizeof(struct iwm_key));
174 ret = iwm_key_init(key, key_index, mac_addr, params);
175 if (ret < 0) {
176 IWM_ERR(iwm, "Invalid key_params\n");
177 return ret;
178 }
179
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200180 return iwm_set_key(iwm, 0, key);
181}
182
183static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berge31b8212010-10-05 19:39:30 +0200184 u8 key_index, bool pairwise, const u8 *mac_addr,
185 void *cookie,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200186 void (*callback)(void *cookie,
187 struct key_params*))
188{
189 struct iwm_priv *iwm = ndev_to_iwm(ndev);
190 struct iwm_key *key = &iwm->keys[key_index];
191 struct key_params params;
192
193 IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
194
195 memset(&params, 0, sizeof(params));
196
197 params.cipher = key->cipher;
198 params.key_len = key->key_len;
199 params.seq_len = key->seq_len;
200 params.seq = key->seq;
201 params.key = key->key;
202
203 callback(cookie, &params);
204
205 return key->key_len ? 0 : -ENOENT;
206}
207
208
209static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berge31b8212010-10-05 19:39:30 +0200210 u8 key_index, bool pairwise, const u8 *mac_addr)
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200211{
212 struct iwm_priv *iwm = ndev_to_iwm(ndev);
213 struct iwm_key *key = &iwm->keys[key_index];
214
215 if (!iwm->keys[key_index].key_len) {
216 IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
217 return 0;
218 }
219
220 if (key_index == iwm->default_key)
221 iwm->default_key = -1;
222
223 return iwm_set_key(iwm, 1, key);
224}
225
226static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
227 struct net_device *ndev,
228 u8 key_index)
229{
230 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200231
232 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
233
234 if (!iwm->keys[key_index].key_len) {
235 IWM_ERR(iwm, "Key %d not used\n", key_index);
236 return -EINVAL;
237 }
238
Samuel Ortiz35497162009-06-15 21:59:54 +0200239 iwm->default_key = key_index;
240
Zhu Yi6e5db0a2009-07-16 17:34:13 +0800241 return iwm_set_tx_key(iwm, key_index);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200242}
243
Samuel Ortizd0418112009-09-01 15:14:00 +0200244static int iwm_cfg80211_get_station(struct wiphy *wiphy,
245 struct net_device *ndev,
246 u8 *mac, struct station_info *sinfo)
Samuel Ortiz9967d462009-07-16 17:34:10 +0800247{
248 struct iwm_priv *iwm = ndev_to_iwm(ndev);
249
250 if (memcmp(mac, iwm->bssid, ETH_ALEN))
251 return -ENOENT;
252
253 sinfo->filled |= STATION_INFO_TX_BITRATE;
254 sinfo->txrate.legacy = iwm->rate * 10;
255
256 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
257 sinfo->filled |= STATION_INFO_SIGNAL;
258 sinfo->signal = iwm->wstats.qual.level;
259 }
260
261 return 0;
262}
263
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200264
Zhu Yibb9f8692009-05-21 21:20:45 +0800265int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
266{
267 struct wiphy *wiphy = iwm_to_wiphy(iwm);
Zhu Yi04d1c222010-02-25 14:15:26 +0800268 struct iwm_bss_info *bss;
Zhu Yibb9f8692009-05-21 21:20:45 +0800269 struct iwm_umac_notif_bss_info *umac_bss;
270 struct ieee80211_mgmt *mgmt;
271 struct ieee80211_channel *channel;
272 struct ieee80211_supported_band *band;
273 s32 signal;
274 int freq;
275
Zhu Yi04d1c222010-02-25 14:15:26 +0800276 list_for_each_entry(bss, &iwm->bss_list, node) {
Zhu Yibb9f8692009-05-21 21:20:45 +0800277 umac_bss = bss->bss;
278 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
279
280 if (umac_bss->band == UMAC_BAND_2GHZ)
281 band = wiphy->bands[IEEE80211_BAND_2GHZ];
282 else if (umac_bss->band == UMAC_BAND_5GHZ)
283 band = wiphy->bands[IEEE80211_BAND_5GHZ];
284 else {
285 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
286 return -EINVAL;
287 }
288
289 freq = ieee80211_channel_to_frequency(umac_bss->channel);
290 channel = ieee80211_get_channel(wiphy, freq);
291 signal = umac_bss->rssi * 100;
292
293 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
294 le16_to_cpu(umac_bss->frame_len),
295 signal, GFP_KERNEL))
296 return -EINVAL;
297 }
298
299 return 0;
300}
301
Johannes Berge36d56b2009-06-09 21:04:43 +0200302static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
303 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800304 enum nl80211_iftype type, u32 *flags,
305 struct vif_params *params)
306{
Zhu Yibb9f8692009-05-21 21:20:45 +0800307 struct wireless_dev *wdev;
308 struct iwm_priv *iwm;
309 u32 old_mode;
310
Zhu Yibb9f8692009-05-21 21:20:45 +0800311 wdev = ndev->ieee80211_ptr;
312 iwm = ndev_to_iwm(ndev);
313 old_mode = iwm->conf.mode;
314
315 switch (type) {
316 case NL80211_IFTYPE_STATION:
317 iwm->conf.mode = UMAC_MODE_BSS;
318 break;
319 case NL80211_IFTYPE_ADHOC:
320 iwm->conf.mode = UMAC_MODE_IBSS;
321 break;
322 default:
323 return -EOPNOTSUPP;
324 }
325
326 wdev->iftype = type;
327
328 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
329 return 0;
330
331 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
332
Zhu Yiae73abf2009-09-01 15:13:58 +0200333 if (iwm->umac_profile_active)
334 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800335
336 return 0;
337}
338
339static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
340 struct cfg80211_scan_request *request)
341{
342 struct iwm_priv *iwm = ndev_to_iwm(ndev);
343 int ret;
344
345 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
346 IWM_ERR(iwm, "Scan while device is not ready\n");
347 return -EIO;
348 }
349
350 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
351 IWM_ERR(iwm, "Scanning already\n");
352 return -EAGAIN;
353 }
354
355 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
356 IWM_ERR(iwm, "Scanning being aborted\n");
357 return -EAGAIN;
358 }
359
360 set_bit(IWM_STATUS_SCANNING, &iwm->status);
361
362 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
363 if (ret) {
364 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
365 return ret;
366 }
367
368 iwm->scan_request = request;
369 return 0;
370}
371
372static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
373{
374 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
375
376 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
377 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
378 int ret;
379
380 iwm->conf.rts_threshold = wiphy->rts_threshold;
381
382 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
383 CFG_RTS_THRESHOLD,
384 iwm->conf.rts_threshold);
385 if (ret < 0)
386 return ret;
387 }
388
389 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
390 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
391 int ret;
392
393 iwm->conf.frag_threshold = wiphy->frag_threshold;
394
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800395 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800396 CFG_FRAG_THRESHOLD,
397 iwm->conf.frag_threshold);
398 if (ret < 0)
399 return ret;
400 }
401
402 return 0;
403}
404
405static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
406 struct cfg80211_ibss_params *params)
407{
408 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
409 struct ieee80211_channel *chan = params->channel;
Zhu Yibb9f8692009-05-21 21:20:45 +0800410
411 if (!test_bit(IWM_STATUS_READY, &iwm->status))
412 return -EIO;
413
Zhu Yi03d1a622009-10-16 13:18:46 +0800414 /* UMAC doesn't support creating or joining an IBSS network
415 * with specified bssid. */
Zhu Yibb9f8692009-05-21 21:20:45 +0800416 if (params->bssid)
417 return -EOPNOTSUPP;
418
Zhu Yibb9f8692009-05-21 21:20:45 +0800419 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
420 iwm->umac_profile->ibss.band = chan->band;
421 iwm->umac_profile->ibss.channel = iwm->channel;
422 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
423 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
424
Zhu Yibb9f8692009-05-21 21:20:45 +0800425 return iwm_send_mlme_profile(iwm);
426}
427
428static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
429{
430 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
431
432 if (iwm->umac_profile_active)
433 return iwm_invalidate_mlme_profile(iwm);
434
435 return 0;
436}
437
Samuel Ortiz9967d462009-07-16 17:34:10 +0800438static int iwm_set_auth_type(struct iwm_priv *iwm,
439 enum nl80211_auth_type sme_auth_type)
440{
441 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
442
443 switch (sme_auth_type) {
444 case NL80211_AUTHTYPE_AUTOMATIC:
445 case NL80211_AUTHTYPE_OPEN_SYSTEM:
446 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
447 *auth_type = UMAC_AUTH_TYPE_OPEN;
448 break;
449 case NL80211_AUTHTYPE_SHARED_KEY:
450 if (iwm->umac_profile->sec.flags &
451 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
452 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
453 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
454 } else {
455 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
456 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
457 }
458
459 break;
460 default:
461 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
462 return -ENOTSUPP;
463 }
464
465 return 0;
466}
467
468static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
469{
Zhu Yi554503f2009-08-03 14:37:01 +0800470 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
471
Samuel Ortiz9967d462009-07-16 17:34:10 +0800472 if (!wpa_version) {
473 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
474 return 0;
475 }
476
Samuel Ortiz6a79c9f2009-10-16 13:18:49 +0800477 if (wpa_version & NL80211_WPA_VERSION_1)
478 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
479
Samuel Ortiz9967d462009-07-16 17:34:10 +0800480 if (wpa_version & NL80211_WPA_VERSION_2)
481 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
482
Samuel Ortiz9967d462009-07-16 17:34:10 +0800483 return 0;
484}
485
486static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
487{
488 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
489 &iwm->umac_profile->sec.mcast_cipher;
490
491 if (!cipher) {
492 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
493 return 0;
494 }
495
Zhu Yi554503f2009-08-03 14:37:01 +0800496 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
497 cipher);
498
Samuel Ortiz9967d462009-07-16 17:34:10 +0800499 switch (cipher) {
500 case IW_AUTH_CIPHER_NONE:
501 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
502 break;
503 case WLAN_CIPHER_SUITE_WEP40:
504 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
505 break;
506 case WLAN_CIPHER_SUITE_WEP104:
507 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
508 break;
509 case WLAN_CIPHER_SUITE_TKIP:
510 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
511 break;
512 case WLAN_CIPHER_SUITE_CCMP:
513 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
514 break;
515 default:
516 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
517 return -ENOTSUPP;
518 }
519
520 return 0;
521}
522
523static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
524{
525 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
526
527 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
528
529 if (key_mgt == WLAN_AKM_SUITE_8021X)
530 *auth_type = UMAC_AUTH_TYPE_8021X;
531 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
532 if (iwm->umac_profile->sec.flags &
533 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
534 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
535 else
536 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
537 } else {
538 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
539 return -EINVAL;
540 }
541
542 return 0;
543}
544
545
546static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
547 struct cfg80211_connect_params *sme)
548{
549 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
550 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200551 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800552 int ret;
553
554 if (!test_bit(IWM_STATUS_READY, &iwm->status))
555 return -EIO;
556
557 if (!sme->ssid)
558 return -EINVAL;
559
Zhu Yiae73abf2009-09-01 15:13:58 +0200560 if (iwm->umac_profile_active) {
561 ret = iwm_invalidate_mlme_profile(iwm);
562 if (ret) {
563 IWM_ERR(iwm, "Couldn't invalidate profile\n");
564 return ret;
565 }
566 }
567
Samuel Ortiz9967d462009-07-16 17:34:10 +0800568 if (chan)
569 iwm->channel =
570 ieee80211_frequency_to_channel(chan->center_freq);
571
572 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
573 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
574
575 if (sme->bssid) {
576 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
577 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
578 iwm->umac_profile->bss_num = 1;
579 } else {
580 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
581 iwm->umac_profile->bss_num = 0;
582 }
583
Zhu Yi554503f2009-08-03 14:37:01 +0800584 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800585 if (ret < 0)
586 return ret;
587
Zhu Yi554503f2009-08-03 14:37:01 +0800588 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800589 if (ret < 0)
590 return ret;
591
592 if (sme->crypto.n_ciphers_pairwise) {
593 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
594 true);
595 if (ret < 0)
596 return ret;
597 }
598
599 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
600 if (ret < 0)
601 return ret;
602
603 if (sme->crypto.n_akm_suites) {
604 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
605 if (ret < 0)
606 return ret;
607 }
608
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200609 /*
610 * We save the WEP key in case we want to do shared authentication.
611 * We have to do it so because UMAC will assert whenever it gets a
612 * key before a profile.
613 */
614 if (sme->key) {
615 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
616 if (key_param.key == NULL)
617 return -ENOMEM;
618 key_param.key_len = sme->key_len;
619 key_param.seq_len = 0;
620 key_param.cipher = sme->crypto.ciphers_pairwise[0];
621
622 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
623 NULL, &key_param);
624 kfree(key_param.key);
625 if (ret < 0) {
626 IWM_ERR(iwm, "Invalid key_params\n");
627 return ret;
628 }
629
630 iwm->default_key = sme->key_idx;
631 }
632
Samuel Ortiza82aedb2009-10-16 13:18:47 +0800633 /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
634 if ((iwm->umac_profile->sec.flags &
635 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
636 iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
637 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
638 }
639
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200640 ret = iwm_send_mlme_profile(iwm);
641
642 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
643 sme->key == NULL)
644 return ret;
645
646 /*
647 * We want to do shared auth.
648 * We need to actually set the key we previously cached,
649 * and then tell the UMAC it's the default one.
650 * That will trigger the auth+assoc UMAC machinery, and again,
651 * this must be done after setting the profile.
652 */
653 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
654 if (ret < 0)
655 return ret;
656
657 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800658}
659
660static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
661 u16 reason_code)
662{
663 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
664
665 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
666
667 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200668 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800669
670 return 0;
671}
672
Zhu Yi257862f2009-06-15 21:59:56 +0200673static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300674 enum nl80211_tx_power_setting type, int mbm)
Zhu Yi257862f2009-06-15 21:59:56 +0200675{
Samuel Ortiz88e61952009-10-16 13:18:53 +0800676 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
677 int ret;
678
Zhu Yi257862f2009-06-15 21:59:56 +0200679 switch (type) {
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300680 case NL80211_TX_POWER_AUTOMATIC:
Zhu Yi257862f2009-06-15 21:59:56 +0200681 return 0;
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300682 case NL80211_TX_POWER_FIXED:
683 if (mbm < 0 || (mbm % 100))
684 return -EOPNOTSUPP;
685
Samuel Ortizfe191762009-11-24 11:33:28 +0800686 if (!test_bit(IWM_STATUS_READY, &iwm->status))
687 return 0;
688
Samuel Ortiz88e61952009-10-16 13:18:53 +0800689 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300690 CFG_TX_PWR_LIMIT_USR,
691 MBM_TO_DBM(mbm) * 2);
Samuel Ortiz88e61952009-10-16 13:18:53 +0800692 if (ret < 0)
693 return ret;
694
695 return iwm_tx_power_trigger(iwm);
Zhu Yi257862f2009-06-15 21:59:56 +0200696 default:
Samuel Ortizfe191762009-11-24 11:33:28 +0800697 IWM_ERR(iwm, "Unsupported power type: %d\n", type);
Zhu Yi257862f2009-06-15 21:59:56 +0200698 return -EOPNOTSUPP;
699 }
700
701 return 0;
702}
703
704static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
705{
706 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
707
Samuel Ortiz88e61952009-10-16 13:18:53 +0800708 *dbm = iwm->txpower >> 1;
Zhu Yi257862f2009-06-15 21:59:56 +0200709
710 return 0;
711}
712
Johannes Bergbc92afd2009-07-01 21:26:57 +0200713static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
714 struct net_device *dev,
715 bool enabled, int timeout)
716{
717 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
718 u32 power_index;
719
720 if (enabled)
721 power_index = IWM_POWER_INDEX_DEFAULT;
722 else
723 power_index = IWM_POWER_INDEX_MIN;
724
725 if (power_index == iwm->conf.power_index)
726 return 0;
727
728 iwm->conf.power_index = power_index;
729
730 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
731 CFG_POWER_INDEX, iwm->conf.power_index);
732}
733
Zhu Yid281fd42010-02-25 14:15:30 +0800734static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy,
735 struct net_device *netdev,
736 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100737{
738 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
739
740 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
741}
742
Zhu Yid281fd42010-02-25 14:15:30 +0800743static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy,
744 struct net_device *netdev,
745 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100746{
747 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
748
749 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
750}
751
Zhu Yid281fd42010-02-25 14:15:30 +0800752static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy,
753 struct net_device *netdev)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100754{
755 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
756 struct cfg80211_pmksa pmksa;
757
758 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
759
760 return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
761}
762
763
Zhu Yibb9f8692009-05-21 21:20:45 +0800764static struct cfg80211_ops iwm_cfg80211_ops = {
765 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200766 .add_key = iwm_cfg80211_add_key,
767 .get_key = iwm_cfg80211_get_key,
768 .del_key = iwm_cfg80211_del_key,
769 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800770 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800771 .scan = iwm_cfg80211_scan,
772 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800773 .connect = iwm_cfg80211_connect,
774 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800775 .join_ibss = iwm_cfg80211_join_ibss,
776 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200777 .set_tx_power = iwm_cfg80211_set_txpower,
778 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200779 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100780 .set_pmksa = iwm_cfg80211_set_pmksa,
781 .del_pmksa = iwm_cfg80211_del_pmksa,
782 .flush_pmksa = iwm_cfg80211_flush_pmksa,
Zhu Yibb9f8692009-05-21 21:20:45 +0800783};
784
Zhu Yi49b77722009-07-16 17:34:08 +0800785static const u32 cipher_suites[] = {
786 WLAN_CIPHER_SUITE_WEP40,
787 WLAN_CIPHER_SUITE_WEP104,
788 WLAN_CIPHER_SUITE_TKIP,
789 WLAN_CIPHER_SUITE_CCMP,
790};
791
Zhu Yibb9f8692009-05-21 21:20:45 +0800792struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
793{
794 int ret = 0;
795 struct wireless_dev *wdev;
796
797 /*
798 * We're trying to have the following memory
799 * layout:
800 *
801 * +-------------------------+
802 * | struct wiphy |
803 * +-------------------------+
804 * | struct iwm_priv |
805 * +-------------------------+
806 * | bus private data |
807 * | (e.g. iwm_priv_sdio) |
808 * +-------------------------+
809 *
810 */
811
812 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
813 if (!wdev) {
814 dev_err(dev, "Couldn't allocate wireless device\n");
815 return ERR_PTR(-ENOMEM);
816 }
817
818 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
819 sizeof(struct iwm_priv) + sizeof_bus);
820 if (!wdev->wiphy) {
821 dev_err(dev, "Couldn't allocate wiphy device\n");
822 ret = -ENOMEM;
823 goto out_err_new;
824 }
825
826 set_wiphy_dev(wdev->wiphy, dev);
827 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100828 wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
Zhu Yibb9f8692009-05-21 21:20:45 +0800829 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
830 BIT(NL80211_IFTYPE_ADHOC);
831 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
832 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
833 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
834
Zhu Yi49b77722009-07-16 17:34:08 +0800835 wdev->wiphy->cipher_suites = cipher_suites;
836 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
837
Zhu Yibb9f8692009-05-21 21:20:45 +0800838 ret = wiphy_register(wdev->wiphy);
839 if (ret < 0) {
840 dev_err(dev, "Couldn't register wiphy device\n");
841 goto out_err_register;
842 }
843
844 return wdev;
845
846 out_err_register:
847 wiphy_free(wdev->wiphy);
848
849 out_err_new:
850 kfree(wdev);
851
852 return ERR_PTR(ret);
853}
854
855void iwm_wdev_free(struct iwm_priv *iwm)
856{
857 struct wireless_dev *wdev = iwm_to_wdev(iwm);
858
859 if (!wdev)
860 return;
861
862 wiphy_unregister(wdev->wiphy);
863 wiphy_free(wdev->wiphy);
864 kfree(wdev);
865}