blob: 48e8218fd23bc32ad79f56c007b10fbb49109ff9 [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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-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 Ortiz13e0fe72009-06-15 21:59:52 +0200165 struct key_params *params)
166{
167 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Dan Carpenter5a5ee762011-10-18 09:50:43 +0300168 struct iwm_key *key;
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200169 int ret;
170
171 IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
172
Dan Carpenter5a5ee762011-10-18 09:50:43 +0300173 if (key_index >= IWM_NUM_KEYS)
174 return -ENOENT;
175
176 key = &iwm->keys[key_index];
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200177 memset(key, 0, sizeof(struct iwm_key));
178 ret = iwm_key_init(key, key_index, mac_addr, params);
179 if (ret < 0) {
180 IWM_ERR(iwm, "Invalid key_params\n");
181 return ret;
182 }
183
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200184 return iwm_set_key(iwm, 0, key);
185}
186
187static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berge31b8212010-10-05 19:39:30 +0200188 u8 key_index, bool pairwise, const u8 *mac_addr,
189 void *cookie,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200190 void (*callback)(void *cookie,
191 struct key_params*))
192{
193 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Dan Carpenterf9a703e2011-10-12 11:10:37 +0300194 struct iwm_key *key;
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200195 struct key_params params;
196
197 IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
198
Dan Carpenterf9a703e2011-10-12 11:10:37 +0300199 if (key_index >= IWM_NUM_KEYS)
200 return -ENOENT;
201
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200202 memset(&params, 0, sizeof(params));
203
Dan Carpenterf9a703e2011-10-12 11:10:37 +0300204 key = &iwm->keys[key_index];
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200205 params.cipher = key->cipher;
206 params.key_len = key->key_len;
207 params.seq_len = key->seq_len;
208 params.seq = key->seq;
209 params.key = key->key;
210
211 callback(cookie, &params);
212
213 return key->key_len ? 0 : -ENOENT;
214}
215
216
217static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berge31b8212010-10-05 19:39:30 +0200218 u8 key_index, bool pairwise, const u8 *mac_addr)
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200219{
220 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Dan Carpenter5a5ee762011-10-18 09:50:43 +0300221 struct iwm_key *key;
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200222
Dan Carpenter5a5ee762011-10-18 09:50:43 +0300223 if (key_index >= IWM_NUM_KEYS)
224 return -ENOENT;
225
226 key = &iwm->keys[key_index];
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200227 if (!iwm->keys[key_index].key_len) {
228 IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
229 return 0;
230 }
231
232 if (key_index == iwm->default_key)
233 iwm->default_key = -1;
234
235 return iwm_set_key(iwm, 1, key);
236}
237
238static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
239 struct net_device *ndev,
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100240 u8 key_index, bool unicast,
241 bool multicast)
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200242{
243 struct iwm_priv *iwm = ndev_to_iwm(ndev);
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200244
245 IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
246
Dan Carpenter5a5ee762011-10-18 09:50:43 +0300247 if (key_index >= IWM_NUM_KEYS)
248 return -ENOENT;
249
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200250 if (!iwm->keys[key_index].key_len) {
251 IWM_ERR(iwm, "Key %d not used\n", key_index);
252 return -EINVAL;
253 }
254
Samuel Ortiz35497162009-06-15 21:59:54 +0200255 iwm->default_key = key_index;
256
Zhu Yi6e5db0a2009-07-16 17:34:13 +0800257 return iwm_set_tx_key(iwm, key_index);
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200258}
259
Samuel Ortizd0418112009-09-01 15:14:00 +0200260static int iwm_cfg80211_get_station(struct wiphy *wiphy,
261 struct net_device *ndev,
262 u8 *mac, struct station_info *sinfo)
Samuel Ortiz9967d462009-07-16 17:34:10 +0800263{
264 struct iwm_priv *iwm = ndev_to_iwm(ndev);
265
266 if (memcmp(mac, iwm->bssid, ETH_ALEN))
267 return -ENOENT;
268
269 sinfo->filled |= STATION_INFO_TX_BITRATE;
270 sinfo->txrate.legacy = iwm->rate * 10;
271
272 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
273 sinfo->filled |= STATION_INFO_SIGNAL;
274 sinfo->signal = iwm->wstats.qual.level;
275 }
276
277 return 0;
278}
279
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200280
Zhu Yibb9f8692009-05-21 21:20:45 +0800281int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
282{
283 struct wiphy *wiphy = iwm_to_wiphy(iwm);
Zhu Yi04d1c222010-02-25 14:15:26 +0800284 struct iwm_bss_info *bss;
Zhu Yibb9f8692009-05-21 21:20:45 +0800285 struct iwm_umac_notif_bss_info *umac_bss;
286 struct ieee80211_mgmt *mgmt;
287 struct ieee80211_channel *channel;
288 struct ieee80211_supported_band *band;
289 s32 signal;
290 int freq;
291
Zhu Yi04d1c222010-02-25 14:15:26 +0800292 list_for_each_entry(bss, &iwm->bss_list, node) {
Zhu Yibb9f8692009-05-21 21:20:45 +0800293 umac_bss = bss->bss;
294 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
295
296 if (umac_bss->band == UMAC_BAND_2GHZ)
297 band = wiphy->bands[IEEE80211_BAND_2GHZ];
298 else if (umac_bss->band == UMAC_BAND_5GHZ)
299 band = wiphy->bands[IEEE80211_BAND_5GHZ];
300 else {
301 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
302 return -EINVAL;
303 }
304
Bruno Randolf59eb21a2011-01-17 13:37:28 +0900305 freq = ieee80211_channel_to_frequency(umac_bss->channel,
306 band->band);
Zhu Yibb9f8692009-05-21 21:20:45 +0800307 channel = ieee80211_get_channel(wiphy, freq);
308 signal = umac_bss->rssi * 100;
309
310 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
311 le16_to_cpu(umac_bss->frame_len),
312 signal, GFP_KERNEL))
313 return -EINVAL;
314 }
315
316 return 0;
317}
318
Johannes Berge36d56b2009-06-09 21:04:43 +0200319static int iwm_cfg80211_change_iface(struct wiphy *wiphy,
320 struct net_device *ndev,
Zhu Yibb9f8692009-05-21 21:20:45 +0800321 enum nl80211_iftype type, u32 *flags,
322 struct vif_params *params)
323{
Zhu Yibb9f8692009-05-21 21:20:45 +0800324 struct wireless_dev *wdev;
325 struct iwm_priv *iwm;
326 u32 old_mode;
327
Zhu Yibb9f8692009-05-21 21:20:45 +0800328 wdev = ndev->ieee80211_ptr;
329 iwm = ndev_to_iwm(ndev);
330 old_mode = iwm->conf.mode;
331
332 switch (type) {
333 case NL80211_IFTYPE_STATION:
334 iwm->conf.mode = UMAC_MODE_BSS;
335 break;
336 case NL80211_IFTYPE_ADHOC:
337 iwm->conf.mode = UMAC_MODE_IBSS;
338 break;
339 default:
340 return -EOPNOTSUPP;
341 }
342
343 wdev->iftype = type;
344
345 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
346 return 0;
347
348 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
349
Zhu Yiae73abf2009-09-01 15:13:58 +0200350 if (iwm->umac_profile_active)
351 iwm_invalidate_mlme_profile(iwm);
Zhu Yibb9f8692009-05-21 21:20:45 +0800352
353 return 0;
354}
355
356static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
357 struct cfg80211_scan_request *request)
358{
359 struct iwm_priv *iwm = ndev_to_iwm(ndev);
360 int ret;
361
362 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
363 IWM_ERR(iwm, "Scan while device is not ready\n");
364 return -EIO;
365 }
366
367 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
368 IWM_ERR(iwm, "Scanning already\n");
369 return -EAGAIN;
370 }
371
372 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
373 IWM_ERR(iwm, "Scanning being aborted\n");
374 return -EAGAIN;
375 }
376
377 set_bit(IWM_STATUS_SCANNING, &iwm->status);
378
379 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
380 if (ret) {
381 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
382 return ret;
383 }
384
385 iwm->scan_request = request;
386 return 0;
387}
388
389static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
390{
391 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
392
393 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
394 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
395 int ret;
396
397 iwm->conf.rts_threshold = wiphy->rts_threshold;
398
399 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
400 CFG_RTS_THRESHOLD,
401 iwm->conf.rts_threshold);
402 if (ret < 0)
403 return ret;
404 }
405
406 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
407 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
408 int ret;
409
410 iwm->conf.frag_threshold = wiphy->frag_threshold;
411
Samuel Ortizb63b0ea2009-05-26 11:10:46 +0800412 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
Zhu Yibb9f8692009-05-21 21:20:45 +0800413 CFG_FRAG_THRESHOLD,
414 iwm->conf.frag_threshold);
415 if (ret < 0)
416 return ret;
417 }
418
419 return 0;
420}
421
422static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
423 struct cfg80211_ibss_params *params)
424{
425 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
426 struct ieee80211_channel *chan = params->channel;
Zhu Yibb9f8692009-05-21 21:20:45 +0800427
428 if (!test_bit(IWM_STATUS_READY, &iwm->status))
429 return -EIO;
430
Zhu Yi03d1a622009-10-16 13:18:46 +0800431 /* UMAC doesn't support creating or joining an IBSS network
432 * with specified bssid. */
Zhu Yibb9f8692009-05-21 21:20:45 +0800433 if (params->bssid)
434 return -EOPNOTSUPP;
435
Zhu Yibb9f8692009-05-21 21:20:45 +0800436 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
437 iwm->umac_profile->ibss.band = chan->band;
438 iwm->umac_profile->ibss.channel = iwm->channel;
439 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
440 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
441
Zhu Yibb9f8692009-05-21 21:20:45 +0800442 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
Samuel Ortiz6a79c9f2009-10-16 13:18:49 +0800494 if (wpa_version & NL80211_WPA_VERSION_1)
495 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
496
Samuel Ortiz9967d462009-07-16 17:34:10 +0800497 if (wpa_version & NL80211_WPA_VERSION_2)
498 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
499
Samuel Ortiz9967d462009-07-16 17:34:10 +0800500 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;
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200568 struct key_params key_param;
Samuel Ortiz9967d462009-07-16 17:34:10 +0800569 int ret;
570
571 if (!test_bit(IWM_STATUS_READY, &iwm->status))
572 return -EIO;
573
574 if (!sme->ssid)
575 return -EINVAL;
576
Zhu Yiae73abf2009-09-01 15:13:58 +0200577 if (iwm->umac_profile_active) {
578 ret = iwm_invalidate_mlme_profile(iwm);
579 if (ret) {
580 IWM_ERR(iwm, "Couldn't invalidate profile\n");
581 return ret;
582 }
583 }
584
Samuel Ortiz9967d462009-07-16 17:34:10 +0800585 if (chan)
586 iwm->channel =
587 ieee80211_frequency_to_channel(chan->center_freq);
588
589 iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
590 memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
591
592 if (sme->bssid) {
593 IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
594 memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
595 iwm->umac_profile->bss_num = 1;
596 } else {
597 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
598 iwm->umac_profile->bss_num = 0;
599 }
600
Zhu Yi554503f2009-08-03 14:37:01 +0800601 ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800602 if (ret < 0)
603 return ret;
604
Zhu Yi554503f2009-08-03 14:37:01 +0800605 ret = iwm_set_auth_type(iwm, sme->auth_type);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800606 if (ret < 0)
607 return ret;
608
609 if (sme->crypto.n_ciphers_pairwise) {
610 ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
611 true);
612 if (ret < 0)
613 return ret;
614 }
615
616 ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
617 if (ret < 0)
618 return ret;
619
620 if (sme->crypto.n_akm_suites) {
621 ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
622 if (ret < 0)
623 return ret;
624 }
625
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200626 /*
627 * We save the WEP key in case we want to do shared authentication.
628 * We have to do it so because UMAC will assert whenever it gets a
629 * key before a profile.
630 */
631 if (sme->key) {
632 key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
633 if (key_param.key == NULL)
634 return -ENOMEM;
635 key_param.key_len = sme->key_len;
636 key_param.seq_len = 0;
637 key_param.cipher = sme->crypto.ciphers_pairwise[0];
638
639 ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
640 NULL, &key_param);
641 kfree(key_param.key);
642 if (ret < 0) {
643 IWM_ERR(iwm, "Invalid key_params\n");
644 return ret;
645 }
646
647 iwm->default_key = sme->key_idx;
648 }
649
Samuel Ortiza82aedb2009-10-16 13:18:47 +0800650 /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
651 if ((iwm->umac_profile->sec.flags &
652 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
653 iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
654 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
655 }
656
Samuel Ortizb90a5c92009-09-01 15:13:59 +0200657 ret = iwm_send_mlme_profile(iwm);
658
659 if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
660 sme->key == NULL)
661 return ret;
662
663 /*
664 * We want to do shared auth.
665 * We need to actually set the key we previously cached,
666 * and then tell the UMAC it's the default one.
667 * That will trigger the auth+assoc UMAC machinery, and again,
668 * this must be done after setting the profile.
669 */
670 ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
671 if (ret < 0)
672 return ret;
673
674 return iwm_set_tx_key(iwm, iwm->default_key);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800675}
676
677static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
678 u16 reason_code)
679{
680 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
681
682 IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
683
684 if (iwm->umac_profile_active)
Zhu Yide15fd32009-09-01 15:14:01 +0200685 iwm_invalidate_mlme_profile(iwm);
Samuel Ortiz9967d462009-07-16 17:34:10 +0800686
687 return 0;
688}
689
Zhu Yi257862f2009-06-15 21:59:56 +0200690static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300691 enum nl80211_tx_power_setting type, int mbm)
Zhu Yi257862f2009-06-15 21:59:56 +0200692{
Samuel Ortiz88e61952009-10-16 13:18:53 +0800693 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
694 int ret;
695
Zhu Yi257862f2009-06-15 21:59:56 +0200696 switch (type) {
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300697 case NL80211_TX_POWER_AUTOMATIC:
Zhu Yi257862f2009-06-15 21:59:56 +0200698 return 0;
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300699 case NL80211_TX_POWER_FIXED:
700 if (mbm < 0 || (mbm % 100))
701 return -EOPNOTSUPP;
702
Samuel Ortizfe191762009-11-24 11:33:28 +0800703 if (!test_bit(IWM_STATUS_READY, &iwm->status))
704 return 0;
705
Samuel Ortiz88e61952009-10-16 13:18:53 +0800706 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
Juuso Oikarinenfa61cf72010-06-23 12:12:37 +0300707 CFG_TX_PWR_LIMIT_USR,
708 MBM_TO_DBM(mbm) * 2);
Samuel Ortiz88e61952009-10-16 13:18:53 +0800709 if (ret < 0)
710 return ret;
711
712 return iwm_tx_power_trigger(iwm);
Zhu Yi257862f2009-06-15 21:59:56 +0200713 default:
Samuel Ortizfe191762009-11-24 11:33:28 +0800714 IWM_ERR(iwm, "Unsupported power type: %d\n", type);
Zhu Yi257862f2009-06-15 21:59:56 +0200715 return -EOPNOTSUPP;
716 }
717
718 return 0;
719}
720
721static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
722{
723 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
724
Samuel Ortiz88e61952009-10-16 13:18:53 +0800725 *dbm = iwm->txpower >> 1;
Zhu Yi257862f2009-06-15 21:59:56 +0200726
727 return 0;
728}
729
Johannes Bergbc92afd2009-07-01 21:26:57 +0200730static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
731 struct net_device *dev,
732 bool enabled, int timeout)
733{
734 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
735 u32 power_index;
736
737 if (enabled)
738 power_index = IWM_POWER_INDEX_DEFAULT;
739 else
740 power_index = IWM_POWER_INDEX_MIN;
741
742 if (power_index == iwm->conf.power_index)
743 return 0;
744
745 iwm->conf.power_index = power_index;
746
747 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
748 CFG_POWER_INDEX, iwm->conf.power_index);
749}
750
Zhu Yid281fd42010-02-25 14:15:30 +0800751static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy,
752 struct net_device *netdev,
753 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100754{
755 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
756
757 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
758}
759
Zhu Yid281fd42010-02-25 14:15:30 +0800760static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy,
761 struct net_device *netdev,
762 struct cfg80211_pmksa *pmksa)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100763{
764 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
765
766 return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
767}
768
Zhu Yid281fd42010-02-25 14:15:30 +0800769static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy,
770 struct net_device *netdev)
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100771{
772 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
773 struct cfg80211_pmksa pmksa;
774
775 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
776
777 return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
778}
779
780
Zhu Yibb9f8692009-05-21 21:20:45 +0800781static struct cfg80211_ops iwm_cfg80211_ops = {
782 .change_virtual_intf = iwm_cfg80211_change_iface,
Samuel Ortiz13e0fe72009-06-15 21:59:52 +0200783 .add_key = iwm_cfg80211_add_key,
784 .get_key = iwm_cfg80211_get_key,
785 .del_key = iwm_cfg80211_del_key,
786 .set_default_key = iwm_cfg80211_set_default_key,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800787 .get_station = iwm_cfg80211_get_station,
Zhu Yibb9f8692009-05-21 21:20:45 +0800788 .scan = iwm_cfg80211_scan,
789 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
Samuel Ortiz9967d462009-07-16 17:34:10 +0800790 .connect = iwm_cfg80211_connect,
791 .disconnect = iwm_cfg80211_disconnect,
Zhu Yibb9f8692009-05-21 21:20:45 +0800792 .join_ibss = iwm_cfg80211_join_ibss,
793 .leave_ibss = iwm_cfg80211_leave_ibss,
Zhu Yi257862f2009-06-15 21:59:56 +0200794 .set_tx_power = iwm_cfg80211_set_txpower,
795 .get_tx_power = iwm_cfg80211_get_txpower,
Johannes Bergbc92afd2009-07-01 21:26:57 +0200796 .set_power_mgmt = iwm_cfg80211_set_power_mgmt,
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100797 .set_pmksa = iwm_cfg80211_set_pmksa,
798 .del_pmksa = iwm_cfg80211_del_pmksa,
799 .flush_pmksa = iwm_cfg80211_flush_pmksa,
Zhu Yibb9f8692009-05-21 21:20:45 +0800800};
801
Zhu Yi49b77722009-07-16 17:34:08 +0800802static const u32 cipher_suites[] = {
803 WLAN_CIPHER_SUITE_WEP40,
804 WLAN_CIPHER_SUITE_WEP104,
805 WLAN_CIPHER_SUITE_TKIP,
806 WLAN_CIPHER_SUITE_CCMP,
807};
808
Zhu Yibb9f8692009-05-21 21:20:45 +0800809struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
810{
811 int ret = 0;
812 struct wireless_dev *wdev;
813
814 /*
815 * We're trying to have the following memory
816 * layout:
817 *
818 * +-------------------------+
819 * | struct wiphy |
820 * +-------------------------+
821 * | struct iwm_priv |
822 * +-------------------------+
823 * | bus private data |
824 * | (e.g. iwm_priv_sdio) |
825 * +-------------------------+
826 *
827 */
828
829 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
830 if (!wdev) {
831 dev_err(dev, "Couldn't allocate wireless device\n");
832 return ERR_PTR(-ENOMEM);
833 }
834
835 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
836 sizeof(struct iwm_priv) + sizeof_bus);
837 if (!wdev->wiphy) {
838 dev_err(dev, "Couldn't allocate wiphy device\n");
839 ret = -ENOMEM;
840 goto out_err_new;
841 }
842
843 set_wiphy_dev(wdev->wiphy, dev);
844 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
Samuel Ortiz9bf22f22009-11-25 00:02:26 +0100845 wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
Zhu Yibb9f8692009-05-21 21:20:45 +0800846 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
847 BIT(NL80211_IFTYPE_ADHOC);
848 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
849 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
850 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
851
Zhu Yi49b77722009-07-16 17:34:08 +0800852 wdev->wiphy->cipher_suites = cipher_suites;
853 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
854
Zhu Yibb9f8692009-05-21 21:20:45 +0800855 ret = wiphy_register(wdev->wiphy);
856 if (ret < 0) {
857 dev_err(dev, "Couldn't register wiphy device\n");
858 goto out_err_register;
859 }
860
861 return wdev;
862
863 out_err_register:
864 wiphy_free(wdev->wiphy);
865
866 out_err_new:
867 kfree(wdev);
868
869 return ERR_PTR(ret);
870}
871
872void iwm_wdev_free(struct iwm_priv *iwm)
873{
874 struct wireless_dev *wdev = iwm_to_wdev(iwm);
875
876 if (!wdev)
877 return;
878
879 wiphy_unregister(wdev->wiphy);
880 wiphy_free(wdev->wiphy);
881 kfree(wdev);
882}