blob: ed57e44028009e0b6dd515169416caa4d213029b [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,
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100228 u8 key_index, bool unicast,
229 bool multicast)
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200230{
231 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200232
233 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
234
235 if (!iwm->keys[key_index].key_len) {
236 IWM_ERR(iwm, "Key %d not used\n", key_index);
237 return -EINVAL;
238 }
239
Samuel Ortiz35497162009-06-15 21:59:54 +0200240 iwm->default_key = key_index;
241
Zhu Yi6e5db0a2009-07-16 17:34:13 +0800242 return iwm_set_tx_key(iwm, key_index);
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200243}
244
Samuel Ortizd0418112009-09-01 15:14:00 +0200245static int iwm_cfg80211_get_station(struct wiphy *wiphy,
246 struct net_device *ndev,
247 u8 *mac, struct station_info *sinfo)
Samuel Ortiz9967d462009-07-16 17:34:10 +0800248{
249 struct iwm_priv *iwm = ndev_to_iwm(ndev);
250
251 if (memcmp(mac, iwm->bssid, ETH_ALEN))
252 return -ENOENT;
253
254 sinfo->filled |= STATION_INFO_TX_BITRATE;
255 sinfo->txrate.legacy = iwm->rate * 10;
256
257 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
258 sinfo->filled |= STATION_INFO_SIGNAL;
259 sinfo->signal = iwm->wstats.qual.level;
260 }
261
262 return 0;
263}
264
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200265
Zhu Yibb9f8692009-05-21 21:20:45 +0800266int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
267{
268 struct wiphy *wiphy = iwm_to_wiphy(iwm);
Zhu Yi04d1c222010-02-25 14:15:26 +0800269 struct iwm_bss_info *bss;
Zhu Yibb9f8692009-05-21 21:20:45 +0800270 struct iwm_umac_notif_bss_info *umac_bss;
271 struct ieee80211_mgmt *mgmt;
272 struct ieee80211_channel *channel;
273 struct ieee80211_supported_band *band;
274 s32 signal;
275 int freq;
276
Zhu Yi04d1c222010-02-25 14:15:26 +0800277 list_for_each_entry(bss, &iwm->bss_list, node) {
Zhu Yibb9f8692009-05-21 21:20:45 +0800278 umac_bss = bss->bss;
279 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
280
281 if (umac_bss->band == UMAC_BAND_2GHZ)
282 band = wiphy->bands[IEEE80211_BAND_2GHZ];
283 else if (umac_bss->band == UMAC_BAND_5GHZ)
284 band = wiphy->bands[IEEE80211_BAND_5GHZ];
285 else {
286 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
287 return -EINVAL;
288 }
289
Bruno Randolf59eb21a2011-01-17 13:37:28 +0900290 freq = ieee80211_channel_to_frequency(umac_bss->channel,
291 band->band);
Zhu Yibb9f8692009-05-21 21:20:45 +0800292 channel = ieee80211_get_channel(wiphy, freq);
293 signal = umac_bss->rssi * 100;
294
295 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
296 le16_to_cpu(umac_bss->frame_len),
297 signal, GFP_KERNEL))
298 return -EINVAL;
299 }
300
301 return 0;
302}
303
Johannes Berge36d56b2009-06-09 21:04:43 +0200304static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
305 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800306 enum nl80211_iftype type, u32 *flags,
307 struct vif_params *params)
308{
Zhu Yibb9f8692009-05-21 21:20:45 +0800309 struct wireless_dev *wdev;
310 struct iwm_priv *iwm;
311 u32 old_mode;
312
Zhu Yibb9f8692009-05-21 21:20:45 +0800313 wdev = ndev->ieee80211_ptr;
314 iwm = ndev_to_iwm(ndev);
315 old_mode = iwm->conf.mode;
316
317 switch (type) {
318 case NL80211_IFTYPE_STATION:
319 iwm->conf.mode = UMAC_MODE_BSS;
320 break;
321 case NL80211_IFTYPE_ADHOC:
322 iwm->conf.mode = UMAC_MODE_IBSS;
323 break;
324 default:
325 return -EOPNOTSUPP;
326 }
327
328 wdev->iftype = type;
329
330 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
331 return 0;
332
333 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
334
Zhu Yiae73abf2009-09-01 15:13:58 +0200335 if (iwm->umac_profile_active)
336 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800337
338 return 0;
339}
340
341static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
342 struct cfg80211_scan_request *request)
343{
344 struct iwm_priv *iwm = ndev_to_iwm(ndev);
345 int ret;
346
347 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
348 IWM_ERR(iwm, "Scan while device is not ready\n");
349 return -EIO;
350 }
351
352 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
353 IWM_ERR(iwm, "Scanning already\n");
354 return -EAGAIN;
355 }
356
357 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
358 IWM_ERR(iwm, "Scanning being aborted\n");
359 return -EAGAIN;
360 }
361
362 set_bit(IWM_STATUS_SCANNING, &iwm->status);
363
364 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
365 if (ret) {
366 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
367 return ret;
368 }
369
370 iwm->scan_request = request;
371 return 0;
372}
373
374static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
375{
376 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
377
378 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
379 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
380 int ret;
381
382 iwm->conf.rts_threshold = wiphy->rts_threshold;
383
384 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
385 CFG_RTS_THRESHOLD,
386 iwm->conf.rts_threshold);
387 if (ret < 0)
388 return ret;
389 }
390
391 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
392 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
393 int ret;
394
395 iwm->conf.frag_threshold = wiphy->frag_threshold;
396
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800397 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800398 CFG_FRAG_THRESHOLD,
399 iwm->conf.frag_threshold);
400 if (ret < 0)
401 return ret;
402 }
403
404 return 0;
405}
406
407static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
408 struct cfg80211_ibss_params *params)
409{
410 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
411 struct ieee80211_channel *chan = params->channel;
Zhu Yibb9f8692009-05-21 21:20:45 +0800412
413 if (!test_bit(IWM_STATUS_READY, &iwm->status))
414 return -EIO;
415
Zhu Yi03d1a622009-10-16 13:18:46 +0800416 /* UMAC doesn't support creating or joining an IBSS network
417 * with specified bssid. */
Zhu Yibb9f8692009-05-21 21:20:45 +0800418 if (params->bssid)
419 return -EOPNOTSUPP;
420
Zhu Yibb9f8692009-05-21 21:20:45 +0800421 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
422 iwm->umac_profile->ibss.band = chan->band;
423 iwm->umac_profile->ibss.channel = iwm->channel;
424 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
425 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
426
Zhu Yibb9f8692009-05-21 21:20:45 +0800427 return iwm_send_mlme_profile(iwm);
428}
429
430static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
431{
432 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
433
434 if (iwm->umac_profile_active)
435 return iwm_invalidate_mlme_profile(iwm);
436
437 return 0;
438}
439
Samuel Ortiz9967d462009-07-16 17:34:10 +0800440static int iwm_set_auth_type(struct iwm_priv *iwm,
441 enum nl80211_auth_type sme_auth_type)
442{
443 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
444
445 switch (sme_auth_type) {
446 case NL80211_AUTHTYPE_AUTOMATIC:
447 case NL80211_AUTHTYPE_OPEN_SYSTEM:
448 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
449 *auth_type = UMAC_AUTH_TYPE_OPEN;
450 break;
451 case NL80211_AUTHTYPE_SHARED_KEY:
452 if (iwm->umac_profile->sec.flags &
453 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
454 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
455 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
456 } else {
457 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
458 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
459 }
460
461 break;
462 default:
463 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
464 return -ENOTSUPP;
465 }
466
467 return 0;
468}
469
470static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
471{
Zhu Yi554503f2009-08-03 14:37:01 +0800472 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
473
Samuel Ortiz9967d462009-07-16 17:34:10 +0800474 if (!wpa_version) {
475 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
476 return 0;
477 }
478
Samuel Ortiz6a79c9f2009-10-16 13:18:49 +0800479 if (wpa_version & NL80211_WPA_VERSION_1)
480 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
481
Samuel Ortiz9967d462009-07-16 17:34:10 +0800482 if (wpa_version & NL80211_WPA_VERSION_2)
483 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
484
Samuel Ortiz9967d462009-07-16 17:34:10 +0800485 return 0;
486}
487
488static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
489{
490 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
491 &iwm->umac_profile->sec.mcast_cipher;
492
493 if (!cipher) {
494 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
495 return 0;
496 }
497
Zhu Yi554503f2009-08-03 14:37:01 +0800498 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
499 cipher);
500
Samuel Ortiz9967d462009-07-16 17:34:10 +0800501 switch (cipher) {
502 case IW_AUTH_CIPHER_NONE:
503 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
504 break;
505 case WLAN_CIPHER_SUITE_WEP40:
506 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
507 break;
508 case WLAN_CIPHER_SUITE_WEP104:
509 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
510 break;
511 case WLAN_CIPHER_SUITE_TKIP:
512 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
513 break;
514 case WLAN_CIPHER_SUITE_CCMP:
515 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
516 break;
517 default:
518 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
519 return -ENOTSUPP;
520 }
521
522 return 0;
523}
524
525static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
526{
527 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
528
529 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
530
531 if (key_mgt == WLAN_AKM_SUITE_8021X)
532 *auth_type = UMAC_AUTH_TYPE_8021X;
533 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
534 if (iwm->umac_profile->sec.flags &
535 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
536 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
537 else
538 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
539 } else {
540 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
541 return -EINVAL;
542 }
543
544 return 0;
545}
546
547
548static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
549 struct cfg80211_connect_params *sme)
550{
551 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
552 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200553 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800554 int ret;
555
556 if (!test_bit(IWM_STATUS_READY, &iwm->status))
557 return -EIO;
558
559 if (!sme->ssid)
560 return -EINVAL;
561
Zhu Yiae73abf2009-09-01 15:13:58 +0200562 if (iwm->umac_profile_active) {
563 ret = iwm_invalidate_mlme_profile(iwm);
564 if (ret) {
565 IWM_ERR(iwm, "Couldn't invalidate profile\n");
566 return ret;
567 }
568 }
569
Samuel Ortiz9967d462009-07-16 17:34:10 +0800570 if (chan)
571 iwm->channel =
572 ieee80211_frequency_to_channel(chan->center_freq);
573
574 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
575 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
576
577 if (sme->bssid) {
578 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
579 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
580 iwm->umac_profile->bss_num = 1;
581 } else {
582 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
583 iwm->umac_profile->bss_num = 0;
584 }
585
Zhu Yi554503f2009-08-03 14:37:01 +0800586 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800587 if (ret < 0)
588 return ret;
589
Zhu Yi554503f2009-08-03 14:37:01 +0800590 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800591 if (ret < 0)
592 return ret;
593
594 if (sme->crypto.n_ciphers_pairwise) {
595 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
596 true);
597 if (ret < 0)
598 return ret;
599 }
600
601 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
602 if (ret < 0)
603 return ret;
604
605 if (sme->crypto.n_akm_suites) {
606 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
607 if (ret < 0)
608 return ret;
609 }
610
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200611 /*
612 * We save the WEP key in case we want to do shared authentication.
613 * We have to do it so because UMAC will assert whenever it gets a
614 * key before a profile.
615 */
616 if (sme->key) {
617 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
618 if (key_param.key == NULL)
619 return -ENOMEM;
620 key_param.key_len = sme->key_len;
621 key_param.seq_len = 0;
622 key_param.cipher = sme->crypto.ciphers_pairwise[0];
623
624 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
625 NULL, &key_param);
626 kfree(key_param.key);
627 if (ret < 0) {
628 IWM_ERR(iwm, "Invalid key_params\n");
629 return ret;
630 }
631
632 iwm->default_key = sme->key_idx;
633 }
634
Samuel Ortiza82aedb2009-10-16 13:18:47 +0800635 /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
636 if ((iwm->umac_profile->sec.flags &
637 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
638 iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
639 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
640 }
641
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200642 ret = iwm_send_mlme_profile(iwm);
643
644 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
645 sme->key == NULL)
646 return ret;
647
648 /*
649 * We want to do shared auth.
650 * We need to actually set the key we previously cached,
651 * and then tell the UMAC it's the default one.
652 * That will trigger the auth+assoc UMAC machinery, and again,
653 * this must be done after setting the profile.
654 */
655 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
656 if (ret < 0)
657 return ret;
658
659 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800660}
661
662static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
663 u16 reason_code)
664{
665 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
666
667 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
668
669 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200670 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800671
672 return 0;
673}
674
Zhu Yi257862f2009-06-15 21:59:56 +0200675static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300676 enum nl80211_tx_power_setting type, int mbm)
Zhu Yi257862f2009-06-15 21:59:56 +0200677{
Samuel Ortiz88e61952009-10-16 13:18:53 +0800678 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
679 int ret;
680
Zhu Yi257862f2009-06-15 21:59:56 +0200681 switch (type) {
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300682 case NL80211_TX_POWER_AUTOMATIC:
Zhu Yi257862f2009-06-15 21:59:56 +0200683 return 0;
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300684 case NL80211_TX_POWER_FIXED:
685 if (mbm < 0 || (mbm % 100))
686 return -EOPNOTSUPP;
687
Samuel Ortizfe191762009-11-24 11:33:28 +0800688 if (!test_bit(IWM_STATUS_READY, &iwm->status))
689 return 0;
690
Samuel Ortiz88e61952009-10-16 13:18:53 +0800691 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300692 CFG_TX_PWR_LIMIT_USR,
693 MBM_TO_DBM(mbm) * 2);
Samuel Ortiz88e61952009-10-16 13:18:53 +0800694 if (ret < 0)
695 return ret;
696
697 return iwm_tx_power_trigger(iwm);
Zhu Yi257862f2009-06-15 21:59:56 +0200698 default:
Samuel Ortizfe191762009-11-24 11:33:28 +0800699 IWM_ERR(iwm, "Unsupported power type: %d\n", type);
Zhu Yi257862f2009-06-15 21:59:56 +0200700 return -EOPNOTSUPP;
701 }
702
703 return 0;
704}
705
706static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
707{
708 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
709
Samuel Ortiz88e61952009-10-16 13:18:53 +0800710 *dbm = iwm->txpower >> 1;
Zhu Yi257862f2009-06-15 21:59:56 +0200711
712 return 0;
713}
714
Johannes Bergbc92afd2009-07-01 21:26:57 +0200715static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
716 struct net_device *dev,
717 bool enabled, int timeout)
718{
719 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
720 u32 power_index;
721
722 if (enabled)
723 power_index = IWM_POWER_INDEX_DEFAULT;
724 else
725 power_index = IWM_POWER_INDEX_MIN;
726
727 if (power_index == iwm->conf.power_index)
728 return 0;
729
730 iwm->conf.power_index = power_index;
731
732 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
733 CFG_POWER_INDEX, iwm->conf.power_index);
734}
735
Zhu Yid281fd42010-02-25 14:15:30 +0800736static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy,
737 struct net_device *netdev,
738 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100739{
740 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
741
742 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
743}
744
Zhu Yid281fd42010-02-25 14:15:30 +0800745static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy,
746 struct net_device *netdev,
747 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100748{
749 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
750
751 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
752}
753
Zhu Yid281fd42010-02-25 14:15:30 +0800754static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy,
755 struct net_device *netdev)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100756{
757 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
758 struct cfg80211_pmksa pmksa;
759
760 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
761
762 return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
763}
764
765
Zhu Yibb9f8692009-05-21 21:20:45 +0800766static struct cfg80211_ops iwm_cfg80211_ops = {
767 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200768 .add_key = iwm_cfg80211_add_key,
769 .get_key = iwm_cfg80211_get_key,
770 .del_key = iwm_cfg80211_del_key,
771 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800772 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800773 .scan = iwm_cfg80211_scan,
774 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800775 .connect = iwm_cfg80211_connect,
776 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800777 .join_ibss = iwm_cfg80211_join_ibss,
778 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200779 .set_tx_power = iwm_cfg80211_set_txpower,
780 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200781 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100782 .set_pmksa = iwm_cfg80211_set_pmksa,
783 .del_pmksa = iwm_cfg80211_del_pmksa,
784 .flush_pmksa = iwm_cfg80211_flush_pmksa,
Zhu Yibb9f8692009-05-21 21:20:45 +0800785};
786
Zhu Yi49b77722009-07-16 17:34:08 +0800787static const u32 cipher_suites[] = {
788 WLAN_CIPHER_SUITE_WEP40,
789 WLAN_CIPHER_SUITE_WEP104,
790 WLAN_CIPHER_SUITE_TKIP,
791 WLAN_CIPHER_SUITE_CCMP,
792};
793
Zhu Yibb9f8692009-05-21 21:20:45 +0800794struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
795{
796 int ret = 0;
797 struct wireless_dev *wdev;
798
799 /*
800 * We're trying to have the following memory
801 * layout:
802 *
803 * +-------------------------+
804 * | struct wiphy |
805 * +-------------------------+
806 * | struct iwm_priv |
807 * +-------------------------+
808 * | bus private data |
809 * | (e.g. iwm_priv_sdio) |
810 * +-------------------------+
811 *
812 */
813
814 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
815 if (!wdev) {
816 dev_err(dev, "Couldn't allocate wireless device\n");
817 return ERR_PTR(-ENOMEM);
818 }
819
820 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
821 sizeof(struct iwm_priv) + sizeof_bus);
822 if (!wdev->wiphy) {
823 dev_err(dev, "Couldn't allocate wiphy device\n");
824 ret = -ENOMEM;
825 goto out_err_new;
826 }
827
828 set_wiphy_dev(wdev->wiphy, dev);
829 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100830 wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
Zhu Yibb9f8692009-05-21 21:20:45 +0800831 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
832 BIT(NL80211_IFTYPE_ADHOC);
833 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
834 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
835 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
836
Zhu Yi49b77722009-07-16 17:34:08 +0800837 wdev->wiphy->cipher_suites = cipher_suites;
838 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
839
Zhu Yibb9f8692009-05-21 21:20:45 +0800840 ret = wiphy_register(wdev->wiphy);
841 if (ret < 0) {
842 dev_err(dev, "Couldn't register wiphy device\n");
843 goto out_err_register;
844 }
845
846 return wdev;
847
848 out_err_register:
849 wiphy_free(wdev->wiphy);
850
851 out_err_new:
852 kfree(wdev);
853
854 return ERR_PTR(ret);
855}
856
857void iwm_wdev_free(struct iwm_priv *iwm)
858{
859 struct wireless_dev *wdev = iwm_to_wdev(iwm);
860
861 if (!wdev)
862 return;
863
864 wiphy_unregister(wdev->wiphy);
865 wiphy_free(wdev->wiphy);
866 kfree(wdev);
867}