blob: 29e63938684fa204a5c8516c4b03a34ac1a76097 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "core.h"
18#include "cfg80211.h"
19#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030020#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030021#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030022
Jouni Malinen6bbc7c32011-09-05 17:38:47 +030023static unsigned int ath6kl_p2p;
24
25module_param(ath6kl_p2p, uint, 0644);
26
Kalle Valobdcd8172011-07-18 00:22:30 +030027#define RATETAB_ENT(_rate, _rateid, _flags) { \
28 .bitrate = (_rate), \
29 .flags = (_flags), \
30 .hw_value = (_rateid), \
31}
32
33#define CHAN2G(_channel, _freq, _flags) { \
34 .band = IEEE80211_BAND_2GHZ, \
35 .hw_value = (_channel), \
36 .center_freq = (_freq), \
37 .flags = (_flags), \
38 .max_antenna_gain = 0, \
39 .max_power = 30, \
40}
41
42#define CHAN5G(_channel, _flags) { \
43 .band = IEEE80211_BAND_5GHZ, \
44 .hw_value = (_channel), \
45 .center_freq = 5000 + (5 * (_channel)), \
46 .flags = (_flags), \
47 .max_antenna_gain = 0, \
48 .max_power = 30, \
49}
50
51static struct ieee80211_rate ath6kl_rates[] = {
52 RATETAB_ENT(10, 0x1, 0),
53 RATETAB_ENT(20, 0x2, 0),
54 RATETAB_ENT(55, 0x4, 0),
55 RATETAB_ENT(110, 0x8, 0),
56 RATETAB_ENT(60, 0x10, 0),
57 RATETAB_ENT(90, 0x20, 0),
58 RATETAB_ENT(120, 0x40, 0),
59 RATETAB_ENT(180, 0x80, 0),
60 RATETAB_ENT(240, 0x100, 0),
61 RATETAB_ENT(360, 0x200, 0),
62 RATETAB_ENT(480, 0x400, 0),
63 RATETAB_ENT(540, 0x800, 0),
64};
65
66#define ath6kl_a_rates (ath6kl_rates + 4)
67#define ath6kl_a_rates_size 8
68#define ath6kl_g_rates (ath6kl_rates + 0)
69#define ath6kl_g_rates_size 12
70
71static struct ieee80211_channel ath6kl_2ghz_channels[] = {
72 CHAN2G(1, 2412, 0),
73 CHAN2G(2, 2417, 0),
74 CHAN2G(3, 2422, 0),
75 CHAN2G(4, 2427, 0),
76 CHAN2G(5, 2432, 0),
77 CHAN2G(6, 2437, 0),
78 CHAN2G(7, 2442, 0),
79 CHAN2G(8, 2447, 0),
80 CHAN2G(9, 2452, 0),
81 CHAN2G(10, 2457, 0),
82 CHAN2G(11, 2462, 0),
83 CHAN2G(12, 2467, 0),
84 CHAN2G(13, 2472, 0),
85 CHAN2G(14, 2484, 0),
86};
87
88static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
89 CHAN5G(34, 0), CHAN5G(36, 0),
90 CHAN5G(38, 0), CHAN5G(40, 0),
91 CHAN5G(42, 0), CHAN5G(44, 0),
92 CHAN5G(46, 0), CHAN5G(48, 0),
93 CHAN5G(52, 0), CHAN5G(56, 0),
94 CHAN5G(60, 0), CHAN5G(64, 0),
95 CHAN5G(100, 0), CHAN5G(104, 0),
96 CHAN5G(108, 0), CHAN5G(112, 0),
97 CHAN5G(116, 0), CHAN5G(120, 0),
98 CHAN5G(124, 0), CHAN5G(128, 0),
99 CHAN5G(132, 0), CHAN5G(136, 0),
100 CHAN5G(140, 0), CHAN5G(149, 0),
101 CHAN5G(153, 0), CHAN5G(157, 0),
102 CHAN5G(161, 0), CHAN5G(165, 0),
103 CHAN5G(184, 0), CHAN5G(188, 0),
104 CHAN5G(192, 0), CHAN5G(196, 0),
105 CHAN5G(200, 0), CHAN5G(204, 0),
106 CHAN5G(208, 0), CHAN5G(212, 0),
107 CHAN5G(216, 0),
108};
109
110static struct ieee80211_supported_band ath6kl_band_2ghz = {
111 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
112 .channels = ath6kl_2ghz_channels,
113 .n_bitrates = ath6kl_g_rates_size,
114 .bitrates = ath6kl_g_rates,
115};
116
117static struct ieee80211_supported_band ath6kl_band_5ghz = {
118 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
119 .channels = ath6kl_5ghz_a_channels,
120 .n_bitrates = ath6kl_a_rates_size,
121 .bitrates = ath6kl_a_rates,
122};
123
Jouni Malinen837cb972011-10-11 17:31:57 +0300124#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
125
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530126static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300127 enum nl80211_wpa_versions wpa_version)
128{
129 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
130
131 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530132 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300133 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530134 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300135 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530136 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300137 } else {
138 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
139 return -ENOTSUPP;
140 }
141
142 return 0;
143}
144
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530145static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300146 enum nl80211_auth_type auth_type)
147{
Kalle Valobdcd8172011-07-18 00:22:30 +0300148 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
149
150 switch (auth_type) {
151 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530152 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300153 break;
154 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530155 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300156 break;
157 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530158 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300159 break;
160
161 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530162 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300163 break;
164
165 default:
166 ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
167 return -ENOTSUPP;
168 }
169
170 return 0;
171}
172
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530173static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300174{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530175 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
176 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
177 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300178
179 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
180 __func__, cipher, ucast);
181
182 switch (cipher) {
183 case 0:
184 /* our own hack to use value 0 as no crypto used */
185 *ar_cipher = NONE_CRYPT;
186 *ar_cipher_len = 0;
187 break;
188 case WLAN_CIPHER_SUITE_WEP40:
189 *ar_cipher = WEP_CRYPT;
190 *ar_cipher_len = 5;
191 break;
192 case WLAN_CIPHER_SUITE_WEP104:
193 *ar_cipher = WEP_CRYPT;
194 *ar_cipher_len = 13;
195 break;
196 case WLAN_CIPHER_SUITE_TKIP:
197 *ar_cipher = TKIP_CRYPT;
198 *ar_cipher_len = 0;
199 break;
200 case WLAN_CIPHER_SUITE_CCMP:
201 *ar_cipher = AES_CRYPT;
202 *ar_cipher_len = 0;
203 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200204 case WLAN_CIPHER_SUITE_SMS4:
205 *ar_cipher = WAPI_CRYPT;
206 *ar_cipher_len = 0;
207 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300208 default:
209 ath6kl_err("cipher 0x%x not supported\n", cipher);
210 return -ENOTSUPP;
211 }
212
213 return 0;
214}
215
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530216static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300217{
218 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
219
220 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530221 if (vif->auth_mode == WPA_AUTH)
222 vif->auth_mode = WPA_PSK_AUTH;
223 else if (vif->auth_mode == WPA2_AUTH)
224 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300225 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530226 if (vif->auth_mode == WPA_AUTH)
227 vif->auth_mode = WPA_AUTH_CCKM;
228 else if (vif->auth_mode == WPA2_AUTH)
229 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300230 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530231 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300232 }
233}
234
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530235static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300236{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530237 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530238
Kalle Valobdcd8172011-07-18 00:22:30 +0300239 if (!test_bit(WMI_READY, &ar->flag)) {
240 ath6kl_err("wmi is not ready\n");
241 return false;
242 }
243
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530244 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300245 ath6kl_err("wlan disabled\n");
246 return false;
247 }
248
249 return true;
250}
251
Kevin Fang6981ffd2011-10-07 08:51:19 +0800252static bool ath6kl_is_wpa_ie(const u8 *pos)
253{
254 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
255 pos[2] == 0x00 && pos[3] == 0x50 &&
256 pos[4] == 0xf2 && pos[5] == 0x01;
257}
258
259static bool ath6kl_is_rsn_ie(const u8 *pos)
260{
261 return pos[0] == WLAN_EID_RSN;
262}
263
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700264static bool ath6kl_is_wps_ie(const u8 *pos)
265{
266 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
267 pos[1] >= 4 &&
268 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
269 pos[5] == 0x04);
270}
271
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530272static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
273 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800274{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530275 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800276 const u8 *pos;
277 u8 *buf = NULL;
278 size_t len = 0;
279 int ret;
280
281 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700282 * Clear previously set flag
283 */
284
285 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
286
287 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800288 * Filter out RSN/WPA IE(s)
289 */
290
291 if (ies && ies_len) {
292 buf = kmalloc(ies_len, GFP_KERNEL);
293 if (buf == NULL)
294 return -ENOMEM;
295 pos = ies;
296
297 while (pos + 1 < ies + ies_len) {
298 if (pos + 2 + pos[1] > ies + ies_len)
299 break;
300 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
301 memcpy(buf + len, pos, 2 + pos[1]);
302 len += 2 + pos[1];
303 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700304
305 if (ath6kl_is_wps_ie(pos))
306 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
307
Kevin Fang6981ffd2011-10-07 08:51:19 +0800308 pos += 2 + pos[1];
309 }
310 }
311
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530312 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
313 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800314 kfree(buf);
315 return ret;
316}
317
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530318static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
319{
320 switch (type) {
321 case NL80211_IFTYPE_STATION:
322 *nw_type = INFRA_NETWORK;
323 break;
324 case NL80211_IFTYPE_ADHOC:
325 *nw_type = ADHOC_NETWORK;
326 break;
327 case NL80211_IFTYPE_AP:
328 *nw_type = AP_NETWORK;
329 break;
330 case NL80211_IFTYPE_P2P_CLIENT:
331 *nw_type = INFRA_NETWORK;
332 break;
333 case NL80211_IFTYPE_P2P_GO:
334 *nw_type = AP_NETWORK;
335 break;
336 default:
337 ath6kl_err("invalid interface type %u\n", type);
338 return -ENOTSUPP;
339 }
340
341 return 0;
342}
343
344static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
345 u8 *if_idx, u8 *nw_type)
346{
347 int i;
348
349 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
350 return false;
351
352 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
353 ar->num_vif))
354 return false;
355
356 if (type == NL80211_IFTYPE_STATION ||
357 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200358 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530359 if ((ar->avail_idx_map >> i) & BIT(0)) {
360 *if_idx = i;
361 return true;
362 }
363 }
364 }
365
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530366 if (type == NL80211_IFTYPE_P2P_CLIENT ||
367 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200368 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530369 if ((ar->avail_idx_map >> i) & BIT(0)) {
370 *if_idx = i;
371 return true;
372 }
373 }
374 }
375
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530376 return false;
377}
378
Kalle Valobdcd8172011-07-18 00:22:30 +0300379static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
380 struct cfg80211_connect_params *sme)
381{
382 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530383 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300384 int status;
385
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530386 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300387
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530388 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300389 return -EIO;
390
391 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
392 ath6kl_err("destroy in progress\n");
393 return -EBUSY;
394 }
395
396 if (test_bit(SKIP_SCAN, &ar->flag) &&
397 ((sme->channel && sme->channel->center_freq == 0) ||
398 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
399 ath6kl_err("SkipScan: channel or bssid invalid\n");
400 return -EINVAL;
401 }
402
403 if (down_interruptible(&ar->sem)) {
404 ath6kl_err("busy, couldn't get access\n");
405 return -ERESTARTSYS;
406 }
407
408 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
409 ath6kl_err("busy, destroy in progress\n");
410 up(&ar->sem);
411 return -EBUSY;
412 }
413
414 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
415 /*
416 * sleep until the command queue drains
417 */
418 wait_event_interruptible_timeout(ar->event_wq,
419 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
420 WMI_TIMEOUT);
421 if (signal_pending(current)) {
422 ath6kl_err("cmd queue drain timeout\n");
423 up(&ar->sem);
424 return -EINTR;
425 }
426 }
427
Kevin Fang6981ffd2011-10-07 08:51:19 +0800428 if (sme->ie && (sme->ie_len > 0)) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530429 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
Dan Carpenter743b4512011-11-18 17:09:32 +0300430 if (status) {
431 up(&ar->sem);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800432 return status;
Dan Carpenter743b4512011-11-18 17:09:32 +0300433 }
Raja Mani542c5192011-11-15 14:14:56 +0530434 } else
435 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800436
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530437 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530438 vif->ssid_len == sme->ssid_len &&
439 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530440 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530441 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
442 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530443 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300444
445 up(&ar->sem);
446 if (status) {
447 ath6kl_err("wmi_reconnect_cmd failed\n");
448 return -EIO;
449 }
450 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530451 } else if (vif->ssid_len == sme->ssid_len &&
452 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530453 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300454 }
455
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530456 memset(vif->ssid, 0, sizeof(vif->ssid));
457 vif->ssid_len = sme->ssid_len;
458 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300459
460 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530461 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300462
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530463 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300464 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530465 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300466
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530467 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300468
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530469 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300470 if (status) {
471 up(&ar->sem);
472 return status;
473 }
474
475 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530476 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300477 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530478 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300479
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530480 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300481
482 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530483 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300484
485 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530486 (vif->auth_mode == NONE_AUTH) &&
487 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300488 struct ath6kl_key *key = NULL;
489
490 if (sme->key_idx < WMI_MIN_KEY_INDEX ||
491 sme->key_idx > WMI_MAX_KEY_INDEX) {
492 ath6kl_err("key index %d out of bounds\n",
493 sme->key_idx);
494 up(&ar->sem);
495 return -ENOENT;
496 }
497
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530498 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300499 key->key_len = sme->key_len;
500 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530501 key->cipher = vif->prwise_crypto;
502 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300503
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530504 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530505 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300506 GROUP_USAGE | TX_USAGE,
507 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200508 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300509 key->key, KEY_OP_INIT_VAL, NULL,
510 NO_SYNC_WMIFLAG);
511 }
512
513 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530514 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530515 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
516 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300517 ath6kl_err("couldn't set bss filtering\n");
518 up(&ar->sem);
519 return -EIO;
520 }
521 }
522
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530523 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300524
525 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
526 "%s: connect called with authmode %d dot11 auth %d"
527 " PW crypto %d PW crypto len %d GRP crypto %d"
528 " GRP crypto len %d channel hint %u\n",
529 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530530 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
531 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530532 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300533
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530534 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530535 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530536 vif->dot11_auth_mode, vif->auth_mode,
537 vif->prwise_crypto,
538 vif->prwise_crypto_len,
539 vif->grp_crypto, vif->grp_crypto_len,
540 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530541 vif->req_bssid, vif->ch_hint,
Kalle Valobdcd8172011-07-18 00:22:30 +0300542 ar->connect_ctrl_flags);
543
544 up(&ar->sem);
545
546 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530547 memset(vif->ssid, 0, sizeof(vif->ssid));
548 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300549 ath6kl_err("invalid request\n");
550 return -ENOENT;
551 } else if (status) {
552 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
553 return -EIO;
554 }
555
556 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530557 ((vif->auth_mode == WPA_PSK_AUTH)
558 || (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530559 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
561 }
562
563 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530564 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300565
566 return 0;
567}
568
Raja Mani4eab6f42011-11-09 17:02:23 +0530569static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
570 enum network_type nw_type,
571 const u8 *bssid,
Jouni Malinen01cac472011-09-19 19:14:59 +0300572 struct ieee80211_channel *chan,
573 const u8 *beacon_ie, size_t beacon_ie_len)
574{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530575 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300576 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530577 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300578 u8 *ie;
579
Raja Mani4eab6f42011-11-09 17:02:23 +0530580 if (nw_type & ADHOC_NETWORK) {
581 cap_mask = WLAN_CAPABILITY_IBSS;
582 cap_val = WLAN_CAPABILITY_IBSS;
583 } else {
584 cap_mask = WLAN_CAPABILITY_ESS;
585 cap_val = WLAN_CAPABILITY_ESS;
586 }
587
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530588 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530589 vif->ssid, vif->ssid_len,
590 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300591 if (bss == NULL) {
592 /*
593 * Since cfg80211 may not yet know about the BSS,
594 * generate a partial entry until the first BSS info
595 * event becomes available.
596 *
597 * Prepend SSID element since it is not included in the Beacon
598 * IEs from the target.
599 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530600 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300601 if (ie == NULL)
602 return -ENOMEM;
603 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530604 ie[1] = vif->ssid_len;
605 memcpy(ie + 2, vif->ssid, vif->ssid_len);
606 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530607 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530608 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530609 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300610 0, GFP_KERNEL);
611 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530612 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
613 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300614 kfree(ie);
615 } else
616 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
617 "entry\n");
618
619 if (bss == NULL)
620 return -ENOMEM;
621
622 cfg80211_put_bss(bss);
623
624 return 0;
625}
626
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530627void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300628 u8 *bssid, u16 listen_intvl,
629 u16 beacon_intvl,
630 enum network_type nw_type,
631 u8 beacon_ie_len, u8 assoc_req_len,
632 u8 assoc_resp_len, u8 *assoc_info)
633{
Jouni Malinen01cac472011-09-19 19:14:59 +0300634 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530635 struct ath6kl *ar = vif->ar;
Kalle Valobdcd8172011-07-18 00:22:30 +0300636
637 /* capinfo + listen interval */
638 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
639
640 /* capinfo + status code + associd */
641 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
642
643 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
644 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
645 assoc_resp_ie_offset;
646
647 assoc_req_len -= assoc_req_ie_offset;
648 assoc_resp_len -= assoc_resp_ie_offset;
649
Jouni Malinen32c10872011-09-19 19:15:07 +0300650 /*
651 * Store Beacon interval here; DTIM period will be available only once
652 * a Beacon frame from the AP is seen.
653 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530654 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530655 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300656
Kalle Valobdcd8172011-07-18 00:22:30 +0300657 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530658 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300659 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
660 "%s: ath6k not in ibss mode\n", __func__);
661 return;
662 }
663 }
664
665 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530666 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
667 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300668 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
669 "%s: ath6k not in station mode\n", __func__);
670 return;
671 }
672 }
673
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530674 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300675
Raja Mani4eab6f42011-11-09 17:02:23 +0530676 if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
677 beacon_ie_len) < 0) {
678 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300679 return;
680 }
681
Raja Mani4eab6f42011-11-09 17:02:23 +0530682 if (nw_type & ADHOC_NETWORK) {
683 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
684 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
685 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300686 return;
687 }
688
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530689 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300690 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530691 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530692 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300693 assoc_req_ie, assoc_req_len,
694 assoc_resp_ie, assoc_resp_len,
695 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530696 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300697 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530698 cfg80211_roamed(vif->ndev, chan, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300699 assoc_req_ie, assoc_req_len,
700 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
701 }
702}
703
704static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
705 struct net_device *dev, u16 reason_code)
706{
707 struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530708 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300709
710 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
711 reason_code);
712
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530713 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300714 return -EIO;
715
716 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
717 ath6kl_err("busy, destroy in progress\n");
718 return -EBUSY;
719 }
720
721 if (down_interruptible(&ar->sem)) {
722 ath6kl_err("busy, couldn't get access\n");
723 return -ERESTARTSYS;
724 }
725
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530726 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530727 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530728 memset(vif->ssid, 0, sizeof(vif->ssid));
729 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300730
731 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530732 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300733
734 up(&ar->sem);
735
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530736 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530737
Kalle Valobdcd8172011-07-18 00:22:30 +0300738 return 0;
739}
740
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530741void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300742 u8 *bssid, u8 assoc_resp_len,
743 u8 *assoc_info, u16 proto_reason)
744{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530745 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530746
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530747 if (vif->scan_req) {
748 cfg80211_scan_done(vif->scan_req, true);
749 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300750 }
751
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530752 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530753 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300754 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
755 "%s: ath6k not in ibss mode\n", __func__);
756 return;
757 }
758 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530759 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300760 return;
761 }
762
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530763 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530764 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
765 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300766 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
767 "%s: ath6k not in station mode\n", __func__);
768 return;
769 }
770 }
771
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530772 /*
773 * Send a disconnect command to target when a disconnect event is
774 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
775 * request from host) to make the firmware stop trying to connect even
776 * after giving disconnect event. There will be one more disconnect
777 * event for this disconnect command with reason code DISCONNECT_CMD
778 * which will be notified to cfg80211.
779 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300780
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530781 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530782 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300783 return;
784 }
785
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530786 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300787
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530788 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530789 cfg80211_connect_result(vif->ndev,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530790 bssid, NULL, 0,
791 NULL, 0,
792 WLAN_STATUS_UNSPECIFIED_FAILURE,
793 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530794 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530795 cfg80211_disconnected(vif->ndev, reason,
Vasanthakumar Thiagarajanac59a2b2011-09-10 15:26:34 +0530796 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300797 }
798
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530799 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300800}
801
Kalle Valobdcd8172011-07-18 00:22:30 +0300802static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
803 struct cfg80211_scan_request *request)
804{
805 struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530806 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300807 s8 n_channels = 0;
808 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300809 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530810 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300811
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530812 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300813 return -EIO;
814
815 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530816 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300817 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530818 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530819 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300820 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
821 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300822 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300823 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300824 }
825 }
826
827 if (request->n_ssids && request->ssids[0].ssid_len) {
828 u8 i;
829
830 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
831 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
832
833 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530834 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
835 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300836 request->ssids[i].ssid_len,
837 request->ssids[i].ssid);
838 }
839
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300840 if (request->ie) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530841 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
842 WMI_FRAME_PROBE_REQ,
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300843 request->ie, request->ie_len);
844 if (ret) {
845 ath6kl_err("failed to set Probe Request appie for "
846 "scan");
847 return ret;
848 }
849 }
850
Jouni Malinen11869be2011-09-02 20:07:06 +0300851 /*
852 * Scan only the requested channels if the request specifies a set of
853 * channels. If the list is longer than the target supports, do not
854 * configure the list and instead, scan all available channels.
855 */
856 if (request->n_channels > 0 &&
857 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300858 u8 i;
859
Jouni Malinen11869be2011-09-02 20:07:06 +0300860 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300861
862 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
863 if (channels == NULL) {
864 ath6kl_warn("failed to set scan channels, "
865 "scan all channels");
866 n_channels = 0;
867 }
868
869 for (i = 0; i < n_channels; i++)
870 channels[i] = request->channels[i]->center_freq;
871 }
872
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530873 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530874 force_fg_scan = 1;
875
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530876 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
877 force_fg_scan, false, 0, 0, n_channels,
878 channels);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300879 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300880 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300881 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530882 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300883
Edward Lu1276c9e2011-08-30 21:58:00 +0300884 kfree(channels);
885
Kalle Valobdcd8172011-07-18 00:22:30 +0300886 return ret;
887}
888
Kalle Valo1c17d312011-11-01 08:43:56 +0200889void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300890{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530891 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300892 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300893
Kalle Valo1c17d312011-11-01 08:43:56 +0200894 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
895 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300896
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530897 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300898 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300899
Kalle Valo1c17d312011-11-01 08:43:56 +0200900 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300901 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300902
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530903 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
904 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530905 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
906 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300907 0, NULL);
908 }
909 }
910
911out:
Kalle Valocb938212011-10-27 18:47:46 +0300912 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530913 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300914}
915
916static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
917 u8 key_index, bool pairwise,
918 const u8 *mac_addr,
919 struct key_params *params)
920{
921 struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530922 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300923 struct ath6kl_key *key = NULL;
924 u8 key_usage;
925 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +0300926
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530927 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300928 return -EIO;
929
Jouni Malinen837cb972011-10-11 17:31:57 +0300930 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
931 if (params->key_len != WMI_KRK_LEN)
932 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530933 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
934 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +0300935 }
936
Kalle Valobdcd8172011-07-18 00:22:30 +0300937 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
938 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
939 "%s: key index %d out of bounds\n", __func__,
940 key_index);
941 return -ENOENT;
942 }
943
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530944 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +0300945 memset(key, 0, sizeof(struct ath6kl_key));
946
947 if (pairwise)
948 key_usage = PAIRWISE_USAGE;
949 else
950 key_usage = GROUP_USAGE;
951
952 if (params) {
Dai Shuibing5e070212011-11-03 11:39:37 +0200953 int seq_len = params->seq_len;
954 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
955 seq_len > ATH6KL_KEY_SEQ_LEN) {
956 /* Only first half of the WPI PN is configured */
957 seq_len = ATH6KL_KEY_SEQ_LEN;
958 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300959 if (params->key_len > WLAN_MAX_KEY_LEN ||
Dai Shuibing5e070212011-11-03 11:39:37 +0200960 seq_len > sizeof(key->seq))
Kalle Valobdcd8172011-07-18 00:22:30 +0300961 return -EINVAL;
962
963 key->key_len = params->key_len;
964 memcpy(key->key, params->key, key->key_len);
Dai Shuibing5e070212011-11-03 11:39:37 +0200965 key->seq_len = seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300966 memcpy(key->seq, params->seq, key->seq_len);
967 key->cipher = params->cipher;
968 }
969
970 switch (key->cipher) {
971 case WLAN_CIPHER_SUITE_WEP40:
972 case WLAN_CIPHER_SUITE_WEP104:
973 key_type = WEP_CRYPT;
974 break;
975
976 case WLAN_CIPHER_SUITE_TKIP:
977 key_type = TKIP_CRYPT;
978 break;
979
980 case WLAN_CIPHER_SUITE_CCMP:
981 key_type = AES_CRYPT;
982 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200983 case WLAN_CIPHER_SUITE_SMS4:
984 key_type = WAPI_CRYPT;
985 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300986
987 default:
988 return -ENOTSUPP;
989 }
990
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530991 if (((vif->auth_mode == WPA_PSK_AUTH)
992 || (vif->auth_mode == WPA2_PSK_AUTH))
Kalle Valobdcd8172011-07-18 00:22:30 +0300993 && (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530994 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +0300995
996 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
997 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
998 __func__, key_index, key->key_len, key_type,
999 key_usage, key->seq_len);
1000
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301001 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001002 (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
1003 ar->ap_mode_bkey.valid = true;
1004 ar->ap_mode_bkey.key_index = key_index;
1005 ar->ap_mode_bkey.key_type = key_type;
1006 ar->ap_mode_bkey.key_len = key->key_len;
1007 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301008 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001009 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1010 "key configuration until AP mode has been "
1011 "started\n");
1012 /*
1013 * The key will be set in ath6kl_connect_ap_mode() once
1014 * the connected event is received from the target.
1015 */
1016 return 0;
1017 }
1018 }
1019
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301020 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301021 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001022 /*
1023 * Store the key locally so that it can be re-configured after
1024 * the AP mode has properly started
1025 * (ath6kl_install_statioc_wep_keys).
1026 */
1027 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1028 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301029 vif->wep_key_list[key_index].key_len = key->key_len;
1030 memcpy(vif->wep_key_list[key_index].key, key->key,
1031 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001032 return 0;
1033 }
1034
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301035 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001036 key_type, key_usage, key->key_len,
1037 key->seq, key->seq_len, key->key,
1038 KEY_OP_INIT_VAL,
1039 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001040}
1041
1042static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1043 u8 key_index, bool pairwise,
1044 const u8 *mac_addr)
1045{
1046 struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301047 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001048
1049 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1050
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301051 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001052 return -EIO;
1053
1054 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1055 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1056 "%s: key index %d out of bounds\n", __func__,
1057 key_index);
1058 return -ENOENT;
1059 }
1060
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301061 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001062 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1063 "%s: index %d is empty\n", __func__, key_index);
1064 return 0;
1065 }
1066
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301067 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001068
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301069 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001070}
1071
1072static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1073 u8 key_index, bool pairwise,
1074 const u8 *mac_addr, void *cookie,
1075 void (*callback) (void *cookie,
1076 struct key_params *))
1077{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301078 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001079 struct ath6kl_key *key = NULL;
1080 struct key_params params;
1081
1082 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1083
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301084 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001085 return -EIO;
1086
1087 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1088 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1089 "%s: key index %d out of bounds\n", __func__,
1090 key_index);
1091 return -ENOENT;
1092 }
1093
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301094 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001095 memset(&params, 0, sizeof(params));
1096 params.cipher = key->cipher;
1097 params.key_len = key->key_len;
1098 params.seq_len = key->seq_len;
1099 params.seq = key->seq;
1100 params.key = key->key;
1101
1102 callback(cookie, &params);
1103
1104 return key->key_len ? 0 : -ENOENT;
1105}
1106
1107static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1108 struct net_device *ndev,
1109 u8 key_index, bool unicast,
1110 bool multicast)
1111{
1112 struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301113 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001114 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001115 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001116 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001117
1118 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1119
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301120 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001121 return -EIO;
1122
1123 if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
1124 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1125 "%s: key index %d out of bounds\n",
1126 __func__, key_index);
1127 return -ENOENT;
1128 }
1129
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301130 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001131 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1132 __func__, key_index);
1133 return -EINVAL;
1134 }
1135
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301136 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301137 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001138 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301139 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001140 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001141 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301142 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001143 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301144 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001145
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301146 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001147 return 0; /* Delay until AP mode has been started */
1148
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001149 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1150 vif->def_txkey_index,
1151 key_type, key_usage,
1152 key->key_len, key->seq, key->seq_len,
1153 key->key,
1154 KEY_OP_INIT_VAL, NULL,
1155 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001156}
1157
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301158void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001159 bool ismcast)
1160{
1161 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1162 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1163
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301164 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001165 (ismcast ? NL80211_KEYTYPE_GROUP :
1166 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1167 GFP_KERNEL);
1168}
1169
1170static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1171{
1172 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301173 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 int ret;
1175
1176 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1177 changed);
1178
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301179 vif = ath6kl_vif_first(ar);
1180 if (!vif)
1181 return -EIO;
1182
1183 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001184 return -EIO;
1185
1186 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1187 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1188 if (ret != 0) {
1189 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1190 return -EIO;
1191 }
1192 }
1193
1194 return 0;
1195}
1196
1197/*
1198 * The type nl80211_tx_power_setting replaces the following
1199 * data type from 2.6.36 onwards
1200*/
1201static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1202 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001203 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001204{
1205 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301206 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001207 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001208 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001209
1210 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1211 type, dbm);
1212
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301213 vif = ath6kl_vif_first(ar);
1214 if (!vif)
1215 return -EIO;
1216
1217 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001218 return -EIO;
1219
1220 switch (type) {
1221 case NL80211_TX_POWER_AUTOMATIC:
1222 return 0;
1223 case NL80211_TX_POWER_LIMITED:
1224 ar->tx_pwr = ath6kl_dbm = dbm;
1225 break;
1226 default:
1227 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1228 __func__, type);
1229 return -EOPNOTSUPP;
1230 }
1231
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301232 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001233
1234 return 0;
1235}
1236
1237static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1238{
1239 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301240 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001241
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301242 vif = ath6kl_vif_first(ar);
1243 if (!vif)
1244 return -EIO;
1245
1246 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001247 return -EIO;
1248
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301249 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001250 ar->tx_pwr = 0;
1251
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301252 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001253 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1254 return -EIO;
1255 }
1256
1257 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1258 5 * HZ);
1259
1260 if (signal_pending(current)) {
1261 ath6kl_err("target did not respond\n");
1262 return -EINTR;
1263 }
1264 }
1265
1266 *dbm = ar->tx_pwr;
1267 return 0;
1268}
1269
1270static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1271 struct net_device *dev,
1272 bool pmgmt, int timeout)
1273{
1274 struct ath6kl *ar = ath6kl_priv(dev);
1275 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301276 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001277
1278 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1279 __func__, pmgmt, timeout);
1280
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301281 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001282 return -EIO;
1283
1284 if (pmgmt) {
1285 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1286 mode.pwr_mode = REC_POWER;
1287 } else {
1288 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1289 mode.pwr_mode = MAX_PERF_POWER;
1290 }
1291
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301292 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
1293 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001294 ath6kl_err("wmi_powermode_cmd failed\n");
1295 return -EIO;
1296 }
1297
1298 return 0;
1299}
1300
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301301static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1302 char *name,
1303 enum nl80211_iftype type,
1304 u32 *flags,
1305 struct vif_params *params)
1306{
1307 struct ath6kl *ar = wiphy_priv(wiphy);
1308 struct net_device *ndev;
1309 u8 if_idx, nw_type;
1310
Kalle Valo71f96ee2011-11-14 19:31:30 +02001311 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301312 ath6kl_err("Reached maximum number of supported vif\n");
1313 return ERR_PTR(-EINVAL);
1314 }
1315
1316 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1317 ath6kl_err("Not a supported interface type\n");
1318 return ERR_PTR(-EINVAL);
1319 }
1320
1321 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1322 if (!ndev)
1323 return ERR_PTR(-ENOMEM);
1324
1325 ar->num_vif++;
1326
1327 return ndev;
1328}
1329
1330static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1331 struct net_device *ndev)
1332{
1333 struct ath6kl *ar = wiphy_priv(wiphy);
1334 struct ath6kl_vif *vif = netdev_priv(ndev);
1335
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301336 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301337 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301338 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301339
1340 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1341
1342 ath6kl_deinit_if_data(vif);
1343
1344 return 0;
1345}
1346
Kalle Valobdcd8172011-07-18 00:22:30 +03001347static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1348 struct net_device *ndev,
1349 enum nl80211_iftype type, u32 *flags,
1350 struct vif_params *params)
1351{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301352 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001353
1354 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1355
Kalle Valobdcd8172011-07-18 00:22:30 +03001356 switch (type) {
1357 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301358 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001359 break;
1360 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301361 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001362 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001363 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301364 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001365 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001366 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301367 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001368 break;
1369 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301370 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001371 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001372 default:
1373 ath6kl_err("invalid interface type %u\n", type);
1374 return -EOPNOTSUPP;
1375 }
1376
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301377 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001378
1379 return 0;
1380}
1381
1382static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1383 struct net_device *dev,
1384 struct cfg80211_ibss_params *ibss_param)
1385{
1386 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301387 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001388 int status;
1389
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301390 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001391 return -EIO;
1392
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301393 vif->ssid_len = ibss_param->ssid_len;
1394 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001395
1396 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301397 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001398
1399 if (ibss_param->channel_fixed) {
1400 /*
1401 * TODO: channel_fixed: The channel should be fixed, do not
1402 * search for IBSSs to join on other channels. Target
1403 * firmware does not support this feature, needs to be
1404 * updated.
1405 */
1406 return -EOPNOTSUPP;
1407 }
1408
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301409 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001410 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301411 memcpy(vif->req_bssid, ibss_param->bssid,
1412 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001413
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301414 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001415
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301416 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001417 if (status)
1418 return status;
1419
1420 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301421 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1422 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001423 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301424 ath6kl_set_cipher(vif, 0, true);
1425 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001426 }
1427
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301428 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001429
1430 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1431 "%s: connect called with authmode %d dot11 auth %d"
1432 " PW crypto %d PW crypto len %d GRP crypto %d"
1433 " GRP crypto len %d channel hint %u\n",
1434 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301435 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1436 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301437 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001438
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301439 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301440 vif->dot11_auth_mode, vif->auth_mode,
1441 vif->prwise_crypto,
1442 vif->prwise_crypto_len,
1443 vif->grp_crypto, vif->grp_crypto_len,
1444 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301445 vif->req_bssid, vif->ch_hint,
Kalle Valobdcd8172011-07-18 00:22:30 +03001446 ar->connect_ctrl_flags);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301447 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001448
1449 return 0;
1450}
1451
1452static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1453 struct net_device *dev)
1454{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301455 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001456
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301457 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001458 return -EIO;
1459
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301460 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301461 memset(vif->ssid, 0, sizeof(vif->ssid));
1462 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001463
1464 return 0;
1465}
1466
1467static const u32 cipher_suites[] = {
1468 WLAN_CIPHER_SUITE_WEP40,
1469 WLAN_CIPHER_SUITE_WEP104,
1470 WLAN_CIPHER_SUITE_TKIP,
1471 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001472 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001473 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001474};
1475
1476static bool is_rate_legacy(s32 rate)
1477{
1478 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1479 6000, 9000, 12000, 18000, 24000,
1480 36000, 48000, 54000
1481 };
1482 u8 i;
1483
1484 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1485 if (rate == legacy[i])
1486 return true;
1487
1488 return false;
1489}
1490
1491static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1492{
1493 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1494 52000, 58500, 65000, 72200
1495 };
1496 u8 i;
1497
1498 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1499 if (rate == ht20[i]) {
1500 if (i == ARRAY_SIZE(ht20) - 1)
1501 /* last rate uses sgi */
1502 *sgi = true;
1503 else
1504 *sgi = false;
1505
1506 *mcs = i;
1507 return true;
1508 }
1509 }
1510 return false;
1511}
1512
1513static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1514{
1515 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1516 81000, 108000, 121500, 135000,
1517 150000
1518 };
1519 u8 i;
1520
1521 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1522 if (rate == ht40[i]) {
1523 if (i == ARRAY_SIZE(ht40) - 1)
1524 /* last rate uses sgi */
1525 *sgi = true;
1526 else
1527 *sgi = false;
1528
1529 *mcs = i;
1530 return true;
1531 }
1532 }
1533
1534 return false;
1535}
1536
1537static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1538 u8 *mac, struct station_info *sinfo)
1539{
1540 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301541 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001542 long left;
1543 bool sgi;
1544 s32 rate;
1545 int ret;
1546 u8 mcs;
1547
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301548 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001549 return -ENOENT;
1550
1551 if (down_interruptible(&ar->sem))
1552 return -EBUSY;
1553
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301554 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001555
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301556 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001557
1558 if (ret != 0) {
1559 up(&ar->sem);
1560 return -EIO;
1561 }
1562
1563 left = wait_event_interruptible_timeout(ar->event_wq,
1564 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301565 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001566 WMI_TIMEOUT);
1567
1568 up(&ar->sem);
1569
1570 if (left == 0)
1571 return -ETIMEDOUT;
1572 else if (left < 0)
1573 return left;
1574
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301575 if (vif->target_stats.rx_byte) {
1576 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001577 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301578 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001579 sinfo->filled |= STATION_INFO_RX_PACKETS;
1580 }
1581
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301582 if (vif->target_stats.tx_byte) {
1583 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001584 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301585 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001586 sinfo->filled |= STATION_INFO_TX_PACKETS;
1587 }
1588
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301589 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001590 sinfo->filled |= STATION_INFO_SIGNAL;
1591
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301592 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001593
1594 if (is_rate_legacy(rate)) {
1595 sinfo->txrate.legacy = rate / 100;
1596 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1597 if (sgi) {
1598 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1599 sinfo->txrate.mcs = mcs - 1;
1600 } else {
1601 sinfo->txrate.mcs = mcs;
1602 }
1603
1604 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1605 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1606 if (sgi) {
1607 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1608 sinfo->txrate.mcs = mcs - 1;
1609 } else {
1610 sinfo->txrate.mcs = mcs;
1611 }
1612
1613 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1614 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1615 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001616 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1617 "invalid rate from stats: %d\n", rate);
1618 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001619 return 0;
1620 }
1621
1622 sinfo->filled |= STATION_INFO_TX_BITRATE;
1623
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301624 if (test_bit(CONNECTED, &vif->flags) &&
1625 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301626 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001627 sinfo->filled |= STATION_INFO_BSS_PARAM;
1628 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301629 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1630 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001631 }
1632
Kalle Valobdcd8172011-07-18 00:22:30 +03001633 return 0;
1634}
1635
1636static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1637 struct cfg80211_pmksa *pmksa)
1638{
1639 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301640 struct ath6kl_vif *vif = netdev_priv(netdev);
1641
1642 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001643 pmksa->pmkid, true);
1644}
1645
1646static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1647 struct cfg80211_pmksa *pmksa)
1648{
1649 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301650 struct ath6kl_vif *vif = netdev_priv(netdev);
1651
1652 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001653 pmksa->pmkid, false);
1654}
1655
1656static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1657{
1658 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301659 struct ath6kl_vif *vif = netdev_priv(netdev);
1660
1661 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301662 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1663 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001664 return 0;
1665}
1666
Raja Mani6cb3c712011-11-07 22:52:45 +02001667static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1668{
1669 struct ath6kl_vif *vif;
1670 int ret, pos, left;
1671 u32 filter = 0;
1672 u16 i;
1673 u8 mask[WOW_MASK_SIZE];
1674
1675 vif = ath6kl_vif_first(ar);
1676 if (!vif)
1677 return -EIO;
1678
1679 if (!ath6kl_cfg80211_ready(vif))
1680 return -EIO;
1681
1682 if (!test_bit(CONNECTED, &vif->flags))
1683 return -EINVAL;
1684
1685 /* Clear existing WOW patterns */
1686 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1687 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1688 WOW_LIST_ID, i);
1689 /* Configure new WOW patterns */
1690 for (i = 0; i < wow->n_patterns; i++) {
1691
1692 /*
1693 * Convert given nl80211 specific mask value to equivalent
1694 * driver specific mask value and send it to the chip along
1695 * with patterns. For example, If the mask value defined in
1696 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1697 * then equivalent driver specific mask value is
1698 * "0xFF 0x00 0xFF 0x00".
1699 */
1700 memset(&mask, 0, sizeof(mask));
1701 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1702 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1703 mask[pos] = 0xFF;
1704 }
1705 /*
1706 * Note: Pattern's offset is not passed as part of wowlan
1707 * parameter from CFG layer. So it's always passed as ZERO
1708 * to the firmware. It means, given WOW patterns are always
1709 * matched from the first byte of received pkt in the firmware.
1710 */
1711 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1712 vif->fw_vif_idx, WOW_LIST_ID,
1713 wow->patterns[i].pattern_len,
1714 0 /* pattern offset */,
1715 wow->patterns[i].pattern, mask);
1716 if (ret)
1717 return ret;
1718 }
1719
1720 if (wow->disconnect)
1721 filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1722
1723 if (wow->magic_pkt)
1724 filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1725
1726 if (wow->gtk_rekey_failure)
1727 filter |= WOW_FILTER_OPTION_GTK_ERROR;
1728
1729 if (wow->eap_identity_req)
1730 filter |= WOW_FILTER_OPTION_EAP_REQ;
1731
1732 if (wow->four_way_handshake)
1733 filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1734
1735 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
1736 ATH6KL_WOW_MODE_ENABLE,
1737 filter,
1738 WOW_HOST_REQ_DELAY);
1739 if (ret)
1740 return ret;
1741
1742 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1743 ATH6KL_HOST_MODE_ASLEEP);
1744 if (ret)
1745 return ret;
1746
1747 if (ar->tx_pending[ar->ctrl_ep]) {
1748 left = wait_event_interruptible_timeout(ar->event_wq,
1749 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
1750 if (left == 0) {
1751 ath6kl_warn("clear wmi ctrl data timeout\n");
1752 ret = -ETIMEDOUT;
1753 } else if (left < 0) {
1754 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
1755 ret = left;
1756 }
1757 }
1758
1759 return ret;
1760}
1761
1762static int ath6kl_wow_resume(struct ath6kl *ar)
1763{
1764 struct ath6kl_vif *vif;
1765 int ret;
1766
1767 vif = ath6kl_vif_first(ar);
1768 if (!vif)
1769 return -EIO;
1770
1771 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1772 ATH6KL_HOST_MODE_AWAKE);
1773 return ret;
1774}
1775
Kalle Valo52d81a62011-11-01 08:44:21 +02001776int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02001777 enum ath6kl_cfg_suspend_mode mode,
1778 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02001779{
1780 int ret;
1781
Kalle Valo52d81a62011-11-01 08:44:21 +02001782 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02001783 case ATH6KL_CFG_SUSPEND_WOW:
1784
1785 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
1786
1787 /* Flush all non control pkts in TX path */
1788 ath6kl_tx_data_cleanup(ar);
1789
1790 ret = ath6kl_wow_suspend(ar, wow);
1791 if (ret) {
1792 ath6kl_err("wow suspend failed: %d\n", ret);
1793 return ret;
1794 }
1795 ar->state = ATH6KL_STATE_WOW;
1796 break;
1797
Kalle Valo52d81a62011-11-01 08:44:21 +02001798 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02001799
1800 ath6kl_cfg80211_stop(ar);
1801
Kalle Valo52d81a62011-11-01 08:44:21 +02001802 /* save the current power mode before enabling power save */
1803 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
1804
1805 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
1806 if (ret) {
1807 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
1808 ret);
1809 }
1810
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001811 ar->state = ATH6KL_STATE_DEEPSLEEP;
1812
Kalle Valo52d81a62011-11-01 08:44:21 +02001813 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001814
1815 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02001816
1817 ath6kl_cfg80211_stop(ar);
1818
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001819 if (ar->state == ATH6KL_STATE_OFF) {
1820 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
1821 "suspend hw off, no action for cutpower\n");
1822 break;
1823 }
1824
1825 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
1826
1827 ret = ath6kl_init_hw_stop(ar);
1828 if (ret) {
1829 ath6kl_warn("failed to stop hw during suspend: %d\n",
1830 ret);
1831 }
1832
1833 ar->state = ATH6KL_STATE_CUTPOWER;
1834
1835 break;
1836
1837 default:
1838 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001839 }
1840
1841 return 0;
1842}
1843
1844int ath6kl_cfg80211_resume(struct ath6kl *ar)
1845{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001846 int ret;
1847
1848 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02001849 case ATH6KL_STATE_WOW:
1850 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
1851
1852 ret = ath6kl_wow_resume(ar);
1853 if (ret) {
1854 ath6kl_warn("wow mode resume failed: %d\n", ret);
1855 return ret;
1856 }
1857
1858 ar->state = ATH6KL_STATE_ON;
1859 break;
1860
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001861 case ATH6KL_STATE_DEEPSLEEP:
1862 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
1863 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
1864 ar->wmi->saved_pwr_mode);
1865 if (ret) {
1866 ath6kl_warn("wmi powermode command failed during resume: %d\n",
1867 ret);
1868 }
1869 }
1870
1871 ar->state = ATH6KL_STATE_ON;
1872
1873 break;
1874
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001875 case ATH6KL_STATE_CUTPOWER:
1876 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
1877
1878 ret = ath6kl_init_hw_start(ar);
1879 if (ret) {
1880 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
1881 return ret;
1882 }
Raja Manid7c44e02011-11-07 22:52:46 +02001883 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02001884
Kalle Valo76a9fbe2011-11-01 08:44:28 +02001885 default:
1886 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02001887 }
1888
1889 return 0;
1890}
1891
Kalle Valoabcb3442011-07-22 08:26:20 +03001892#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02001893
1894/* hif layer decides what suspend mode to use */
1895static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03001896 struct cfg80211_wowlan *wow)
1897{
1898 struct ath6kl *ar = wiphy_priv(wiphy);
1899
Raja Mani0f60e9f2011-11-07 22:52:45 +02001900 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03001901}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001902
Kalle Valo52d81a62011-11-01 08:44:21 +02001903static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03001904{
1905 struct ath6kl *ar = wiphy_priv(wiphy);
1906
1907 return ath6kl_hif_resume(ar);
1908}
Raja Mania918fb32011-11-07 22:52:46 +02001909
1910/*
1911 * FIXME: WOW suspend mode is selected if the host sdio controller supports
1912 * both sdio irq wake up and keep power. The target pulls sdio data line to
1913 * wake up the host when WOW pattern matches. This causes sdio irq handler
1914 * is being called in the host side which internally hits ath6kl's RX path.
1915 *
1916 * Since sdio interrupt is not disabled, RX path executes even before
1917 * the host executes the actual resume operation from PM module.
1918 *
1919 * In the current scenario, WOW resume should happen before start processing
1920 * any data from the target. So It's required to perform WOW resume in RX path.
1921 * Ideally we should perform WOW resume only in the actual platform
1922 * resume path. This area needs bit rework to avoid WOW resume in RX path.
1923 *
1924 * ath6kl_check_wow_status() is called from ath6kl_rx().
1925 */
1926void ath6kl_check_wow_status(struct ath6kl *ar)
1927{
1928 if (ar->state == ATH6KL_STATE_WOW)
1929 ath6kl_cfg80211_resume(ar);
1930}
1931
1932#else
1933
1934void ath6kl_check_wow_status(struct ath6kl *ar)
1935{
1936}
Kalle Valoabcb3442011-07-22 08:26:20 +03001937#endif
1938
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001939static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
1940 struct ieee80211_channel *chan,
1941 enum nl80211_channel_type channel_type)
1942{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301943 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001944
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301945 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001946 return -EIO;
1947
1948 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
1949 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301950 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001951
1952 return 0;
1953}
1954
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03001955static bool ath6kl_is_p2p_ie(const u8 *pos)
1956{
1957 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
1958 pos[2] == 0x50 && pos[3] == 0x6f &&
1959 pos[4] == 0x9a && pos[5] == 0x09;
1960}
1961
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301962static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
1963 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03001964{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301965 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03001966 const u8 *pos;
1967 u8 *buf = NULL;
1968 size_t len = 0;
1969 int ret;
1970
1971 /*
1972 * Filter out P2P IE(s) since they will be included depending on
1973 * the Probe Request frame in ath6kl_send_go_probe_resp().
1974 */
1975
1976 if (ies && ies_len) {
1977 buf = kmalloc(ies_len, GFP_KERNEL);
1978 if (buf == NULL)
1979 return -ENOMEM;
1980 pos = ies;
1981 while (pos + 1 < ies + ies_len) {
1982 if (pos + 2 + pos[1] > ies + ies_len)
1983 break;
1984 if (!ath6kl_is_p2p_ie(pos)) {
1985 memcpy(buf + len, pos, 2 + pos[1]);
1986 len += 2 + pos[1];
1987 }
1988 pos += 2 + pos[1];
1989 }
1990 }
1991
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301992 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
1993 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03001994 kfree(buf);
1995 return ret;
1996}
1997
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03001998static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
1999 struct beacon_parameters *info, bool add)
2000{
2001 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302002 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002003 struct ieee80211_mgmt *mgmt;
2004 u8 *ies;
2005 int ies_len;
2006 struct wmi_connect_cmd p;
2007 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302008 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002009
2010 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
2011
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302012 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002013 return -EIO;
2014
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302015 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002016 return -EOPNOTSUPP;
2017
2018 if (info->beacon_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302019 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2020 WMI_FRAME_BEACON,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002021 info->beacon_ies,
2022 info->beacon_ies_len);
2023 if (res)
2024 return res;
2025 }
2026 if (info->proberesp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302027 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002028 info->proberesp_ies_len);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002029 if (res)
2030 return res;
2031 }
2032 if (info->assocresp_ies) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302033 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2034 WMI_FRAME_ASSOC_RESP,
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002035 info->assocresp_ies,
2036 info->assocresp_ies_len);
2037 if (res)
2038 return res;
2039 }
2040
2041 if (!add)
2042 return 0;
2043
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002044 ar->ap_mode_bkey.valid = false;
2045
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002046 /* TODO:
2047 * info->interval
2048 * info->dtim_period
2049 */
2050
2051 if (info->head == NULL)
2052 return -EINVAL;
2053 mgmt = (struct ieee80211_mgmt *) info->head;
2054 ies = mgmt->u.beacon.variable;
2055 if (ies > info->head + info->head_len)
2056 return -EINVAL;
2057 ies_len = info->head + info->head_len - ies;
2058
2059 if (info->ssid == NULL)
2060 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302061 memcpy(vif->ssid, info->ssid, info->ssid_len);
2062 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002063 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
2064 return -EOPNOTSUPP; /* TODO */
2065
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302066 ret = ath6kl_set_auth_type(vif, info->auth_type);
2067 if (ret)
2068 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002069
2070 memset(&p, 0, sizeof(p));
2071
2072 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2073 switch (info->crypto.akm_suites[i]) {
2074 case WLAN_AKM_SUITE_8021X:
2075 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2076 p.auth_mode |= WPA_AUTH;
2077 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2078 p.auth_mode |= WPA2_AUTH;
2079 break;
2080 case WLAN_AKM_SUITE_PSK:
2081 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2082 p.auth_mode |= WPA_PSK_AUTH;
2083 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2084 p.auth_mode |= WPA2_PSK_AUTH;
2085 break;
2086 }
2087 }
2088 if (p.auth_mode == 0)
2089 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302090 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002091
2092 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2093 switch (info->crypto.ciphers_pairwise[i]) {
2094 case WLAN_CIPHER_SUITE_WEP40:
2095 case WLAN_CIPHER_SUITE_WEP104:
2096 p.prwise_crypto_type |= WEP_CRYPT;
2097 break;
2098 case WLAN_CIPHER_SUITE_TKIP:
2099 p.prwise_crypto_type |= TKIP_CRYPT;
2100 break;
2101 case WLAN_CIPHER_SUITE_CCMP:
2102 p.prwise_crypto_type |= AES_CRYPT;
2103 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002104 case WLAN_CIPHER_SUITE_SMS4:
2105 p.prwise_crypto_type |= WAPI_CRYPT;
2106 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002107 }
2108 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002109 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002110 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302111 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002112 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302113 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002114
2115 switch (info->crypto.cipher_group) {
2116 case WLAN_CIPHER_SUITE_WEP40:
2117 case WLAN_CIPHER_SUITE_WEP104:
2118 p.grp_crypto_type = WEP_CRYPT;
2119 break;
2120 case WLAN_CIPHER_SUITE_TKIP:
2121 p.grp_crypto_type = TKIP_CRYPT;
2122 break;
2123 case WLAN_CIPHER_SUITE_CCMP:
2124 p.grp_crypto_type = AES_CRYPT;
2125 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002126 case WLAN_CIPHER_SUITE_SMS4:
2127 p.grp_crypto_type = WAPI_CRYPT;
2128 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002129 default:
2130 p.grp_crypto_type = NONE_CRYPT;
2131 break;
2132 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302133 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002134
2135 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302136 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002137
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302138 p.ssid_len = vif->ssid_len;
2139 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2140 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302141 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002142
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302143 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002144 if (res < 0)
2145 return res;
2146
2147 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002148}
2149
2150static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
2151 struct beacon_parameters *info)
2152{
2153 return ath6kl_ap_beacon(wiphy, dev, info, true);
2154}
2155
2156static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
2157 struct beacon_parameters *info)
2158{
2159 return ath6kl_ap_beacon(wiphy, dev, info, false);
2160}
2161
2162static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
2163{
2164 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302165 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002166
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302167 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002168 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302169 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002170 return -ENOTCONN;
2171
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302172 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302173 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002174
2175 return 0;
2176}
2177
Jouni Malinen23875132011-08-30 21:57:53 +03002178static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2179 u8 *mac, struct station_parameters *params)
2180{
2181 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302182 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002183
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302184 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002185 return -EOPNOTSUPP;
2186
2187 /* Use this only for authorizing/unauthorizing a station */
2188 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2189 return -EOPNOTSUPP;
2190
2191 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302192 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2193 WMI_AP_MLME_AUTHORIZE, mac, 0);
2194 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2195 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002196}
2197
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002198static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2199 struct net_device *dev,
2200 struct ieee80211_channel *chan,
2201 enum nl80211_channel_type channel_type,
2202 unsigned int duration,
2203 u64 *cookie)
2204{
2205 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302206 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002207 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002208
2209 /* TODO: if already pending or ongoing remain-on-channel,
2210 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002211 id = ++vif->last_roc_id;
2212 if (id == 0) {
2213 /* Do not use 0 as the cookie value */
2214 id = ++vif->last_roc_id;
2215 }
2216 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002217
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302218 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2219 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002220}
2221
2222static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2223 struct net_device *dev,
2224 u64 cookie)
2225{
2226 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302227 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002228
Jouni Malinen10522612011-10-27 16:00:13 +03002229 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002230 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002231 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002232
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302233 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002234}
2235
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302236static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2237 const u8 *buf, size_t len,
2238 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002239{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302240 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002241 const u8 *pos;
2242 u8 *p2p;
2243 int p2p_len;
2244 int ret;
2245 const struct ieee80211_mgmt *mgmt;
2246
2247 mgmt = (const struct ieee80211_mgmt *) buf;
2248
2249 /* Include P2P IE(s) from the frame generated in user space. */
2250
2251 p2p = kmalloc(len, GFP_KERNEL);
2252 if (p2p == NULL)
2253 return -ENOMEM;
2254 p2p_len = 0;
2255
2256 pos = mgmt->u.probe_resp.variable;
2257 while (pos + 1 < buf + len) {
2258 if (pos + 2 + pos[1] > buf + len)
2259 break;
2260 if (ath6kl_is_p2p_ie(pos)) {
2261 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2262 p2p_len += 2 + pos[1];
2263 }
2264 pos += 2 + pos[1];
2265 }
2266
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302267 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2268 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002269 kfree(p2p);
2270 return ret;
2271}
2272
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002273static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2274 struct ieee80211_channel *chan, bool offchan,
2275 enum nl80211_channel_type channel_type,
2276 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002277 const u8 *buf, size_t len, bool no_cck,
2278 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002279{
2280 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302281 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002282 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002283 const struct ieee80211_mgmt *mgmt;
2284
2285 mgmt = (const struct ieee80211_mgmt *) buf;
2286 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302287 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002288 ieee80211_is_probe_resp(mgmt->frame_control)) {
2289 /*
2290 * Send Probe Response frame in AP mode using a separate WMI
2291 * command to allow the target to fill in the generic IEs.
2292 */
2293 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302294 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002295 chan->center_freq);
2296 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002297
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302298 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002299 if (id == 0) {
2300 /*
2301 * 0 is a reserved value in the WMI command and shall not be
2302 * used for the command.
2303 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302304 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002305 }
2306
2307 *cookie = id;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302308 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2309 chan->center_freq, wait,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002310 buf, len);
2311}
2312
Jouni Malinenae32c302011-08-30 21:58:01 +03002313static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2314 struct net_device *dev,
2315 u16 frame_type, bool reg)
2316{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302317 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002318
2319 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2320 __func__, frame_type, reg);
2321 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2322 /*
2323 * Note: This notification callback is not allowed to sleep, so
2324 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2325 * hardcode target to report Probe Request frames all the time.
2326 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302327 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002328 }
2329}
2330
Jouni Malinenf80574a2011-08-30 21:58:04 +03002331static const struct ieee80211_txrx_stypes
2332ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2333 [NL80211_IFTYPE_STATION] = {
2334 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2335 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2336 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2337 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2338 },
2339 [NL80211_IFTYPE_P2P_CLIENT] = {
2340 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2341 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2342 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2343 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2344 },
2345 [NL80211_IFTYPE_P2P_GO] = {
2346 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2347 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2348 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2349 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2350 },
2351};
2352
Kalle Valobdcd8172011-07-18 00:22:30 +03002353static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302354 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2355 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002356 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2357 .scan = ath6kl_cfg80211_scan,
2358 .connect = ath6kl_cfg80211_connect,
2359 .disconnect = ath6kl_cfg80211_disconnect,
2360 .add_key = ath6kl_cfg80211_add_key,
2361 .get_key = ath6kl_cfg80211_get_key,
2362 .del_key = ath6kl_cfg80211_del_key,
2363 .set_default_key = ath6kl_cfg80211_set_default_key,
2364 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2365 .set_tx_power = ath6kl_cfg80211_set_txpower,
2366 .get_tx_power = ath6kl_cfg80211_get_txpower,
2367 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2368 .join_ibss = ath6kl_cfg80211_join_ibss,
2369 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2370 .get_station = ath6kl_get_station,
2371 .set_pmksa = ath6kl_set_pmksa,
2372 .del_pmksa = ath6kl_del_pmksa,
2373 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002374 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002375#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002376 .suspend = __ath6kl_cfg80211_suspend,
2377 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002378#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002379 .set_channel = ath6kl_set_channel,
2380 .add_beacon = ath6kl_add_beacon,
2381 .set_beacon = ath6kl_set_beacon,
2382 .del_beacon = ath6kl_del_beacon,
Jouni Malinen23875132011-08-30 21:57:53 +03002383 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002384 .remain_on_channel = ath6kl_remain_on_channel,
2385 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002386 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002387 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valobdcd8172011-07-18 00:22:30 +03002388};
2389
Kalle Valoec4b7f62011-11-01 08:44:04 +02002390void ath6kl_cfg80211_stop(struct ath6kl *ar)
2391{
2392 struct ath6kl_vif *vif;
2393
2394 /* FIXME: for multi vif */
2395 vif = ath6kl_vif_first(ar);
2396 if (!vif) {
2397 /* save the current power mode before enabling power save */
2398 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2399
2400 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
2401 ath6kl_warn("ath6kl_deep_sleep_enable: "
2402 "wmi_powermode_cmd failed\n");
2403 return;
2404 }
2405
2406 switch (vif->sme_state) {
2407 case SME_CONNECTING:
2408 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2409 NULL, 0,
2410 WLAN_STATUS_UNSPECIFIED_FAILURE,
2411 GFP_KERNEL);
2412 break;
2413 case SME_CONNECTED:
2414 default:
2415 /*
2416 * FIXME: oddly enough smeState is in DISCONNECTED during
2417 * suspend, why? Need to send disconnected event in that
2418 * state.
2419 */
2420 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2421 break;
2422 }
2423
2424 if (test_bit(CONNECTED, &vif->flags) ||
2425 test_bit(CONNECT_PEND, &vif->flags))
2426 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
2427
2428 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002429 clear_bit(CONNECTED, &vif->flags);
2430 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002431
2432 /* disable scanning */
2433 if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0,
2434 0, 0, 0, 0, 0, 0, 0) != 0)
2435 printk(KERN_WARNING "ath6kl: failed to disable scan "
2436 "during suspend\n");
2437
2438 ath6kl_cfg80211_scan_complete_event(vif, true);
2439}
2440
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302441struct ath6kl *ath6kl_core_alloc(struct device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +03002442{
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002443 struct ath6kl *ar;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302444 struct wiphy *wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302445 u8 ctr;
Kalle Valobdcd8172011-07-18 00:22:30 +03002446
2447 /* create a new wiphy for use with cfg80211 */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302448 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302449
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302450 if (!wiphy) {
Kalle Valobdcd8172011-07-18 00:22:30 +03002451 ath6kl_err("couldn't allocate wiphy device\n");
Kalle Valobdcd8172011-07-18 00:22:30 +03002452 return NULL;
2453 }
2454
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302455 ar = wiphy_priv(wiphy);
Vasanthakumar Thiagarajan774439a2011-11-18 10:05:26 +05302456 ar->p2p = !!ath6kl_p2p;
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302457 ar->wiphy = wiphy;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302458 ar->dev = dev;
2459
Kalle Valo71f96ee2011-11-14 19:31:30 +02002460 ar->vif_max = 1;
2461
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +05302462 ar->max_norm_iface = 1;
2463
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302464 spin_lock_init(&ar->lock);
2465 spin_lock_init(&ar->mcastpsq_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302466 spin_lock_init(&ar->list_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302467
2468 init_waitqueue_head(&ar->event_wq);
2469 sema_init(&ar->sem, 1);
2470
2471 INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302472 INIT_LIST_HEAD(&ar->vif_list);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302473
2474 clear_bit(WMI_ENABLED, &ar->flag);
2475 clear_bit(SKIP_SCAN, &ar->flag);
2476 clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
2477
2478 ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
2479 ar->listen_intvl_b = 0;
2480 ar->tx_pwr = 0;
2481
2482 ar->intra_bss = 1;
2483 memset(&ar->sc_params, 0, sizeof(ar->sc_params));
2484 ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
2485 ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
2486 ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
2487
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002488 ar->state = ATH6KL_STATE_OFF;
2489
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302490 memset((u8 *)ar->sta_list, 0,
2491 AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
2492
2493 /* Init the PS queues */
2494 for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
2495 spin_lock_init(&ar->sta_list[ctr].psq_lock);
2496 skb_queue_head_init(&ar->sta_list[ctr].psq);
2497 }
2498
2499 skb_queue_head_init(&ar->mcastpsq);
2500
2501 memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
2502
2503 return ar;
2504}
2505
2506int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
2507{
2508 struct wiphy *wiphy = ar->wiphy;
2509 int ret;
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002510
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302511 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
Jouni Malinenf80574a2011-08-30 21:58:04 +03002512
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302513 wiphy->max_remain_on_channel_duration = 5000;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002514
Kalle Valobdcd8172011-07-18 00:22:30 +03002515 /* set device pointer for wiphy */
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302516 set_wiphy_dev(wiphy, ar->dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03002517
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302518 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302519 BIT(NL80211_IFTYPE_ADHOC) |
2520 BIT(NL80211_IFTYPE_AP);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002521 if (ar->p2p) {
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302522 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302523 BIT(NL80211_IFTYPE_P2P_CLIENT);
Jouni Malinen6bbc7c32011-09-05 17:38:47 +03002524 }
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302525
Kalle Valobdcd8172011-07-18 00:22:30 +03002526 /* max num of ssids that can be probed during scanning */
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302527 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
2528 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
2529 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
2530 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
2531 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Kalle Valobdcd8172011-07-18 00:22:30 +03002532
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302533 wiphy->cipher_suites = cipher_suites;
2534 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
Kalle Valobdcd8172011-07-18 00:22:30 +03002535
Raja Manieae9e062011-11-07 22:52:46 +02002536 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
2537 WIPHY_WOWLAN_DISCONNECT |
2538 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
2539 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
2540 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
2541 WIPHY_WOWLAN_4WAY_HANDSHAKE;
2542 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
2543 wiphy->wowlan.pattern_min_len = 1;
2544 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
2545
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302546 ret = wiphy_register(wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002547 if (ret < 0) {
2548 ath6kl_err("couldn't register wiphy device\n");
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302549 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +03002550 }
2551
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302552 return 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03002553}
2554
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302555static int ath6kl_init_if_data(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03002556{
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302557 vif->aggr_cntxt = aggr_init(vif->ndev);
2558 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302559 ath6kl_err("failed to initialize aggr\n");
2560 return -ENOMEM;
2561 }
Kalle Valobdcd8172011-07-18 00:22:30 +03002562
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302563 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302564 (unsigned long) vif->ndev);
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05302565 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05302566 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302567
2568 return 0;
2569}
2570
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302571void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302572{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302573 struct ath6kl *ar = vif->ar;
2574
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05302575 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302576
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302577 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
2578
2579 if (vif->nw_type == ADHOC_NETWORK)
2580 ar->ibss_if_active = false;
2581
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302582 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302583
2584 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302585}
2586
2587struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302588 enum nl80211_iftype type, u8 fw_vif_idx,
2589 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302590{
2591 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302592 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302593
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302594 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302595 if (!ndev)
2596 return NULL;
2597
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302598 vif = netdev_priv(ndev);
2599 ndev->ieee80211_ptr = &vif->wdev;
2600 vif->wdev.wiphy = ar->wiphy;
2601 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302602 vif->ndev = ndev;
2603 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
2604 vif->wdev.netdev = ndev;
2605 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302606 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302607 vif->nw_type = vif->next_mode = nw_type;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302608
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302609 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
2610 if (fw_vif_idx != 0)
2611 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
2612 0x2;
2613
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302614 init_netdev(ndev);
2615
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05302616 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302617
2618 /* TODO: Pass interface specific pointer instead of ar */
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05302619 if (ath6kl_init_if_data(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302620 goto err;
2621
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302622 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302623 goto err;
2624
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302625 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05302626 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302627 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302628 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302629 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302630
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302631 if (type == NL80211_IFTYPE_ADHOC)
2632 ar->ibss_if_active = true;
2633
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302634 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302635 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05302636 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302637
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302638 return ndev;
2639
2640err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05302641 aggr_module_destroy(vif->aggr_cntxt);
2642 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05302643 return NULL;
2644}
2645
2646void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
2647{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05302648 wiphy_unregister(ar->wiphy);
2649 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03002650}