blob: 5a4982271e96a8f9cb57fb6c22610549b1dbcb0b [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
290 freq = ieee80211_channel_to_frequency(umac_bss->channel);
291 channel = ieee80211_get_channel(wiphy, freq);
292 signal = umac_bss->rssi * 100;
293
294 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
295 le16_to_cpu(umac_bss->frame_len),
296 signal, GFP_KERNEL))
297 return -EINVAL;
298 }
299
300 return 0;
301}
302
Johannes Berge36d56b2009-06-09 21:04:43 +0200303static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
304 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800305 enum nl80211_iftype type, u32 *flags,
306 struct vif_params *params)
307{
Zhu Yibb9f8692009-05-21 21:20:45 +0800308 struct wireless_dev *wdev;
309 struct iwm_priv *iwm;
310 u32 old_mode;
311
Zhu Yibb9f8692009-05-21 21:20:45 +0800312 wdev = ndev->ieee80211_ptr;
313 iwm = ndev_to_iwm(ndev);
314 old_mode = iwm->conf.mode;
315
316 switch (type) {
317 case NL80211_IFTYPE_STATION:
318 iwm->conf.mode = UMAC_MODE_BSS;
319 break;
320 case NL80211_IFTYPE_ADHOC:
321 iwm->conf.mode = UMAC_MODE_IBSS;
322 break;
323 default:
324 return -EOPNOTSUPP;
325 }
326
327 wdev->iftype = type;
328
329 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
330 return 0;
331
332 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
333
Zhu Yiae73abf2009-09-01 15:13:58 +0200334 if (iwm->umac_profile_active)
335 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800336
337 return 0;
338}
339
340static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
341 struct cfg80211_scan_request *request)
342{
343 struct iwm_priv *iwm = ndev_to_iwm(ndev);
344 int ret;
345
346 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
347 IWM_ERR(iwm, "Scan while device is not ready\n");
348 return -EIO;
349 }
350
351 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
352 IWM_ERR(iwm, "Scanning already\n");
353 return -EAGAIN;
354 }
355
356 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
357 IWM_ERR(iwm, "Scanning being aborted\n");
358 return -EAGAIN;
359 }
360
361 set_bit(IWM_STATUS_SCANNING, &iwm->status);
362
363 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
364 if (ret) {
365 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
366 return ret;
367 }
368
369 iwm->scan_request = request;
370 return 0;
371}
372
373static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
374{
375 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
376
377 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
378 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
379 int ret;
380
381 iwm->conf.rts_threshold = wiphy->rts_threshold;
382
383 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
384 CFG_RTS_THRESHOLD,
385 iwm->conf.rts_threshold);
386 if (ret < 0)
387 return ret;
388 }
389
390 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
391 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
392 int ret;
393
394 iwm->conf.frag_threshold = wiphy->frag_threshold;
395
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800396 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800397 CFG_FRAG_THRESHOLD,
398 iwm->conf.frag_threshold);
399 if (ret < 0)
400 return ret;
401 }
402
403 return 0;
404}
405
406static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
407 struct cfg80211_ibss_params *params)
408{
409 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
410 struct ieee80211_channel *chan = params->channel;
Zhu Yibb9f8692009-05-21 21:20:45 +0800411
412 if (!test_bit(IWM_STATUS_READY, &iwm->status))
413 return -EIO;
414
Zhu Yi03d1a622009-10-16 13:18:46 +0800415 /* UMAC doesn't support creating or joining an IBSS network
416 * with specified bssid. */
Zhu Yibb9f8692009-05-21 21:20:45 +0800417 if (params->bssid)
418 return -EOPNOTSUPP;
419
Zhu Yibb9f8692009-05-21 21:20:45 +0800420 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
421 iwm->umac_profile->ibss.band = chan->band;
422 iwm->umac_profile->ibss.channel = iwm->channel;
423 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
424 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
425
Zhu Yibb9f8692009-05-21 21:20:45 +0800426 return iwm_send_mlme_profile(iwm);
427}
428
429static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
430{
431 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
432
433 if (iwm->umac_profile_active)
434 return iwm_invalidate_mlme_profile(iwm);
435
436 return 0;
437}
438
Samuel Ortiz9967d462009-07-16 17:34:10 +0800439static int iwm_set_auth_type(struct iwm_priv *iwm,
440 enum nl80211_auth_type sme_auth_type)
441{
442 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
443
444 switch (sme_auth_type) {
445 case NL80211_AUTHTYPE_AUTOMATIC:
446 case NL80211_AUTHTYPE_OPEN_SYSTEM:
447 IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
448 *auth_type = UMAC_AUTH_TYPE_OPEN;
449 break;
450 case NL80211_AUTHTYPE_SHARED_KEY:
451 if (iwm->umac_profile->sec.flags &
452 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
453 IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
454 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
455 } else {
456 IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
457 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
458 }
459
460 break;
461 default:
462 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
463 return -ENOTSUPP;
464 }
465
466 return 0;
467}
468
469static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
470{
Zhu Yi554503f2009-08-03 14:37:01 +0800471 IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
472
Samuel Ortiz9967d462009-07-16 17:34:10 +0800473 if (!wpa_version) {
474 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
475 return 0;
476 }
477
Samuel Ortiz6a79c9f2009-10-16 13:18:49 +0800478 if (wpa_version & NL80211_WPA_VERSION_1)
479 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
480
Samuel Ortiz9967d462009-07-16 17:34:10 +0800481 if (wpa_version & NL80211_WPA_VERSION_2)
482 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
483
Samuel Ortiz9967d462009-07-16 17:34:10 +0800484 return 0;
485}
486
487static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
488{
489 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
490 &iwm->umac_profile->sec.mcast_cipher;
491
492 if (!cipher) {
493 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
494 return 0;
495 }
496
Zhu Yi554503f2009-08-03 14:37:01 +0800497 IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
498 cipher);
499
Samuel Ortiz9967d462009-07-16 17:34:10 +0800500 switch (cipher) {
501 case IW_AUTH_CIPHER_NONE:
502 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
503 break;
504 case WLAN_CIPHER_SUITE_WEP40:
505 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
506 break;
507 case WLAN_CIPHER_SUITE_WEP104:
508 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
509 break;
510 case WLAN_CIPHER_SUITE_TKIP:
511 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
512 break;
513 case WLAN_CIPHER_SUITE_CCMP:
514 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
515 break;
516 default:
517 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
518 return -ENOTSUPP;
519 }
520
521 return 0;
522}
523
524static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
525{
526 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
527
528 IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
529
530 if (key_mgt == WLAN_AKM_SUITE_8021X)
531 *auth_type = UMAC_AUTH_TYPE_8021X;
532 else if (key_mgt == WLAN_AKM_SUITE_PSK) {
533 if (iwm->umac_profile->sec.flags &
534 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
535 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
536 else
537 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
538 } else {
539 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
540 return -EINVAL;
541 }
542
543 return 0;
544}
545
546
547static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
548 struct cfg80211_connect_params *sme)
549{
550 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
551 struct ieee80211_channel *chan = sme->channel;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200552 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800553 int ret;
554
555 if (!test_bit(IWM_STATUS_READY, &iwm->status))
556 return -EIO;
557
558 if (!sme->ssid)
559 return -EINVAL;
560
Zhu Yiae73abf2009-09-01 15:13:58 +0200561 if (iwm->umac_profile_active) {
562 ret = iwm_invalidate_mlme_profile(iwm);
563 if (ret) {
564 IWM_ERR(iwm, "Couldn't invalidate profile\n");
565 return ret;
566 }
567 }
568
Samuel Ortiz9967d462009-07-16 17:34:10 +0800569 if (chan)
570 iwm->channel =
571 ieee80211_frequency_to_channel(chan->center_freq);
572
573 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
574 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
575
576 if (sme->bssid) {
577 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
578 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
579 iwm->umac_profile->bss_num = 1;
580 } else {
581 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
582 iwm->umac_profile->bss_num = 0;
583 }
584
Zhu Yi554503f2009-08-03 14:37:01 +0800585 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800586 if (ret < 0)
587 return ret;
588
Zhu Yi554503f2009-08-03 14:37:01 +0800589 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800590 if (ret < 0)
591 return ret;
592
593 if (sme->crypto.n_ciphers_pairwise) {
594 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
595 true);
596 if (ret < 0)
597 return ret;
598 }
599
600 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
601 if (ret < 0)
602 return ret;
603
604 if (sme->crypto.n_akm_suites) {
605 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
606 if (ret < 0)
607 return ret;
608 }
609
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200610 /*
611 * We save the WEP key in case we want to do shared authentication.
612 * We have to do it so because UMAC will assert whenever it gets a
613 * key before a profile.
614 */
615 if (sme->key) {
616 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
617 if (key_param.key == NULL)
618 return -ENOMEM;
619 key_param.key_len = sme->key_len;
620 key_param.seq_len = 0;
621 key_param.cipher = sme->crypto.ciphers_pairwise[0];
622
623 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
624 NULL, &key_param);
625 kfree(key_param.key);
626 if (ret < 0) {
627 IWM_ERR(iwm, "Invalid key_params\n");
628 return ret;
629 }
630
631 iwm->default_key = sme->key_idx;
632 }
633
Samuel Ortiza82aedb2009-10-16 13:18:47 +0800634 /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
635 if ((iwm->umac_profile->sec.flags &
636 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
637 iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
638 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
639 }
640
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200641 ret = iwm_send_mlme_profile(iwm);
642
643 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
644 sme->key == NULL)
645 return ret;
646
647 /*
648 * We want to do shared auth.
649 * We need to actually set the key we previously cached,
650 * and then tell the UMAC it's the default one.
651 * That will trigger the auth+assoc UMAC machinery, and again,
652 * this must be done after setting the profile.
653 */
654 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
655 if (ret < 0)
656 return ret;
657
658 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800659}
660
661static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
662 u16 reason_code)
663{
664 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
665
666 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
667
668 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200669 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800670
671 return 0;
672}
673
Zhu Yi257862f2009-06-15 21:59:56 +0200674static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300675 enum nl80211_tx_power_setting type, int mbm)
Zhu Yi257862f2009-06-15 21:59:56 +0200676{
Samuel Ortiz88e61952009-10-16 13:18:53 +0800677 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
678 int ret;
679
Zhu Yi257862f2009-06-15 21:59:56 +0200680 switch (type) {
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300681 case NL80211_TX_POWER_AUTOMATIC:
Zhu Yi257862f2009-06-15 21:59:56 +0200682 return 0;
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300683 case NL80211_TX_POWER_FIXED:
684 if (mbm < 0 || (mbm % 100))
685 return -EOPNOTSUPP;
686
Samuel Ortizfe191762009-11-24 11:33:28 +0800687 if (!test_bit(IWM_STATUS_READY, &iwm->status))
688 return 0;
689
Samuel Ortiz88e61952009-10-16 13:18:53 +0800690 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300691 CFG_TX_PWR_LIMIT_USR,
692 MBM_TO_DBM(mbm) * 2);
Samuel Ortiz88e61952009-10-16 13:18:53 +0800693 if (ret < 0)
694 return ret;
695
696 return iwm_tx_power_trigger(iwm);
Zhu Yi257862f2009-06-15 21:59:56 +0200697 default:
Samuel Ortizfe191762009-11-24 11:33:28 +0800698 IWM_ERR(iwm, "Unsupported power type: %d\n", type);
Zhu Yi257862f2009-06-15 21:59:56 +0200699 return -EOPNOTSUPP;
700 }
701
702 return 0;
703}
704
705static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
706{
707 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
708
Samuel Ortiz88e61952009-10-16 13:18:53 +0800709 *dbm = iwm->txpower >> 1;
Zhu Yi257862f2009-06-15 21:59:56 +0200710
711 return 0;
712}
713
Johannes Bergbc92afd2009-07-01 21:26:57 +0200714static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
715 struct net_device *dev,
716 bool enabled, int timeout)
717{
718 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
719 u32 power_index;
720
721 if (enabled)
722 power_index = IWM_POWER_INDEX_DEFAULT;
723 else
724 power_index = IWM_POWER_INDEX_MIN;
725
726 if (power_index == iwm->conf.power_index)
727 return 0;
728
729 iwm->conf.power_index = power_index;
730
731 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
732 CFG_POWER_INDEX, iwm->conf.power_index);
733}
734
Zhu Yid281fd42010-02-25 14:15:30 +0800735static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy,
736 struct net_device *netdev,
737 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100738{
739 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
740
741 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
742}
743
Zhu Yid281fd42010-02-25 14:15:30 +0800744static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy,
745 struct net_device *netdev,
746 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100747{
748 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
749
750 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
751}
752
Zhu Yid281fd42010-02-25 14:15:30 +0800753static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy,
754 struct net_device *netdev)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100755{
756 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
757 struct cfg80211_pmksa pmksa;
758
759 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
760
761 return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
762}
763
764
Zhu Yibb9f8692009-05-21 21:20:45 +0800765static struct cfg80211_ops iwm_cfg80211_ops = {
766 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe702009-06-15 21:59:52 +0200767 .add_key = iwm_cfg80211_add_key,
768 .get_key = iwm_cfg80211_get_key,
769 .del_key = iwm_cfg80211_del_key,
770 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800771 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800772 .scan = iwm_cfg80211_scan,
773 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800774 .connect = iwm_cfg80211_connect,
775 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800776 .join_ibss = iwm_cfg80211_join_ibss,
777 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200778 .set_tx_power = iwm_cfg80211_set_txpower,
779 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200780 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100781 .set_pmksa = iwm_cfg80211_set_pmksa,
782 .del_pmksa = iwm_cfg80211_del_pmksa,
783 .flush_pmksa = iwm_cfg80211_flush_pmksa,
Zhu Yibb9f8692009-05-21 21:20:45 +0800784};
785
Zhu Yi49b77722009-07-16 17:34:08 +0800786static const u32 cipher_suites[] = {
787 WLAN_CIPHER_SUITE_WEP40,
788 WLAN_CIPHER_SUITE_WEP104,
789 WLAN_CIPHER_SUITE_TKIP,
790 WLAN_CIPHER_SUITE_CCMP,
791};
792
Zhu Yibb9f8692009-05-21 21:20:45 +0800793struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
794{
795 int ret = 0;
796 struct wireless_dev *wdev;
797
798 /*
799 * We're trying to have the following memory
800 * layout:
801 *
802 * +-------------------------+
803 * | struct wiphy |
804 * +-------------------------+
805 * | struct iwm_priv |
806 * +-------------------------+
807 * | bus private data |
808 * | (e.g. iwm_priv_sdio) |
809 * +-------------------------+
810 *
811 */
812
813 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
814 if (!wdev) {
815 dev_err(dev, "Couldn't allocate wireless device\n");
816 return ERR_PTR(-ENOMEM);
817 }
818
819 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
820 sizeof(struct iwm_priv) + sizeof_bus);
821 if (!wdev->wiphy) {
822 dev_err(dev, "Couldn't allocate wiphy device\n");
823 ret = -ENOMEM;
824 goto out_err_new;
825 }
826
827 set_wiphy_dev(wdev->wiphy, dev);
828 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100829 wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
Zhu Yibb9f8692009-05-21 21:20:45 +0800830 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
831 BIT(NL80211_IFTYPE_ADHOC);
832 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
833 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
834 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
835
Zhu Yi49b77722009-07-16 17:34:08 +0800836 wdev->wiphy->cipher_suites = cipher_suites;
837 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
838
Zhu Yibb9f8692009-05-21 21:20:45 +0800839 ret = wiphy_register(wdev->wiphy);
840 if (ret < 0) {
841 dev_err(dev, "Couldn't register wiphy device\n");
842 goto out_err_register;
843 }
844
845 return wdev;
846
847 out_err_register:
848 wiphy_free(wdev->wiphy);
849
850 out_err_new:
851 kfree(wdev);
852
853 return ERR_PTR(ret);
854}
855
856void iwm_wdev_free(struct iwm_priv *iwm)
857{
858 struct wireless_dev *wdev = iwm_to_wdev(iwm);
859
860 if (!wdev)
861 return;
862
863 wiphy_unregister(wdev->wiphy);
864 wiphy_free(wdev->wiphy);
865 kfree(wdev);
866}