blob: e14d1a072cfed65d686041148bf99806199b1664 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040018#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053019#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020020#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040021
Kalle Valobdcd8172011-07-18 00:22:30 +030022#include "core.h"
23#include "cfg80211.h"
24#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030025#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030026#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030027
28#define RATETAB_ENT(_rate, _rateid, _flags) { \
29 .bitrate = (_rate), \
30 .flags = (_flags), \
31 .hw_value = (_rateid), \
32}
33
34#define CHAN2G(_channel, _freq, _flags) { \
35 .band = IEEE80211_BAND_2GHZ, \
36 .hw_value = (_channel), \
37 .center_freq = (_freq), \
38 .flags = (_flags), \
39 .max_antenna_gain = 0, \
40 .max_power = 30, \
41}
42
43#define CHAN5G(_channel, _flags) { \
44 .band = IEEE80211_BAND_5GHZ, \
45 .hw_value = (_channel), \
46 .center_freq = 5000 + (5 * (_channel)), \
47 .flags = (_flags), \
48 .max_antenna_gain = 0, \
49 .max_power = 30, \
50}
51
52static struct ieee80211_rate ath6kl_rates[] = {
53 RATETAB_ENT(10, 0x1, 0),
54 RATETAB_ENT(20, 0x2, 0),
55 RATETAB_ENT(55, 0x4, 0),
56 RATETAB_ENT(110, 0x8, 0),
57 RATETAB_ENT(60, 0x10, 0),
58 RATETAB_ENT(90, 0x20, 0),
59 RATETAB_ENT(120, 0x40, 0),
60 RATETAB_ENT(180, 0x80, 0),
61 RATETAB_ENT(240, 0x100, 0),
62 RATETAB_ENT(360, 0x200, 0),
63 RATETAB_ENT(480, 0x400, 0),
64 RATETAB_ENT(540, 0x800, 0),
65};
66
67#define ath6kl_a_rates (ath6kl_rates + 4)
68#define ath6kl_a_rates_size 8
69#define ath6kl_g_rates (ath6kl_rates + 0)
70#define ath6kl_g_rates_size 12
71
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053072#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
73 IEEE80211_HT_CAP_SGI_20 | \
74 IEEE80211_HT_CAP_SGI_40)
75
Kalle Valobdcd8172011-07-18 00:22:30 +030076static struct ieee80211_channel ath6kl_2ghz_channels[] = {
77 CHAN2G(1, 2412, 0),
78 CHAN2G(2, 2417, 0),
79 CHAN2G(3, 2422, 0),
80 CHAN2G(4, 2427, 0),
81 CHAN2G(5, 2432, 0),
82 CHAN2G(6, 2437, 0),
83 CHAN2G(7, 2442, 0),
84 CHAN2G(8, 2447, 0),
85 CHAN2G(9, 2452, 0),
86 CHAN2G(10, 2457, 0),
87 CHAN2G(11, 2462, 0),
88 CHAN2G(12, 2467, 0),
89 CHAN2G(13, 2472, 0),
90 CHAN2G(14, 2484, 0),
91};
92
93static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
94 CHAN5G(34, 0), CHAN5G(36, 0),
95 CHAN5G(38, 0), CHAN5G(40, 0),
96 CHAN5G(42, 0), CHAN5G(44, 0),
97 CHAN5G(46, 0), CHAN5G(48, 0),
98 CHAN5G(52, 0), CHAN5G(56, 0),
99 CHAN5G(60, 0), CHAN5G(64, 0),
100 CHAN5G(100, 0), CHAN5G(104, 0),
101 CHAN5G(108, 0), CHAN5G(112, 0),
102 CHAN5G(116, 0), CHAN5G(120, 0),
103 CHAN5G(124, 0), CHAN5G(128, 0),
104 CHAN5G(132, 0), CHAN5G(136, 0),
105 CHAN5G(140, 0), CHAN5G(149, 0),
106 CHAN5G(153, 0), CHAN5G(157, 0),
107 CHAN5G(161, 0), CHAN5G(165, 0),
108 CHAN5G(184, 0), CHAN5G(188, 0),
109 CHAN5G(192, 0), CHAN5G(196, 0),
110 CHAN5G(200, 0), CHAN5G(204, 0),
111 CHAN5G(208, 0), CHAN5G(212, 0),
112 CHAN5G(216, 0),
113};
114
115static struct ieee80211_supported_band ath6kl_band_2ghz = {
116 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
117 .channels = ath6kl_2ghz_channels,
118 .n_bitrates = ath6kl_g_rates_size,
119 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530120 .ht_cap.cap = ath6kl_g_htcap,
121 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300122};
123
124static struct ieee80211_supported_band ath6kl_band_5ghz = {
125 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
126 .channels = ath6kl_5ghz_a_channels,
127 .n_bitrates = ath6kl_a_rates_size,
128 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530129 .ht_cap.cap = ath6kl_g_htcap,
130 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300131};
132
Jouni Malinen837cb972011-10-11 17:31:57 +0300133#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
134
Kalle Valo10509f92011-12-13 14:52:07 +0200135/* returns true if scheduled scan was stopped */
136static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
137{
138 struct ath6kl *ar = vif->ar;
139
140 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
141 return false;
142
143 del_timer_sync(&vif->sched_scan_timer);
144
145 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
146 ATH6KL_HOST_MODE_AWAKE);
147
148 ar->state = ATH6KL_STATE_ON;
149
150 return true;
151}
152
153static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
154{
155 struct ath6kl *ar = vif->ar;
156 bool stopped;
157
158 stopped = __ath6kl_cfg80211_sscan_stop(vif);
159
160 if (!stopped)
161 return;
162
163 cfg80211_sched_scan_stopped(ar->wiphy);
164}
165
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530166static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300167 enum nl80211_wpa_versions wpa_version)
168{
169 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
170
171 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530172 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300173 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530174 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300175 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530176 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300177 } else {
178 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
179 return -ENOTSUPP;
180 }
181
182 return 0;
183}
184
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530185static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300186 enum nl80211_auth_type auth_type)
187{
Kalle Valobdcd8172011-07-18 00:22:30 +0300188 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
189
190 switch (auth_type) {
191 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530192 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300193 break;
194 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530195 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300196 break;
197 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530198 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300199 break;
200
201 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530202 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300203 break;
204
205 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900206 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300207 return -ENOTSUPP;
208 }
209
210 return 0;
211}
212
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530213static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300214{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530215 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
216 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
217 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300218
219 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
220 __func__, cipher, ucast);
221
222 switch (cipher) {
223 case 0:
224 /* our own hack to use value 0 as no crypto used */
225 *ar_cipher = NONE_CRYPT;
226 *ar_cipher_len = 0;
227 break;
228 case WLAN_CIPHER_SUITE_WEP40:
229 *ar_cipher = WEP_CRYPT;
230 *ar_cipher_len = 5;
231 break;
232 case WLAN_CIPHER_SUITE_WEP104:
233 *ar_cipher = WEP_CRYPT;
234 *ar_cipher_len = 13;
235 break;
236 case WLAN_CIPHER_SUITE_TKIP:
237 *ar_cipher = TKIP_CRYPT;
238 *ar_cipher_len = 0;
239 break;
240 case WLAN_CIPHER_SUITE_CCMP:
241 *ar_cipher = AES_CRYPT;
242 *ar_cipher_len = 0;
243 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200244 case WLAN_CIPHER_SUITE_SMS4:
245 *ar_cipher = WAPI_CRYPT;
246 *ar_cipher_len = 0;
247 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300248 default:
249 ath6kl_err("cipher 0x%x not supported\n", cipher);
250 return -ENOTSUPP;
251 }
252
253 return 0;
254}
255
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530256static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300257{
258 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
259
260 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530261 if (vif->auth_mode == WPA_AUTH)
262 vif->auth_mode = WPA_PSK_AUTH;
263 else if (vif->auth_mode == WPA2_AUTH)
264 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300265 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530266 if (vif->auth_mode == WPA_AUTH)
267 vif->auth_mode = WPA_AUTH_CCKM;
268 else if (vif->auth_mode == WPA2_AUTH)
269 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300270 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530271 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300272 }
273}
274
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530275static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300276{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530277 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530278
Kalle Valobdcd8172011-07-18 00:22:30 +0300279 if (!test_bit(WMI_READY, &ar->flag)) {
280 ath6kl_err("wmi is not ready\n");
281 return false;
282 }
283
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530284 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300285 ath6kl_err("wlan disabled\n");
286 return false;
287 }
288
289 return true;
290}
291
Kevin Fang6981ffd2011-10-07 08:51:19 +0800292static bool ath6kl_is_wpa_ie(const u8 *pos)
293{
294 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
295 pos[2] == 0x00 && pos[3] == 0x50 &&
296 pos[4] == 0xf2 && pos[5] == 0x01;
297}
298
299static bool ath6kl_is_rsn_ie(const u8 *pos)
300{
301 return pos[0] == WLAN_EID_RSN;
302}
303
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700304static bool ath6kl_is_wps_ie(const u8 *pos)
305{
306 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
307 pos[1] >= 4 &&
308 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
309 pos[5] == 0x04);
310}
311
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530312static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
313 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800314{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530315 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800316 const u8 *pos;
317 u8 *buf = NULL;
318 size_t len = 0;
319 int ret;
320
321 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700322 * Clear previously set flag
323 */
324
325 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
326
327 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800328 * Filter out RSN/WPA IE(s)
329 */
330
331 if (ies && ies_len) {
332 buf = kmalloc(ies_len, GFP_KERNEL);
333 if (buf == NULL)
334 return -ENOMEM;
335 pos = ies;
336
337 while (pos + 1 < ies + ies_len) {
338 if (pos + 2 + pos[1] > ies + ies_len)
339 break;
340 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
341 memcpy(buf + len, pos, 2 + pos[1]);
342 len += 2 + pos[1];
343 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700344
345 if (ath6kl_is_wps_ie(pos))
346 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
347
Kevin Fang6981ffd2011-10-07 08:51:19 +0800348 pos += 2 + pos[1];
349 }
350 }
351
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530352 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
353 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800354 kfree(buf);
355 return ret;
356}
357
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530358static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
359{
360 switch (type) {
361 case NL80211_IFTYPE_STATION:
362 *nw_type = INFRA_NETWORK;
363 break;
364 case NL80211_IFTYPE_ADHOC:
365 *nw_type = ADHOC_NETWORK;
366 break;
367 case NL80211_IFTYPE_AP:
368 *nw_type = AP_NETWORK;
369 break;
370 case NL80211_IFTYPE_P2P_CLIENT:
371 *nw_type = INFRA_NETWORK;
372 break;
373 case NL80211_IFTYPE_P2P_GO:
374 *nw_type = AP_NETWORK;
375 break;
376 default:
377 ath6kl_err("invalid interface type %u\n", type);
378 return -ENOTSUPP;
379 }
380
381 return 0;
382}
383
384static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
385 u8 *if_idx, u8 *nw_type)
386{
387 int i;
388
389 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
390 return false;
391
392 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200393 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530394 return false;
395
396 if (type == NL80211_IFTYPE_STATION ||
397 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200398 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530399 if ((ar->avail_idx_map >> i) & BIT(0)) {
400 *if_idx = i;
401 return true;
402 }
403 }
404 }
405
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530406 if (type == NL80211_IFTYPE_P2P_CLIENT ||
407 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200408 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530409 if ((ar->avail_idx_map >> i) & BIT(0)) {
410 *if_idx = i;
411 return true;
412 }
413 }
414 }
415
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530416 return false;
417}
418
Kalle Valobdcd8172011-07-18 00:22:30 +0300419static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
420 struct cfg80211_connect_params *sme)
421{
422 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530423 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300424 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800425 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530426 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300427
Kalle Valo10509f92011-12-13 14:52:07 +0200428 ath6kl_cfg80211_sscan_disable(vif);
429
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530430 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300431
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530432 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300433 return -EIO;
434
435 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
436 ath6kl_err("destroy in progress\n");
437 return -EBUSY;
438 }
439
440 if (test_bit(SKIP_SCAN, &ar->flag) &&
441 ((sme->channel && sme->channel->center_freq == 0) ||
442 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
443 ath6kl_err("SkipScan: channel or bssid invalid\n");
444 return -EINVAL;
445 }
446
447 if (down_interruptible(&ar->sem)) {
448 ath6kl_err("busy, couldn't get access\n");
449 return -ERESTARTSYS;
450 }
451
452 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
453 ath6kl_err("busy, destroy in progress\n");
454 up(&ar->sem);
455 return -EBUSY;
456 }
457
458 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
459 /*
460 * sleep until the command queue drains
461 */
462 wait_event_interruptible_timeout(ar->event_wq,
463 ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0,
464 WMI_TIMEOUT);
465 if (signal_pending(current)) {
466 ath6kl_err("cmd queue drain timeout\n");
467 up(&ar->sem);
468 return -EINTR;
469 }
470 }
471
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200472 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
473 if (status) {
474 up(&ar->sem);
475 return status;
476 }
477
478 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530479 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800480
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530481 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530482 vif->ssid_len == sme->ssid_len &&
483 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530484 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530485 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
486 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530487 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300488
489 up(&ar->sem);
490 if (status) {
491 ath6kl_err("wmi_reconnect_cmd failed\n");
492 return -EIO;
493 }
494 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530495 } else if (vif->ssid_len == sme->ssid_len &&
496 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530497 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300498 }
499
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530500 memset(vif->ssid, 0, sizeof(vif->ssid));
501 vif->ssid_len = sme->ssid_len;
502 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300503
504 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530505 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300506
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530507 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300508 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530509 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300510
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530511 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300512
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530513 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514 if (status) {
515 up(&ar->sem);
516 return status;
517 }
518
519 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530520 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300521 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530522 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300523
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530524 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300525
526 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530527 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300528
529 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530530 (vif->auth_mode == NONE_AUTH) &&
531 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300532 struct ath6kl_key *key = NULL;
533
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530534 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300535 ath6kl_err("key index %d out of bounds\n",
536 sme->key_idx);
537 up(&ar->sem);
538 return -ENOENT;
539 }
540
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530541 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300542 key->key_len = sme->key_len;
543 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530544 key->cipher = vif->prwise_crypto;
545 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300546
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530547 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530548 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300549 GROUP_USAGE | TX_USAGE,
550 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200551 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300552 key->key, KEY_OP_INIT_VAL, NULL,
553 NO_SYNC_WMIFLAG);
554 }
555
556 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530557 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530558 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200559 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 ath6kl_err("couldn't set bss filtering\n");
561 up(&ar->sem);
562 return -EIO;
563 }
564 }
565
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530566 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300567
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800568 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
569 nw_subtype = SUBTYPE_P2PCLIENT;
570
Kalle Valobdcd8172011-07-18 00:22:30 +0300571 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
572 "%s: connect called with authmode %d dot11 auth %d"
573 " PW crypto %d PW crypto len %d GRP crypto %d"
574 " GRP crypto len %d channel hint %u\n",
575 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530576 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
577 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530578 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300579
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530580 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530581
582 if (vif->nw_type == INFRA_NETWORK) {
583 interval = max(vif->listen_intvl_t,
584 (u16) ATH6KL_MAX_WOW_LISTEN_INTL);
585 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
586 interval,
587 0);
588 if (status) {
589 ath6kl_err("couldn't set listen intervel\n");
590 up(&ar->sem);
591 return status;
592 }
593 }
594
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530595 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530596 vif->dot11_auth_mode, vif->auth_mode,
597 vif->prwise_crypto,
598 vif->prwise_crypto_len,
599 vif->grp_crypto, vif->grp_crypto_len,
600 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530601 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800602 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300603
604 up(&ar->sem);
605
606 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530607 memset(vif->ssid, 0, sizeof(vif->ssid));
608 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300609 ath6kl_err("invalid request\n");
610 return -ENOENT;
611 } else if (status) {
612 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
613 return -EIO;
614 }
615
616 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200617 ((vif->auth_mode == WPA_PSK_AUTH) ||
618 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530619 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300620 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
621 }
622
623 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530624 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300625
626 return 0;
627}
628
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530629static struct cfg80211_bss *
630ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
631 enum network_type nw_type,
632 const u8 *bssid,
633 struct ieee80211_channel *chan,
634 const u8 *beacon_ie,
635 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300636{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530637 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300638 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530639 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300640 u8 *ie;
641
Raja Mani4eab6f42011-11-09 17:02:23 +0530642 if (nw_type & ADHOC_NETWORK) {
643 cap_mask = WLAN_CAPABILITY_IBSS;
644 cap_val = WLAN_CAPABILITY_IBSS;
645 } else {
646 cap_mask = WLAN_CAPABILITY_ESS;
647 cap_val = WLAN_CAPABILITY_ESS;
648 }
649
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530650 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530651 vif->ssid, vif->ssid_len,
652 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300653 if (bss == NULL) {
654 /*
655 * Since cfg80211 may not yet know about the BSS,
656 * generate a partial entry until the first BSS info
657 * event becomes available.
658 *
659 * Prepend SSID element since it is not included in the Beacon
660 * IEs from the target.
661 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530662 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300663 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530664 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300665 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530666 ie[1] = vif->ssid_len;
667 memcpy(ie + 2, vif->ssid, vif->ssid_len);
668 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530669 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530670 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530671 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300672 0, GFP_KERNEL);
673 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530674 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
675 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300676 kfree(ie);
677 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530678 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300679
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530680 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300681}
682
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530683void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300684 u8 *bssid, u16 listen_intvl,
685 u16 beacon_intvl,
686 enum network_type nw_type,
687 u8 beacon_ie_len, u8 assoc_req_len,
688 u8 assoc_resp_len, u8 *assoc_info)
689{
Jouni Malinen01cac472011-09-19 19:14:59 +0300690 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530691 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530692 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300693
694 /* capinfo + listen interval */
695 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
696
697 /* capinfo + status code + associd */
698 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
699
700 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
701 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
702 assoc_resp_ie_offset;
703
704 assoc_req_len -= assoc_req_ie_offset;
705 assoc_resp_len -= assoc_resp_ie_offset;
706
Jouni Malinen32c10872011-09-19 19:15:07 +0300707 /*
708 * Store Beacon interval here; DTIM period will be available only once
709 * a Beacon frame from the AP is seen.
710 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530711 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530712 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300713
Kalle Valobdcd8172011-07-18 00:22:30 +0300714 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530715 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300716 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
717 "%s: ath6k not in ibss mode\n", __func__);
718 return;
719 }
720 }
721
722 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530723 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
724 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300725 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
726 "%s: ath6k not in station mode\n", __func__);
727 return;
728 }
729 }
730
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530731 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300732
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530733 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
734 assoc_info, beacon_ie_len);
735 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530736 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300737 return;
738 }
739
Raja Mani4eab6f42011-11-09 17:02:23 +0530740 if (nw_type & ADHOC_NETWORK) {
741 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
742 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
743 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530744 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300745 return;
746 }
747
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530748 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300749 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530750 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530751 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300752 assoc_req_ie, assoc_req_len,
753 assoc_resp_ie, assoc_resp_len,
754 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530755 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530756 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300757 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530758 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
759 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300760 }
761}
762
763static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
764 struct net_device *dev, u16 reason_code)
765{
Kalle Valod6d5c062011-11-25 13:17:37 +0200766 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530767 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300768
769 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
770 reason_code);
771
Kalle Valo10509f92011-12-13 14:52:07 +0200772 ath6kl_cfg80211_sscan_disable(vif);
773
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530774 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300775 return -EIO;
776
777 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
778 ath6kl_err("busy, destroy in progress\n");
779 return -EBUSY;
780 }
781
782 if (down_interruptible(&ar->sem)) {
783 ath6kl_err("busy, couldn't get access\n");
784 return -ERESTARTSYS;
785 }
786
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530787 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530788 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530789 memset(vif->ssid, 0, sizeof(vif->ssid));
790 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300791
792 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530793 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300794
795 up(&ar->sem);
796
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530797 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530798
Kalle Valobdcd8172011-07-18 00:22:30 +0300799 return 0;
800}
801
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530802void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300803 u8 *bssid, u8 assoc_resp_len,
804 u8 *assoc_info, u16 proto_reason)
805{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530806 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530807
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530808 if (vif->scan_req) {
809 cfg80211_scan_done(vif->scan_req, true);
810 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300811 }
812
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530813 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530814 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300815 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
816 "%s: ath6k not in ibss mode\n", __func__);
817 return;
818 }
819 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530820 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300821 return;
822 }
823
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530824 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530825 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
826 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300827 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
828 "%s: ath6k not in station mode\n", __func__);
829 return;
830 }
831 }
832
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530833 /*
834 * Send a disconnect command to target when a disconnect event is
835 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
836 * request from host) to make the firmware stop trying to connect even
837 * after giving disconnect event. There will be one more disconnect
838 * event for this disconnect command with reason code DISCONNECT_CMD
839 * which will be notified to cfg80211.
840 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300841
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530842 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530843 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300844 return;
845 }
846
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530847 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300848
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530849 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530850 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200851 bssid, NULL, 0,
852 NULL, 0,
853 WLAN_STATUS_UNSPECIFIED_FAILURE,
854 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530855 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530856 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200857 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300858 }
859
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530860 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300861}
862
Kalle Valobdcd8172011-07-18 00:22:30 +0300863static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
864 struct cfg80211_scan_request *request)
865{
Kalle Valod6d5c062011-11-25 13:17:37 +0200866 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530867 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300868 s8 n_channels = 0;
869 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300870 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530871 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300872
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530873 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300874 return -EIO;
875
Kalle Valo10509f92011-12-13 14:52:07 +0200876 ath6kl_cfg80211_sscan_disable(vif);
877
Kalle Valobdcd8172011-07-18 00:22:30 +0300878 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530879 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300880 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530881 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530882 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300883 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
884 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300885 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300886 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300887 }
888 }
889
890 if (request->n_ssids && request->ssids[0].ssid_len) {
891 u8 i;
892
893 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
894 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
895
896 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530897 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
898 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300899 request->ssids[i].ssid_len,
900 request->ssids[i].ssid);
901 }
902
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800903 /* this also clears IE in fw if it's not set */
904 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
905 WMI_FRAME_PROBE_REQ,
906 request->ie, request->ie_len);
907 if (ret) {
908 ath6kl_err("failed to set Probe Request appie for "
909 "scan");
910 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300911 }
912
Jouni Malinen11869be2011-09-02 20:07:06 +0300913 /*
914 * Scan only the requested channels if the request specifies a set of
915 * channels. If the list is longer than the target supports, do not
916 * configure the list and instead, scan all available channels.
917 */
918 if (request->n_channels > 0 &&
919 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300920 u8 i;
921
Jouni Malinen11869be2011-09-02 20:07:06 +0300922 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300923
924 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
925 if (channels == NULL) {
926 ath6kl_warn("failed to set scan channels, "
927 "scan all channels");
928 n_channels = 0;
929 }
930
931 for (i = 0; i < n_channels; i++)
932 channels[i] = request->channels[i]->center_freq;
933 }
934
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530935 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530936 force_fg_scan = 1;
937
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800938 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200939 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800940 /*
941 * If capable of doing P2P mgmt operations using
942 * station interface, send additional information like
943 * supported rates to advertise and xmit rates for
944 * probe requests
945 */
946 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
947 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530948 false, 0,
949 ATH6KL_FG_SCAN_INTERVAL,
950 n_channels, channels,
951 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800952 request->rates);
953 } else {
954 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
955 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530956 false, 0,
957 ATH6KL_FG_SCAN_INTERVAL,
958 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800959 }
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300960 if (ret)
Kalle Valobdcd8172011-07-18 00:22:30 +0300961 ath6kl_err("wmi_startscan_cmd failed\n");
Jouni Malinen11869be2011-09-02 20:07:06 +0300962 else
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530963 vif->scan_req = request;
Kalle Valobdcd8172011-07-18 00:22:30 +0300964
Edward Lu1276c9e2011-08-30 21:58:00 +0300965 kfree(channels);
966
Kalle Valobdcd8172011-07-18 00:22:30 +0300967 return ret;
968}
969
Kalle Valo1c17d312011-11-01 08:43:56 +0200970void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300971{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530972 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300973 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300974
Kalle Valo1c17d312011-11-01 08:43:56 +0200975 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
976 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300977
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530978 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300979 return;
Kalle Valobdcd8172011-07-18 00:22:30 +0300980
Kalle Valo1c17d312011-11-01 08:43:56 +0200981 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300982 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300983
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530984 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
985 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530986 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
987 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300988 0, NULL);
989 }
990 }
991
992out:
Kalle Valocb938212011-10-27 18:47:46 +0300993 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530994 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300995}
996
997static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
998 u8 key_index, bool pairwise,
999 const u8 *mac_addr,
1000 struct key_params *params)
1001{
Kalle Valod6d5c062011-11-25 13:17:37 +02001002 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301003 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001004 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301005 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001006 u8 key_usage;
1007 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001008
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301009 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001010 return -EIO;
1011
Jouni Malinen837cb972011-10-11 17:31:57 +03001012 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1013 if (params->key_len != WMI_KRK_LEN)
1014 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301015 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1016 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001017 }
1018
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301019 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001020 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1021 "%s: key index %d out of bounds\n", __func__,
1022 key_index);
1023 return -ENOENT;
1024 }
1025
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301026 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001027 memset(key, 0, sizeof(struct ath6kl_key));
1028
1029 if (pairwise)
1030 key_usage = PAIRWISE_USAGE;
1031 else
1032 key_usage = GROUP_USAGE;
1033
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301034 seq_len = params->seq_len;
1035 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1036 seq_len > ATH6KL_KEY_SEQ_LEN) {
1037 /* Only first half of the WPI PN is configured */
1038 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001039 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301040 if (params->key_len > WLAN_MAX_KEY_LEN ||
1041 seq_len > sizeof(key->seq))
1042 return -EINVAL;
1043
1044 key->key_len = params->key_len;
1045 memcpy(key->key, params->key, key->key_len);
1046 key->seq_len = seq_len;
1047 memcpy(key->seq, params->seq, key->seq_len);
1048 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001049
1050 switch (key->cipher) {
1051 case WLAN_CIPHER_SUITE_WEP40:
1052 case WLAN_CIPHER_SUITE_WEP104:
1053 key_type = WEP_CRYPT;
1054 break;
1055
1056 case WLAN_CIPHER_SUITE_TKIP:
1057 key_type = TKIP_CRYPT;
1058 break;
1059
1060 case WLAN_CIPHER_SUITE_CCMP:
1061 key_type = AES_CRYPT;
1062 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001063 case WLAN_CIPHER_SUITE_SMS4:
1064 key_type = WAPI_CRYPT;
1065 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001066
1067 default:
1068 return -ENOTSUPP;
1069 }
1070
Kalle Valoddc3d772012-03-07 20:03:58 +02001071 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1072 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1073 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301074 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001075
1076 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1077 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1078 __func__, key_index, key->key_len, key_type,
1079 key_usage, key->seq_len);
1080
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301081 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001082 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301083 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001084 ar->ap_mode_bkey.valid = true;
1085 ar->ap_mode_bkey.key_index = key_index;
1086 ar->ap_mode_bkey.key_type = key_type;
1087 ar->ap_mode_bkey.key_len = key->key_len;
1088 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301089 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001090 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1091 "key configuration until AP mode has been "
1092 "started\n");
1093 /*
1094 * The key will be set in ath6kl_connect_ap_mode() once
1095 * the connected event is received from the target.
1096 */
1097 return 0;
1098 }
1099 }
1100
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301101 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301102 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001103 /*
1104 * Store the key locally so that it can be re-configured after
1105 * the AP mode has properly started
1106 * (ath6kl_install_statioc_wep_keys).
1107 */
1108 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1109 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301110 vif->wep_key_list[key_index].key_len = key->key_len;
1111 memcpy(vif->wep_key_list[key_index].key, key->key,
1112 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001113 return 0;
1114 }
1115
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301116 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001117 key_type, key_usage, key->key_len,
1118 key->seq, key->seq_len, key->key,
1119 KEY_OP_INIT_VAL,
1120 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001121}
1122
1123static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1124 u8 key_index, bool pairwise,
1125 const u8 *mac_addr)
1126{
Kalle Valod6d5c062011-11-25 13:17:37 +02001127 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301128 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001129
1130 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1131
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301132 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001133 return -EIO;
1134
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301135 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001136 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1137 "%s: key index %d out of bounds\n", __func__,
1138 key_index);
1139 return -ENOENT;
1140 }
1141
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301142 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001143 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1144 "%s: index %d is empty\n", __func__, key_index);
1145 return 0;
1146 }
1147
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301148 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001149
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301150 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001151}
1152
1153static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1154 u8 key_index, bool pairwise,
1155 const u8 *mac_addr, void *cookie,
1156 void (*callback) (void *cookie,
1157 struct key_params *))
1158{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301159 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001160 struct ath6kl_key *key = NULL;
1161 struct key_params params;
1162
1163 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1164
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301165 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001166 return -EIO;
1167
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301168 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001169 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1170 "%s: key index %d out of bounds\n", __func__,
1171 key_index);
1172 return -ENOENT;
1173 }
1174
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301175 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001176 memset(&params, 0, sizeof(params));
1177 params.cipher = key->cipher;
1178 params.key_len = key->key_len;
1179 params.seq_len = key->seq_len;
1180 params.seq = key->seq;
1181 params.key = key->key;
1182
1183 callback(cookie, &params);
1184
1185 return key->key_len ? 0 : -ENOENT;
1186}
1187
1188static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1189 struct net_device *ndev,
1190 u8 key_index, bool unicast,
1191 bool multicast)
1192{
Kalle Valod6d5c062011-11-25 13:17:37 +02001193 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301194 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001195 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001196 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001197 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001198
1199 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1200
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301201 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001202 return -EIO;
1203
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301204 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001205 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1206 "%s: key index %d out of bounds\n",
1207 __func__, key_index);
1208 return -ENOENT;
1209 }
1210
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301211 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001212 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1213 __func__, key_index);
1214 return -EINVAL;
1215 }
1216
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301217 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301218 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001219 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301220 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001221 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001222 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301223 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001224 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301225 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001226
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301227 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001228 return 0; /* Delay until AP mode has been started */
1229
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001230 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1231 vif->def_txkey_index,
1232 key_type, key_usage,
1233 key->key_len, key->seq, key->seq_len,
1234 key->key,
1235 KEY_OP_INIT_VAL, NULL,
1236 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001237}
1238
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301239void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001240 bool ismcast)
1241{
1242 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1243 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1244
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301245 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001246 (ismcast ? NL80211_KEYTYPE_GROUP :
1247 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1248 GFP_KERNEL);
1249}
1250
1251static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1252{
1253 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301254 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001255 int ret;
1256
1257 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1258 changed);
1259
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301260 vif = ath6kl_vif_first(ar);
1261 if (!vif)
1262 return -EIO;
1263
1264 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001265 return -EIO;
1266
1267 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1268 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1269 if (ret != 0) {
1270 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1271 return -EIO;
1272 }
1273 }
1274
1275 return 0;
1276}
1277
1278/*
1279 * The type nl80211_tx_power_setting replaces the following
1280 * data type from 2.6.36 onwards
1281*/
1282static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1283 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001284 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001285{
1286 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301287 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001288 u8 ath6kl_dbm;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001289 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001290
1291 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1292 type, dbm);
1293
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301294 vif = ath6kl_vif_first(ar);
1295 if (!vif)
1296 return -EIO;
1297
1298 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001299 return -EIO;
1300
1301 switch (type) {
1302 case NL80211_TX_POWER_AUTOMATIC:
1303 return 0;
1304 case NL80211_TX_POWER_LIMITED:
1305 ar->tx_pwr = ath6kl_dbm = dbm;
1306 break;
1307 default:
1308 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1309 __func__, type);
1310 return -EOPNOTSUPP;
1311 }
1312
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301313 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001314
1315 return 0;
1316}
1317
1318static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1319{
1320 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301321 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001322
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301323 vif = ath6kl_vif_first(ar);
1324 if (!vif)
1325 return -EIO;
1326
1327 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001328 return -EIO;
1329
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301330 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001331 ar->tx_pwr = 0;
1332
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301333 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001334 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1335 return -EIO;
1336 }
1337
1338 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1339 5 * HZ);
1340
1341 if (signal_pending(current)) {
1342 ath6kl_err("target did not respond\n");
1343 return -EINTR;
1344 }
1345 }
1346
1347 *dbm = ar->tx_pwr;
1348 return 0;
1349}
1350
1351static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1352 struct net_device *dev,
1353 bool pmgmt, int timeout)
1354{
1355 struct ath6kl *ar = ath6kl_priv(dev);
1356 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301357 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001358
1359 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1360 __func__, pmgmt, timeout);
1361
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301362 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001363 return -EIO;
1364
1365 if (pmgmt) {
1366 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1367 mode.pwr_mode = REC_POWER;
1368 } else {
1369 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1370 mode.pwr_mode = MAX_PERF_POWER;
1371 }
1372
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301373 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001374 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001375 ath6kl_err("wmi_powermode_cmd failed\n");
1376 return -EIO;
1377 }
1378
1379 return 0;
1380}
1381
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301382static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1383 char *name,
1384 enum nl80211_iftype type,
1385 u32 *flags,
1386 struct vif_params *params)
1387{
1388 struct ath6kl *ar = wiphy_priv(wiphy);
1389 struct net_device *ndev;
1390 u8 if_idx, nw_type;
1391
Kalle Valo71f96ee2011-11-14 19:31:30 +02001392 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301393 ath6kl_err("Reached maximum number of supported vif\n");
1394 return ERR_PTR(-EINVAL);
1395 }
1396
1397 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1398 ath6kl_err("Not a supported interface type\n");
1399 return ERR_PTR(-EINVAL);
1400 }
1401
1402 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1403 if (!ndev)
1404 return ERR_PTR(-ENOMEM);
1405
1406 ar->num_vif++;
1407
1408 return ndev;
1409}
1410
1411static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1412 struct net_device *ndev)
1413{
1414 struct ath6kl *ar = wiphy_priv(wiphy);
1415 struct ath6kl_vif *vif = netdev_priv(ndev);
1416
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301417 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301418 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301419 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301420
1421 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1422
Kalle Valoc25889e2012-01-17 20:08:27 +02001423 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301424
1425 return 0;
1426}
1427
Kalle Valobdcd8172011-07-18 00:22:30 +03001428static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1429 struct net_device *ndev,
1430 enum nl80211_iftype type, u32 *flags,
1431 struct vif_params *params)
1432{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301433 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001434
1435 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1436
Kalle Valobdcd8172011-07-18 00:22:30 +03001437 switch (type) {
1438 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301439 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001440 break;
1441 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301442 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001443 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001444 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301445 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001446 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001447 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301448 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001449 break;
1450 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301451 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001452 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001453 default:
1454 ath6kl_err("invalid interface type %u\n", type);
1455 return -EOPNOTSUPP;
1456 }
1457
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301458 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001459
1460 return 0;
1461}
1462
1463static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1464 struct net_device *dev,
1465 struct cfg80211_ibss_params *ibss_param)
1466{
1467 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301468 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001469 int status;
1470
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301471 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001472 return -EIO;
1473
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301474 vif->ssid_len = ibss_param->ssid_len;
1475 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001476
1477 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301478 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001479
1480 if (ibss_param->channel_fixed) {
1481 /*
1482 * TODO: channel_fixed: The channel should be fixed, do not
1483 * search for IBSSs to join on other channels. Target
1484 * firmware does not support this feature, needs to be
1485 * updated.
1486 */
1487 return -EOPNOTSUPP;
1488 }
1489
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301490 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001491 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301492 memcpy(vif->req_bssid, ibss_param->bssid,
1493 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001494
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301495 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001496
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301497 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001498 if (status)
1499 return status;
1500
1501 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301502 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1503 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001504 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301505 ath6kl_set_cipher(vif, 0, true);
1506 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001507 }
1508
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301509 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001510
1511 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1512 "%s: connect called with authmode %d dot11 auth %d"
1513 " PW crypto %d PW crypto len %d GRP crypto %d"
1514 " GRP crypto len %d channel hint %u\n",
1515 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301516 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1517 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301518 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001519
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301520 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301521 vif->dot11_auth_mode, vif->auth_mode,
1522 vif->prwise_crypto,
1523 vif->prwise_crypto_len,
1524 vif->grp_crypto, vif->grp_crypto_len,
1525 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301526 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001527 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301528 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001529
1530 return 0;
1531}
1532
1533static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1534 struct net_device *dev)
1535{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301536 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001537
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301538 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001539 return -EIO;
1540
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301541 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301542 memset(vif->ssid, 0, sizeof(vif->ssid));
1543 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001544
1545 return 0;
1546}
1547
1548static const u32 cipher_suites[] = {
1549 WLAN_CIPHER_SUITE_WEP40,
1550 WLAN_CIPHER_SUITE_WEP104,
1551 WLAN_CIPHER_SUITE_TKIP,
1552 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001553 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001554 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001555};
1556
1557static bool is_rate_legacy(s32 rate)
1558{
1559 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1560 6000, 9000, 12000, 18000, 24000,
1561 36000, 48000, 54000
1562 };
1563 u8 i;
1564
1565 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1566 if (rate == legacy[i])
1567 return true;
1568
1569 return false;
1570}
1571
1572static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1573{
1574 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1575 52000, 58500, 65000, 72200
1576 };
1577 u8 i;
1578
1579 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1580 if (rate == ht20[i]) {
1581 if (i == ARRAY_SIZE(ht20) - 1)
1582 /* last rate uses sgi */
1583 *sgi = true;
1584 else
1585 *sgi = false;
1586
1587 *mcs = i;
1588 return true;
1589 }
1590 }
1591 return false;
1592}
1593
1594static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1595{
1596 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1597 81000, 108000, 121500, 135000,
1598 150000
1599 };
1600 u8 i;
1601
1602 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1603 if (rate == ht40[i]) {
1604 if (i == ARRAY_SIZE(ht40) - 1)
1605 /* last rate uses sgi */
1606 *sgi = true;
1607 else
1608 *sgi = false;
1609
1610 *mcs = i;
1611 return true;
1612 }
1613 }
1614
1615 return false;
1616}
1617
1618static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1619 u8 *mac, struct station_info *sinfo)
1620{
1621 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301622 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001623 long left;
1624 bool sgi;
1625 s32 rate;
1626 int ret;
1627 u8 mcs;
1628
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301629 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001630 return -ENOENT;
1631
1632 if (down_interruptible(&ar->sem))
1633 return -EBUSY;
1634
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301635 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001636
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301637 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001638
1639 if (ret != 0) {
1640 up(&ar->sem);
1641 return -EIO;
1642 }
1643
1644 left = wait_event_interruptible_timeout(ar->event_wq,
1645 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301646 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001647 WMI_TIMEOUT);
1648
1649 up(&ar->sem);
1650
1651 if (left == 0)
1652 return -ETIMEDOUT;
1653 else if (left < 0)
1654 return left;
1655
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301656 if (vif->target_stats.rx_byte) {
1657 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001658 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301659 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001660 sinfo->filled |= STATION_INFO_RX_PACKETS;
1661 }
1662
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301663 if (vif->target_stats.tx_byte) {
1664 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001665 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301666 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001667 sinfo->filled |= STATION_INFO_TX_PACKETS;
1668 }
1669
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301670 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001671 sinfo->filled |= STATION_INFO_SIGNAL;
1672
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301673 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001674
1675 if (is_rate_legacy(rate)) {
1676 sinfo->txrate.legacy = rate / 100;
1677 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1678 if (sgi) {
1679 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1680 sinfo->txrate.mcs = mcs - 1;
1681 } else {
1682 sinfo->txrate.mcs = mcs;
1683 }
1684
1685 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1686 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1687 if (sgi) {
1688 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1689 sinfo->txrate.mcs = mcs - 1;
1690 } else {
1691 sinfo->txrate.mcs = mcs;
1692 }
1693
1694 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1695 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1696 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001697 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1698 "invalid rate from stats: %d\n", rate);
1699 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001700 return 0;
1701 }
1702
1703 sinfo->filled |= STATION_INFO_TX_BITRATE;
1704
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301705 if (test_bit(CONNECTED, &vif->flags) &&
1706 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301707 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001708 sinfo->filled |= STATION_INFO_BSS_PARAM;
1709 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301710 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1711 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001712 }
1713
Kalle Valobdcd8172011-07-18 00:22:30 +03001714 return 0;
1715}
1716
1717static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1718 struct cfg80211_pmksa *pmksa)
1719{
1720 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301721 struct ath6kl_vif *vif = netdev_priv(netdev);
1722
1723 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001724 pmksa->pmkid, true);
1725}
1726
1727static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1728 struct cfg80211_pmksa *pmksa)
1729{
1730 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301731 struct ath6kl_vif *vif = netdev_priv(netdev);
1732
1733 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001734 pmksa->pmkid, false);
1735}
1736
1737static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1738{
1739 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301740 struct ath6kl_vif *vif = netdev_priv(netdev);
1741
1742 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301743 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1744 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001745 return 0;
1746}
1747
Raja Manid91e8ee2012-01-30 17:13:10 +05301748static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1749 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001750{
Raja Manid91e8ee2012-01-30 17:13:10 +05301751 int ret, pos;
1752 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001753 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001754
Raja Manid91e8ee2012-01-30 17:13:10 +05301755 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001756 for (i = 0; i < wow->n_patterns; i++) {
1757
1758 /*
1759 * Convert given nl80211 specific mask value to equivalent
1760 * driver specific mask value and send it to the chip along
1761 * with patterns. For example, If the mask value defined in
1762 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1763 * then equivalent driver specific mask value is
1764 * "0xFF 0x00 0xFF 0x00".
1765 */
1766 memset(&mask, 0, sizeof(mask));
1767 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1768 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1769 mask[pos] = 0xFF;
1770 }
1771 /*
1772 * Note: Pattern's offset is not passed as part of wowlan
1773 * parameter from CFG layer. So it's always passed as ZERO
1774 * to the firmware. It means, given WOW patterns are always
1775 * matched from the first byte of received pkt in the firmware.
1776 */
1777 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301778 vif->fw_vif_idx, WOW_LIST_ID,
1779 wow->patterns[i].pattern_len,
1780 0 /* pattern offset */,
1781 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001782 if (ret)
1783 return ret;
1784 }
1785
Raja Manid91e8ee2012-01-30 17:13:10 +05301786 if (wow->disconnect)
1787 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1788
1789 if (wow->magic_pkt)
1790 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1791
1792 if (wow->gtk_rekey_failure)
1793 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1794
1795 if (wow->eap_identity_req)
1796 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1797
1798 if (wow->four_way_handshake)
1799 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1800
1801 return 0;
1802}
1803
1804static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1805{
1806 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1807 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1808 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1809 0x00, 0x08 };
1810 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1811 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1812 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1813 0x00, 0x7f };
1814 u8 unicst_offset = 0;
1815 static const u8 arp_pattern[] = { 0x08, 0x06 };
1816 static const u8 arp_mask[] = { 0xff, 0xff };
1817 u8 arp_offset = 20;
1818 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1819 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1820 u8 discvr_offset = 38;
1821 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1822 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1823 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1824 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1825 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1826 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1827 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1828 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1829 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1830 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1832 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1833 u8 dhcp_offset = 0;
1834 int ret;
1835
1836 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1837 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1838 vif->fw_vif_idx, WOW_LIST_ID,
1839 sizeof(unicst_pattern), unicst_offset,
1840 unicst_pattern, unicst_mask);
1841 if (ret) {
1842 ath6kl_err("failed to add WOW unicast IP pattern\n");
1843 return ret;
1844 }
1845
1846 /* Setup all ARP pkt pattern */
1847 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1848 vif->fw_vif_idx, WOW_LIST_ID,
1849 sizeof(arp_pattern), arp_offset,
1850 arp_pattern, arp_mask);
1851 if (ret) {
1852 ath6kl_err("failed to add WOW ARP pattern\n");
1853 return ret;
1854 }
1855
1856 /*
1857 * Setup multicast pattern for mDNS 224.0.0.251,
1858 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1859 */
1860 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1861 vif->fw_vif_idx, WOW_LIST_ID,
1862 sizeof(discvr_pattern), discvr_offset,
1863 discvr_pattern, discvr_mask);
1864 if (ret) {
1865 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1866 return ret;
1867 }
1868
1869 /* Setup all DHCP broadcast pkt pattern */
1870 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1871 vif->fw_vif_idx, WOW_LIST_ID,
1872 sizeof(dhcp_pattern), dhcp_offset,
1873 dhcp_pattern, dhcp_mask);
1874 if (ret) {
1875 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1876 return ret;
1877 }
1878
1879 return 0;
1880}
1881
1882static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1883{
1884 struct net_device *ndev = vif->ndev;
1885 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1886 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1887 u8 discvr_offset = 38;
1888 u8 mac_mask[ETH_ALEN];
1889 int ret;
1890
1891 /* Setup unicast pkt pattern */
1892 memset(mac_mask, 0xff, ETH_ALEN);
1893 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1894 vif->fw_vif_idx, WOW_LIST_ID,
1895 ETH_ALEN, 0, ndev->dev_addr,
1896 mac_mask);
1897 if (ret) {
1898 ath6kl_err("failed to add WOW unicast pattern\n");
1899 return ret;
1900 }
1901
1902 /*
1903 * Setup multicast pattern for mDNS 224.0.0.251,
1904 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1905 */
1906 if ((ndev->flags & IFF_ALLMULTI) ||
1907 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1908 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1909 vif->fw_vif_idx, WOW_LIST_ID,
1910 sizeof(discvr_pattern), discvr_offset,
1911 discvr_pattern, discvr_mask);
1912 if (ret) {
1913 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1914 "pattern\n");
1915 return ret;
1916 }
1917 }
1918
1919 return 0;
1920}
1921
1922static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1923{
1924 struct in_device *in_dev;
1925 struct in_ifaddr *ifa;
1926 struct ath6kl_vif *vif;
1927 int ret, left;
1928 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05301929 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05301930 u8 index = 0;
1931 __be32 ips[MAX_IP_ADDRS];
1932
1933 vif = ath6kl_vif_first(ar);
1934 if (!vif)
1935 return -EIO;
1936
1937 if (!ath6kl_cfg80211_ready(vif))
1938 return -EIO;
1939
1940 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301941 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301942
1943 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
1944 return -EINVAL;
1945
1946 /* Clear existing WOW patterns */
1947 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1948 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1949 WOW_LIST_ID, i);
1950
1951 /*
1952 * Skip the default WOW pattern configuration
1953 * if the driver receives any WOW patterns from
1954 * the user.
1955 */
1956 if (wow)
1957 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
1958 else if (vif->nw_type == AP_NETWORK)
1959 ret = ath6kl_wow_ap(ar, vif);
1960 else
1961 ret = ath6kl_wow_sta(ar, vif);
1962
1963 if (ret)
1964 return ret;
1965
Raja Mani390a8c82012-03-07 11:35:04 +05301966 netif_stop_queue(vif->ndev);
1967
Raja Manice0dc0c2012-02-20 19:08:08 +05301968 if (vif->nw_type != AP_NETWORK) {
1969 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
1970 ATH6KL_MAX_WOW_LISTEN_INTL,
1971 0);
1972 if (ret)
1973 return ret;
1974
1975 /* Set listen interval x 15 times as bmiss time */
1976 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
1977 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
1978 bmiss_time = ATH6KL_MAX_BMISS_TIME;
1979
1980 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
1981 bmiss_time, 0);
1982 if (ret)
1983 return ret;
1984
1985 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
1986 0xFFFF, 0, 0xFFFF, 0, 0, 0,
1987 0, 0, 0, 0);
1988 if (ret)
1989 return ret;
1990 }
1991
Raja Mani390a8c82012-03-07 11:35:04 +05301992 ar->state = ATH6KL_STATE_SUSPENDING;
1993
Raja Manic08631c2011-12-16 14:24:24 +05301994 /* Setup own IP addr for ARP agent. */
1995 in_dev = __in_dev_get_rtnl(vif->ndev);
1996 if (!in_dev)
1997 goto skip_arp;
1998
1999 ifa = in_dev->ifa_list;
2000 memset(&ips, 0, sizeof(ips));
2001
2002 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2003 while (index < MAX_IP_ADDRS && ifa) {
2004 ips[index] = ifa->ifa_local;
2005 ifa = ifa->ifa_next;
2006 index++;
2007 }
2008
2009 if (ifa) {
2010 ath6kl_err("total IP addr count is exceeding fw limit\n");
2011 return -EINVAL;
2012 }
2013
2014 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2015 if (ret) {
2016 ath6kl_err("fail to setup ip for arp agent\n");
2017 return ret;
2018 }
2019
2020skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002021 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2022 ATH6KL_WOW_MODE_ENABLE,
2023 filter,
2024 WOW_HOST_REQ_DELAY);
2025 if (ret)
2026 return ret;
2027
Raja Mani081c7a82012-01-30 17:13:11 +05302028 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2029
Raja Mani6cb3c712011-11-07 22:52:45 +02002030 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2031 ATH6KL_HOST_MODE_ASLEEP);
2032 if (ret)
2033 return ret;
2034
Raja Mani081c7a82012-01-30 17:13:11 +05302035 left = wait_event_interruptible_timeout(ar->event_wq,
2036 test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
2037 WMI_TIMEOUT);
2038 if (left == 0) {
2039 ath6kl_warn("timeout, didn't get host sleep cmd "
2040 "processed event\n");
2041 ret = -ETIMEDOUT;
2042 } else if (left < 0) {
2043 ath6kl_warn("error while waiting for host sleep cmd "
2044 "processed event %d\n", left);
2045 ret = left;
2046 }
2047
Raja Mani6cb3c712011-11-07 22:52:45 +02002048 if (ar->tx_pending[ar->ctrl_ep]) {
2049 left = wait_event_interruptible_timeout(ar->event_wq,
2050 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
2051 if (left == 0) {
2052 ath6kl_warn("clear wmi ctrl data timeout\n");
2053 ret = -ETIMEDOUT;
2054 } else if (left < 0) {
2055 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2056 ret = left;
2057 }
2058 }
2059
2060 return ret;
2061}
2062
2063static int ath6kl_wow_resume(struct ath6kl *ar)
2064{
2065 struct ath6kl_vif *vif;
2066 int ret;
2067
2068 vif = ath6kl_vif_first(ar);
2069 if (!vif)
2070 return -EIO;
2071
Raja Mani390a8c82012-03-07 11:35:04 +05302072 ar->state = ATH6KL_STATE_RESUMING;
2073
Raja Mani6cb3c712011-11-07 22:52:45 +02002074 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2075 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302076 if (ret) {
2077 ath6kl_warn("Failed to configure host sleep mode for "
2078 "wow resume: %d\n", ret);
2079 ar->state = ATH6KL_STATE_WOW;
2080 return ret;
2081 }
2082
Raja Manice0dc0c2012-02-20 19:08:08 +05302083 if (vif->nw_type != AP_NETWORK) {
2084 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2085 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2086 if (ret)
2087 return ret;
2088
2089 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2090 vif->listen_intvl_t, 0);
2091 if (ret)
2092 return ret;
2093
2094 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2095 vif->bmiss_time_t, 0);
2096 if (ret)
2097 return ret;
2098 }
2099
Raja Mani390a8c82012-03-07 11:35:04 +05302100 ar->state = ATH6KL_STATE_ON;
2101
2102 netif_wake_queue(vif->ndev);
2103
2104 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002105}
2106
Kalle Valo52d81a62011-11-01 08:44:21 +02002107int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002108 enum ath6kl_cfg_suspend_mode mode,
2109 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002110{
Raja Mani390a8c82012-03-07 11:35:04 +05302111 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002112 int ret;
2113
Kalle Valo52d81a62011-11-01 08:44:21 +02002114 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002115 case ATH6KL_CFG_SUSPEND_WOW:
2116
2117 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2118
2119 /* Flush all non control pkts in TX path */
2120 ath6kl_tx_data_cleanup(ar);
2121
Raja Mani390a8c82012-03-07 11:35:04 +05302122 prev_state = ar->state;
2123
Raja Manid7c44e02011-11-07 22:52:46 +02002124 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302125 if (ret) {
2126 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002127 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302128 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302129
Raja Manid7c44e02011-11-07 22:52:46 +02002130 ar->state = ATH6KL_STATE_WOW;
2131 break;
2132
Kalle Valo52d81a62011-11-01 08:44:21 +02002133 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002134
Kalle Valo7125f012011-12-13 14:51:37 +02002135 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002136
Kalle Valo52d81a62011-11-01 08:44:21 +02002137 /* save the current power mode before enabling power save */
2138 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2139
2140 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2141 if (ret) {
2142 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
2143 ret);
2144 }
2145
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002146 ar->state = ATH6KL_STATE_DEEPSLEEP;
2147
Kalle Valo52d81a62011-11-01 08:44:21 +02002148 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002149
2150 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002151
Kalle Valo7125f012011-12-13 14:51:37 +02002152 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002153
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002154 if (ar->state == ATH6KL_STATE_OFF) {
2155 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2156 "suspend hw off, no action for cutpower\n");
2157 break;
2158 }
2159
2160 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2161
2162 ret = ath6kl_init_hw_stop(ar);
2163 if (ret) {
2164 ath6kl_warn("failed to stop hw during suspend: %d\n",
2165 ret);
2166 }
2167
2168 ar->state = ATH6KL_STATE_CUTPOWER;
2169
2170 break;
2171
Kalle Valo10509f92011-12-13 14:52:07 +02002172 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2173 /*
2174 * Nothing needed for schedule scan, firmware is already in
2175 * wow mode and sleeping most of the time.
2176 */
2177 break;
2178
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002179 default:
2180 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002181 }
2182
2183 return 0;
2184}
Kalle Valod6a434d2012-01-17 20:09:36 +02002185EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002186
2187int ath6kl_cfg80211_resume(struct ath6kl *ar)
2188{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002189 int ret;
2190
2191 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002192 case ATH6KL_STATE_WOW:
2193 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2194
2195 ret = ath6kl_wow_resume(ar);
2196 if (ret) {
2197 ath6kl_warn("wow mode resume failed: %d\n", ret);
2198 return ret;
2199 }
2200
Raja Manid7c44e02011-11-07 22:52:46 +02002201 break;
2202
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002203 case ATH6KL_STATE_DEEPSLEEP:
2204 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2205 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2206 ar->wmi->saved_pwr_mode);
2207 if (ret) {
2208 ath6kl_warn("wmi powermode command failed during resume: %d\n",
2209 ret);
2210 }
2211 }
2212
2213 ar->state = ATH6KL_STATE_ON;
2214
2215 break;
2216
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002217 case ATH6KL_STATE_CUTPOWER:
2218 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2219
2220 ret = ath6kl_init_hw_start(ar);
2221 if (ret) {
2222 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2223 return ret;
2224 }
Raja Manid7c44e02011-11-07 22:52:46 +02002225 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002226
Kalle Valo10509f92011-12-13 14:52:07 +02002227 case ATH6KL_STATE_SCHED_SCAN:
2228 break;
2229
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002230 default:
2231 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002232 }
2233
2234 return 0;
2235}
Kalle Valod6a434d2012-01-17 20:09:36 +02002236EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002237
Kalle Valoabcb3442011-07-22 08:26:20 +03002238#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002239
2240/* hif layer decides what suspend mode to use */
2241static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002242 struct cfg80211_wowlan *wow)
2243{
2244 struct ath6kl *ar = wiphy_priv(wiphy);
2245
Raja Mani0f60e9f2011-11-07 22:52:45 +02002246 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002247}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002248
Kalle Valo52d81a62011-11-01 08:44:21 +02002249static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002250{
2251 struct ath6kl *ar = wiphy_priv(wiphy);
2252
2253 return ath6kl_hif_resume(ar);
2254}
Raja Mania918fb32011-11-07 22:52:46 +02002255
2256/*
2257 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2258 * both sdio irq wake up and keep power. The target pulls sdio data line to
2259 * wake up the host when WOW pattern matches. This causes sdio irq handler
2260 * is being called in the host side which internally hits ath6kl's RX path.
2261 *
2262 * Since sdio interrupt is not disabled, RX path executes even before
2263 * the host executes the actual resume operation from PM module.
2264 *
2265 * In the current scenario, WOW resume should happen before start processing
2266 * any data from the target. So It's required to perform WOW resume in RX path.
2267 * Ideally we should perform WOW resume only in the actual platform
2268 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2269 *
2270 * ath6kl_check_wow_status() is called from ath6kl_rx().
2271 */
2272void ath6kl_check_wow_status(struct ath6kl *ar)
2273{
Raja Mani390a8c82012-03-07 11:35:04 +05302274 if (ar->state == ATH6KL_STATE_SUSPENDING)
2275 return;
2276
Raja Mania918fb32011-11-07 22:52:46 +02002277 if (ar->state == ATH6KL_STATE_WOW)
2278 ath6kl_cfg80211_resume(ar);
2279}
2280
2281#else
2282
2283void ath6kl_check_wow_status(struct ath6kl *ar)
2284{
2285}
Kalle Valoabcb3442011-07-22 08:26:20 +03002286#endif
2287
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002288static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2289 struct ieee80211_channel *chan,
2290 enum nl80211_channel_type channel_type)
2291{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302292 struct ath6kl_vif *vif;
2293
2294 /*
2295 * 'dev' could be NULL if a channel change is required for the hardware
2296 * device itself, instead of a particular VIF.
2297 *
2298 * FIXME: To be handled properly when monitor mode is supported.
2299 */
2300 if (!dev)
2301 return -EBUSY;
2302
2303 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002304
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302305 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002306 return -EIO;
2307
2308 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2309 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302310 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002311
2312 return 0;
2313}
2314
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002315static bool ath6kl_is_p2p_ie(const u8 *pos)
2316{
2317 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2318 pos[2] == 0x50 && pos[3] == 0x6f &&
2319 pos[4] == 0x9a && pos[5] == 0x09;
2320}
2321
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302322static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2323 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002324{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302325 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002326 const u8 *pos;
2327 u8 *buf = NULL;
2328 size_t len = 0;
2329 int ret;
2330
2331 /*
2332 * Filter out P2P IE(s) since they will be included depending on
2333 * the Probe Request frame in ath6kl_send_go_probe_resp().
2334 */
2335
2336 if (ies && ies_len) {
2337 buf = kmalloc(ies_len, GFP_KERNEL);
2338 if (buf == NULL)
2339 return -ENOMEM;
2340 pos = ies;
2341 while (pos + 1 < ies + ies_len) {
2342 if (pos + 2 + pos[1] > ies + ies_len)
2343 break;
2344 if (!ath6kl_is_p2p_ie(pos)) {
2345 memcpy(buf + len, pos, 2 + pos[1]);
2346 len += 2 + pos[1];
2347 }
2348 pos += 2 + pos[1];
2349 }
2350 }
2351
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302352 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2353 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002354 kfree(buf);
2355 return ret;
2356}
2357
Johannes Berg88600202012-02-13 15:17:18 +01002358static int ath6kl_set_ies(struct ath6kl_vif *vif,
2359 struct cfg80211_beacon_data *info)
2360{
2361 struct ath6kl *ar = vif->ar;
2362 int res;
2363
2364 if (info->beacon_ies) {
2365 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2366 WMI_FRAME_BEACON,
2367 info->beacon_ies,
2368 info->beacon_ies_len);
2369 if (res)
2370 return res;
2371 }
2372
2373 if (info->proberesp_ies) {
2374 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2375 info->proberesp_ies_len);
2376 if (res)
2377 return res;
2378 }
2379
2380 if (info->assocresp_ies) {
2381 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2382 WMI_FRAME_ASSOC_RESP,
2383 info->assocresp_ies,
2384 info->assocresp_ies_len);
2385 if (res)
2386 return res;
2387 }
2388
2389 return 0;
2390}
2391
2392static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2393 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002394{
2395 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302396 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002397 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002398 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002399 u8 *ies;
2400 int ies_len;
2401 struct wmi_connect_cmd p;
2402 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302403 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002404
Johannes Berg88600202012-02-13 15:17:18 +01002405 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002406
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302407 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002408 return -EIO;
2409
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302410 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002411 return -EOPNOTSUPP;
2412
Johannes Berg88600202012-02-13 15:17:18 +01002413 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002414
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002415 ar->ap_mode_bkey.valid = false;
2416
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002417 /* TODO:
2418 * info->interval
2419 * info->dtim_period
2420 */
2421
Johannes Berg88600202012-02-13 15:17:18 +01002422 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002423 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002424 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002425 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002426 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002427 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002428 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002429
2430 if (info->ssid == NULL)
2431 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302432 memcpy(vif->ssid, info->ssid, info->ssid_len);
2433 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002434 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002435 hidden = true;
2436
2437 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2438 if (res)
2439 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002440
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302441 ret = ath6kl_set_auth_type(vif, info->auth_type);
2442 if (ret)
2443 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002444
2445 memset(&p, 0, sizeof(p));
2446
2447 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2448 switch (info->crypto.akm_suites[i]) {
2449 case WLAN_AKM_SUITE_8021X:
2450 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2451 p.auth_mode |= WPA_AUTH;
2452 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2453 p.auth_mode |= WPA2_AUTH;
2454 break;
2455 case WLAN_AKM_SUITE_PSK:
2456 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2457 p.auth_mode |= WPA_PSK_AUTH;
2458 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2459 p.auth_mode |= WPA2_PSK_AUTH;
2460 break;
2461 }
2462 }
2463 if (p.auth_mode == 0)
2464 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302465 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002466
2467 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2468 switch (info->crypto.ciphers_pairwise[i]) {
2469 case WLAN_CIPHER_SUITE_WEP40:
2470 case WLAN_CIPHER_SUITE_WEP104:
2471 p.prwise_crypto_type |= WEP_CRYPT;
2472 break;
2473 case WLAN_CIPHER_SUITE_TKIP:
2474 p.prwise_crypto_type |= TKIP_CRYPT;
2475 break;
2476 case WLAN_CIPHER_SUITE_CCMP:
2477 p.prwise_crypto_type |= AES_CRYPT;
2478 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002479 case WLAN_CIPHER_SUITE_SMS4:
2480 p.prwise_crypto_type |= WAPI_CRYPT;
2481 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002482 }
2483 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002484 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002485 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302486 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002487 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302488 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002489
2490 switch (info->crypto.cipher_group) {
2491 case WLAN_CIPHER_SUITE_WEP40:
2492 case WLAN_CIPHER_SUITE_WEP104:
2493 p.grp_crypto_type = WEP_CRYPT;
2494 break;
2495 case WLAN_CIPHER_SUITE_TKIP:
2496 p.grp_crypto_type = TKIP_CRYPT;
2497 break;
2498 case WLAN_CIPHER_SUITE_CCMP:
2499 p.grp_crypto_type = AES_CRYPT;
2500 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002501 case WLAN_CIPHER_SUITE_SMS4:
2502 p.grp_crypto_type = WAPI_CRYPT;
2503 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002504 default:
2505 p.grp_crypto_type = NONE_CRYPT;
2506 break;
2507 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302508 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002509
2510 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302511 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002512
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302513 p.ssid_len = vif->ssid_len;
2514 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2515 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302516 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002517
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302518 /* Enable uAPSD support by default */
2519 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2520 if (res < 0)
2521 return res;
2522
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002523 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2524 p.nw_subtype = SUBTYPE_P2PGO;
2525 } else {
2526 /*
2527 * Due to firmware limitation, it is not possible to
2528 * do P2P mgmt operations in AP mode
2529 */
2530 p.nw_subtype = SUBTYPE_NONE;
2531 }
2532
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302533 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002534 if (res < 0)
2535 return res;
2536
2537 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002538}
2539
Johannes Berg88600202012-02-13 15:17:18 +01002540static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2541 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002542{
Johannes Berg88600202012-02-13 15:17:18 +01002543 struct ath6kl_vif *vif = netdev_priv(dev);
2544
2545 if (!ath6kl_cfg80211_ready(vif))
2546 return -EIO;
2547
2548 if (vif->next_mode != AP_NETWORK)
2549 return -EOPNOTSUPP;
2550
2551 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002552}
2553
Johannes Berg88600202012-02-13 15:17:18 +01002554static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002555{
2556 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302557 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002558
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302559 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002560 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302561 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002562 return -ENOTCONN;
2563
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302564 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302565 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002566
2567 return 0;
2568}
2569
Jouni Malinen33e53082011-12-27 11:02:56 +02002570static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2571
2572static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2573 u8 *mac)
2574{
2575 struct ath6kl *ar = ath6kl_priv(dev);
2576 struct ath6kl_vif *vif = netdev_priv(dev);
2577 const u8 *addr = mac ? mac : bcast_addr;
2578
2579 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2580 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2581}
2582
Jouni Malinen23875132011-08-30 21:57:53 +03002583static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2584 u8 *mac, struct station_parameters *params)
2585{
2586 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302587 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002588
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302589 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002590 return -EOPNOTSUPP;
2591
2592 /* Use this only for authorizing/unauthorizing a station */
2593 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2594 return -EOPNOTSUPP;
2595
2596 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302597 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2598 WMI_AP_MLME_AUTHORIZE, mac, 0);
2599 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2600 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002601}
2602
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002603static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2604 struct net_device *dev,
2605 struct ieee80211_channel *chan,
2606 enum nl80211_channel_type channel_type,
2607 unsigned int duration,
2608 u64 *cookie)
2609{
2610 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302611 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002612 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002613
2614 /* TODO: if already pending or ongoing remain-on-channel,
2615 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002616 id = ++vif->last_roc_id;
2617 if (id == 0) {
2618 /* Do not use 0 as the cookie value */
2619 id = ++vif->last_roc_id;
2620 }
2621 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002622
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302623 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2624 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002625}
2626
2627static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2628 struct net_device *dev,
2629 u64 cookie)
2630{
2631 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302632 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002633
Jouni Malinen10522612011-10-27 16:00:13 +03002634 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002635 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002636 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002637
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302638 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002639}
2640
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302641static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2642 const u8 *buf, size_t len,
2643 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002644{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302645 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002646 const u8 *pos;
2647 u8 *p2p;
2648 int p2p_len;
2649 int ret;
2650 const struct ieee80211_mgmt *mgmt;
2651
2652 mgmt = (const struct ieee80211_mgmt *) buf;
2653
2654 /* Include P2P IE(s) from the frame generated in user space. */
2655
2656 p2p = kmalloc(len, GFP_KERNEL);
2657 if (p2p == NULL)
2658 return -ENOMEM;
2659 p2p_len = 0;
2660
2661 pos = mgmt->u.probe_resp.variable;
2662 while (pos + 1 < buf + len) {
2663 if (pos + 2 + pos[1] > buf + len)
2664 break;
2665 if (ath6kl_is_p2p_ie(pos)) {
2666 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2667 p2p_len += 2 + pos[1];
2668 }
2669 pos += 2 + pos[1];
2670 }
2671
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302672 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2673 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002674 kfree(p2p);
2675 return ret;
2676}
2677
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002678static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2679 u32 id,
2680 u32 freq,
2681 u32 wait,
2682 const u8 *buf,
2683 size_t len,
2684 bool *more_data,
2685 bool no_cck)
2686{
2687 struct ieee80211_mgmt *mgmt;
2688 struct ath6kl_sta *conn;
2689 bool is_psq_empty = false;
2690 struct ath6kl_mgmt_buff *mgmt_buf;
2691 size_t mgmt_buf_size;
2692 struct ath6kl *ar = vif->ar;
2693
2694 mgmt = (struct ieee80211_mgmt *) buf;
2695 if (is_multicast_ether_addr(mgmt->da))
2696 return false;
2697
2698 conn = ath6kl_find_sta(vif, mgmt->da);
2699 if (!conn)
2700 return false;
2701
2702 if (conn->sta_flags & STA_PS_SLEEP) {
2703 if (!(conn->sta_flags & STA_PS_POLLED)) {
2704 /* Queue the frames if the STA is sleeping */
2705 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2706 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2707 if (!mgmt_buf)
2708 return false;
2709
2710 INIT_LIST_HEAD(&mgmt_buf->list);
2711 mgmt_buf->id = id;
2712 mgmt_buf->freq = freq;
2713 mgmt_buf->wait = wait;
2714 mgmt_buf->len = len;
2715 mgmt_buf->no_cck = no_cck;
2716 memcpy(mgmt_buf->buf, buf, len);
2717 spin_lock_bh(&conn->psq_lock);
2718 is_psq_empty = skb_queue_empty(&conn->psq) &&
2719 (conn->mgmt_psq_len == 0);
2720 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2721 conn->mgmt_psq_len++;
2722 spin_unlock_bh(&conn->psq_lock);
2723
2724 /*
2725 * If this is the first pkt getting queued
2726 * for this STA, update the PVB for this
2727 * STA.
2728 */
2729 if (is_psq_empty)
2730 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2731 conn->aid, 1);
2732 return true;
2733 }
2734
2735 /*
2736 * This tx is because of a PsPoll.
2737 * Determine if MoreData bit has to be set.
2738 */
2739 spin_lock_bh(&conn->psq_lock);
2740 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2741 *more_data = true;
2742 spin_unlock_bh(&conn->psq_lock);
2743 }
2744
2745 return false;
2746}
2747
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002748static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2749 struct ieee80211_channel *chan, bool offchan,
2750 enum nl80211_channel_type channel_type,
2751 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002752 const u8 *buf, size_t len, bool no_cck,
2753 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002754{
2755 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302756 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002757 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002758 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002759 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002760
2761 mgmt = (const struct ieee80211_mgmt *) buf;
2762 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302763 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002764 ieee80211_is_probe_resp(mgmt->frame_control)) {
2765 /*
2766 * Send Probe Response frame in AP mode using a separate WMI
2767 * command to allow the target to fill in the generic IEs.
2768 */
2769 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302770 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002771 chan->center_freq);
2772 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002773
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302774 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002775 if (id == 0) {
2776 /*
2777 * 0 is a reserved value in the WMI command and shall not be
2778 * used for the command.
2779 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302780 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002781 }
2782
2783 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002784
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002785 /* AP mode Power saving processing */
2786 if (vif->nw_type == AP_NETWORK) {
2787 queued = ath6kl_mgmt_powersave_ap(vif,
2788 id, chan->center_freq,
2789 wait, buf,
2790 len, &more_data, no_cck);
2791 if (queued)
2792 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002793 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002794
2795 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2796 chan->center_freq, wait,
2797 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002798}
2799
Jouni Malinenae32c302011-08-30 21:58:01 +03002800static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2801 struct net_device *dev,
2802 u16 frame_type, bool reg)
2803{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302804 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002805
2806 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2807 __func__, frame_type, reg);
2808 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2809 /*
2810 * Note: This notification callback is not allowed to sleep, so
2811 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2812 * hardcode target to report Probe Request frames all the time.
2813 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302814 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002815 }
2816}
2817
Kalle Valo10509f92011-12-13 14:52:07 +02002818static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2819 struct net_device *dev,
2820 struct cfg80211_sched_scan_request *request)
2821{
2822 struct ath6kl *ar = ath6kl_priv(dev);
2823 struct ath6kl_vif *vif = netdev_priv(dev);
2824 u16 interval;
2825 int ret;
2826 u8 i;
2827
2828 if (ar->state != ATH6KL_STATE_ON)
2829 return -EIO;
2830
2831 if (vif->sme_state != SME_DISCONNECTED)
2832 return -EBUSY;
2833
2834 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2835 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2836 i, DISABLE_SSID_FLAG,
2837 0, NULL);
2838 }
2839
2840 /* fw uses seconds, also make sure that it's >0 */
2841 interval = max_t(u16, 1, request->interval / 1000);
2842
2843 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2844 interval, interval,
2845 10, 0, 0, 0, 3, 0, 0, 0);
2846
2847 if (request->n_ssids && request->ssids[0].ssid_len) {
2848 for (i = 0; i < request->n_ssids; i++) {
2849 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2850 i, SPECIFIC_SSID_FLAG,
2851 request->ssids[i].ssid_len,
2852 request->ssids[i].ssid);
2853 }
2854 }
2855
2856 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2857 ATH6KL_WOW_MODE_ENABLE,
2858 WOW_FILTER_SSID,
2859 WOW_HOST_REQ_DELAY);
2860 if (ret) {
2861 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2862 return ret;
2863 }
2864
2865 /* this also clears IE in fw if it's not set */
2866 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2867 WMI_FRAME_PROBE_REQ,
2868 request->ie, request->ie_len);
2869 if (ret) {
2870 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2871 ret);
2872 return ret;
2873 }
2874
2875 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2876 ATH6KL_HOST_MODE_ASLEEP);
2877 if (ret) {
2878 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2879 ret);
2880 return ret;
2881 }
2882
2883 ar->state = ATH6KL_STATE_SCHED_SCAN;
2884
2885 return ret;
2886}
2887
2888static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2889 struct net_device *dev)
2890{
2891 struct ath6kl_vif *vif = netdev_priv(dev);
2892 bool stopped;
2893
2894 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2895
2896 if (!stopped)
2897 return -EIO;
2898
2899 return 0;
2900}
2901
Jouni Malinenf80574a2011-08-30 21:58:04 +03002902static const struct ieee80211_txrx_stypes
2903ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2904 [NL80211_IFTYPE_STATION] = {
2905 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2906 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2907 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2908 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2909 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002910 [NL80211_IFTYPE_AP] = {
2911 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2912 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2913 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2914 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2915 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002916 [NL80211_IFTYPE_P2P_CLIENT] = {
2917 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2918 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2919 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2920 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2921 },
2922 [NL80211_IFTYPE_P2P_GO] = {
2923 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2924 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2925 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2926 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2927 },
2928};
2929
Kalle Valobdcd8172011-07-18 00:22:30 +03002930static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302931 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2932 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002933 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2934 .scan = ath6kl_cfg80211_scan,
2935 .connect = ath6kl_cfg80211_connect,
2936 .disconnect = ath6kl_cfg80211_disconnect,
2937 .add_key = ath6kl_cfg80211_add_key,
2938 .get_key = ath6kl_cfg80211_get_key,
2939 .del_key = ath6kl_cfg80211_del_key,
2940 .set_default_key = ath6kl_cfg80211_set_default_key,
2941 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2942 .set_tx_power = ath6kl_cfg80211_set_txpower,
2943 .get_tx_power = ath6kl_cfg80211_get_txpower,
2944 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2945 .join_ibss = ath6kl_cfg80211_join_ibss,
2946 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2947 .get_station = ath6kl_get_station,
2948 .set_pmksa = ath6kl_set_pmksa,
2949 .del_pmksa = ath6kl_del_pmksa,
2950 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002951 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002952#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002953 .suspend = __ath6kl_cfg80211_suspend,
2954 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002955#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002956 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01002957 .start_ap = ath6kl_start_ap,
2958 .change_beacon = ath6kl_change_beacon,
2959 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02002960 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002961 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002962 .remain_on_channel = ath6kl_remain_on_channel,
2963 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002964 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002965 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002966 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2967 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002968};
2969
Kalle Valo7125f012011-12-13 14:51:37 +02002970void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002971{
Kalle Valo10509f92011-12-13 14:52:07 +02002972 ath6kl_cfg80211_sscan_disable(vif);
2973
Kalle Valoec4b7f62011-11-01 08:44:04 +02002974 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002975 case SME_DISCONNECTED:
2976 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002977 case SME_CONNECTING:
2978 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2979 NULL, 0,
2980 WLAN_STATUS_UNSPECIFIED_FAILURE,
2981 GFP_KERNEL);
2982 break;
2983 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002984 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2985 break;
2986 }
2987
2988 if (test_bit(CONNECTED, &vif->flags) ||
2989 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002990 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002991
2992 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002993 clear_bit(CONNECTED, &vif->flags);
2994 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002995
2996 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002997 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2998 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2999 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003000
3001 ath6kl_cfg80211_scan_complete_event(vif, true);
3002}
3003
Kalle Valo7125f012011-12-13 14:51:37 +02003004void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3005{
3006 struct ath6kl_vif *vif;
3007
3008 vif = ath6kl_vif_first(ar);
3009 if (!vif) {
3010 /* save the current power mode before enabling power save */
3011 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3012
3013 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3014 ath6kl_warn("ath6kl_deep_sleep_enable: "
3015 "wmi_powermode_cmd failed\n");
3016 return;
3017 }
3018
3019 /*
3020 * FIXME: we should take ar->list_lock to protect changes in the
3021 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3022 * sleeps.
3023 */
3024 list_for_each_entry(vif, &ar->vif_list, list)
3025 ath6kl_cfg80211_stop(vif);
3026}
3027
Kalle Valoc25889e2012-01-17 20:08:27 +02003028static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003029{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303030 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303031 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303032 ath6kl_err("failed to initialize aggr\n");
3033 return -ENOMEM;
3034 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003035
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303036 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303037 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003038 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3039 (unsigned long) vif);
3040
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303041 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303042 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303043
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303044 INIT_LIST_HEAD(&vif->mc_filter);
3045
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303046 return 0;
3047}
3048
Kalle Valoc25889e2012-01-17 20:08:27 +02003049void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303050{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303051 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303052 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303053
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303054 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303055
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303056 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3057
3058 if (vif->nw_type == ADHOC_NETWORK)
3059 ar->ibss_if_active = false;
3060
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303061 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3062 list_del(&mc_filter->list);
3063 kfree(mc_filter);
3064 }
3065
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303066 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303067
3068 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303069}
3070
3071struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303072 enum nl80211_iftype type, u8 fw_vif_idx,
3073 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303074{
3075 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303076 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303077
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303078 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303079 if (!ndev)
3080 return NULL;
3081
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303082 vif = netdev_priv(ndev);
3083 ndev->ieee80211_ptr = &vif->wdev;
3084 vif->wdev.wiphy = ar->wiphy;
3085 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303086 vif->ndev = ndev;
3087 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3088 vif->wdev.netdev = ndev;
3089 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303090 vif->fw_vif_idx = fw_vif_idx;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303091 vif->nw_type = vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303092 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303093 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303094
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303095 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3096 if (fw_vif_idx != 0)
3097 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3098 0x2;
3099
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303100 init_netdev(ndev);
3101
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303102 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303103
Kalle Valoc25889e2012-01-17 20:08:27 +02003104 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303105 goto err;
3106
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303107 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303108 goto err;
3109
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303110 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303111 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303112 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303113 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303114 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303115
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303116 if (type == NL80211_IFTYPE_ADHOC)
3117 ar->ibss_if_active = true;
3118
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303119 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303120 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303121 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303122
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303123 return ndev;
3124
3125err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303126 aggr_module_destroy(vif->aggr_cntxt);
3127 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303128 return NULL;
3129}
3130
Kalle Valo46d33a22012-01-17 20:08:40 +02003131int ath6kl_cfg80211_init(struct ath6kl *ar)
3132{
3133 struct wiphy *wiphy = ar->wiphy;
3134 int ret;
3135
3136 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3137
3138 wiphy->max_remain_on_channel_duration = 5000;
3139
3140 /* set device pointer for wiphy */
3141 set_wiphy_dev(wiphy, ar->dev);
3142
3143 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3144 BIT(NL80211_IFTYPE_ADHOC) |
3145 BIT(NL80211_IFTYPE_AP);
3146 if (ar->p2p) {
3147 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3148 BIT(NL80211_IFTYPE_P2P_CLIENT);
3149 }
3150
3151 /* max num of ssids that can be probed during scanning */
3152 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3153 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3154 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3155 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3156 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3157
3158 wiphy->cipher_suites = cipher_suites;
3159 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3160
3161 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3162 WIPHY_WOWLAN_DISCONNECT |
3163 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3164 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3165 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3166 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3167 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3168 wiphy->wowlan.pattern_min_len = 1;
3169 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3170
3171 wiphy->max_sched_scan_ssids = 10;
3172
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303173 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3174 WIPHY_FLAG_HAVE_AP_SME |
3175 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3176 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3177
3178 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3179 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3180
3181 ar->wiphy->probe_resp_offload =
3182 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3183 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3184 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3185 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3186
Kalle Valo46d33a22012-01-17 20:08:40 +02003187 ret = wiphy_register(wiphy);
3188 if (ret < 0) {
3189 ath6kl_err("couldn't register wiphy device\n");
3190 return ret;
3191 }
3192
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303193 ar->wiphy_registered = true;
3194
Kalle Valo46d33a22012-01-17 20:08:40 +02003195 return 0;
3196}
3197
3198void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303199{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303200 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303201
3202 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003203}
Kalle Valo46d33a22012-01-17 20:08:40 +02003204
Kalle Valo45eaa782012-01-17 20:09:05 +02003205struct ath6kl *ath6kl_cfg80211_create(void)
3206{
3207 struct ath6kl *ar;
3208 struct wiphy *wiphy;
3209
3210 /* create a new wiphy for use with cfg80211 */
3211 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3212
3213 if (!wiphy) {
3214 ath6kl_err("couldn't allocate wiphy device\n");
3215 return NULL;
3216 }
3217
3218 ar = wiphy_priv(wiphy);
3219 ar->wiphy = wiphy;
3220
3221 return ar;
3222}
3223
3224/* Note: ar variable must not be accessed after calling this! */
3225void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3226{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303227 int i;
3228
3229 for (i = 0; i < AP_MAX_NUM_STA; i++)
3230 kfree(ar->sta_list[i].aggr_conn);
3231
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303232 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003233}
Kalle Valo45eaa782012-01-17 20:09:05 +02003234