blob: 9b87e6bf6c27d1762a0c37c182bb12c769dd3306 [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;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001288 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001289
1290 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1291 type, dbm);
1292
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301293 vif = ath6kl_vif_first(ar);
1294 if (!vif)
1295 return -EIO;
1296
1297 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001298 return -EIO;
1299
1300 switch (type) {
1301 case NL80211_TX_POWER_AUTOMATIC:
1302 return 0;
1303 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001304 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001305 break;
1306 default:
1307 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1308 __func__, type);
1309 return -EOPNOTSUPP;
1310 }
1311
Kalle Valod0d670a2012-03-07 20:03:58 +02001312 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001313
1314 return 0;
1315}
1316
1317static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1318{
1319 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301320 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001321
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301322 vif = ath6kl_vif_first(ar);
1323 if (!vif)
1324 return -EIO;
1325
1326 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001327 return -EIO;
1328
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301329 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001330 ar->tx_pwr = 0;
1331
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301332 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001333 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1334 return -EIO;
1335 }
1336
1337 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1338 5 * HZ);
1339
1340 if (signal_pending(current)) {
1341 ath6kl_err("target did not respond\n");
1342 return -EINTR;
1343 }
1344 }
1345
1346 *dbm = ar->tx_pwr;
1347 return 0;
1348}
1349
1350static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1351 struct net_device *dev,
1352 bool pmgmt, int timeout)
1353{
1354 struct ath6kl *ar = ath6kl_priv(dev);
1355 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301356 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001357
1358 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1359 __func__, pmgmt, timeout);
1360
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301361 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001362 return -EIO;
1363
1364 if (pmgmt) {
1365 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1366 mode.pwr_mode = REC_POWER;
1367 } else {
1368 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1369 mode.pwr_mode = MAX_PERF_POWER;
1370 }
1371
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301372 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001373 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001374 ath6kl_err("wmi_powermode_cmd failed\n");
1375 return -EIO;
1376 }
1377
1378 return 0;
1379}
1380
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301381static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1382 char *name,
1383 enum nl80211_iftype type,
1384 u32 *flags,
1385 struct vif_params *params)
1386{
1387 struct ath6kl *ar = wiphy_priv(wiphy);
1388 struct net_device *ndev;
1389 u8 if_idx, nw_type;
1390
Kalle Valo71f96ee2011-11-14 19:31:30 +02001391 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301392 ath6kl_err("Reached maximum number of supported vif\n");
1393 return ERR_PTR(-EINVAL);
1394 }
1395
1396 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1397 ath6kl_err("Not a supported interface type\n");
1398 return ERR_PTR(-EINVAL);
1399 }
1400
1401 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1402 if (!ndev)
1403 return ERR_PTR(-ENOMEM);
1404
1405 ar->num_vif++;
1406
1407 return ndev;
1408}
1409
1410static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1411 struct net_device *ndev)
1412{
1413 struct ath6kl *ar = wiphy_priv(wiphy);
1414 struct ath6kl_vif *vif = netdev_priv(ndev);
1415
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301416 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301417 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301418 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301419
1420 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1421
Kalle Valoc25889e2012-01-17 20:08:27 +02001422 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301423
1424 return 0;
1425}
1426
Kalle Valobdcd8172011-07-18 00:22:30 +03001427static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1428 struct net_device *ndev,
1429 enum nl80211_iftype type, u32 *flags,
1430 struct vif_params *params)
1431{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301432 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001433
1434 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1435
Kalle Valobdcd8172011-07-18 00:22:30 +03001436 switch (type) {
1437 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301438 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001439 break;
1440 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301441 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001442 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001443 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301444 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001445 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001446 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301447 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001448 break;
1449 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301450 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001451 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001452 default:
1453 ath6kl_err("invalid interface type %u\n", type);
1454 return -EOPNOTSUPP;
1455 }
1456
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301457 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001458
1459 return 0;
1460}
1461
1462static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1463 struct net_device *dev,
1464 struct cfg80211_ibss_params *ibss_param)
1465{
1466 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301467 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001468 int status;
1469
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301470 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001471 return -EIO;
1472
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301473 vif->ssid_len = ibss_param->ssid_len;
1474 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001475
1476 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301477 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001478
1479 if (ibss_param->channel_fixed) {
1480 /*
1481 * TODO: channel_fixed: The channel should be fixed, do not
1482 * search for IBSSs to join on other channels. Target
1483 * firmware does not support this feature, needs to be
1484 * updated.
1485 */
1486 return -EOPNOTSUPP;
1487 }
1488
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301489 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001490 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301491 memcpy(vif->req_bssid, ibss_param->bssid,
1492 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001493
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301494 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001495
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301496 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001497 if (status)
1498 return status;
1499
1500 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301501 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1502 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001503 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301504 ath6kl_set_cipher(vif, 0, true);
1505 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001506 }
1507
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301508 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001509
1510 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1511 "%s: connect called with authmode %d dot11 auth %d"
1512 " PW crypto %d PW crypto len %d GRP crypto %d"
1513 " GRP crypto len %d channel hint %u\n",
1514 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301515 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1516 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301517 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001518
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301519 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301520 vif->dot11_auth_mode, vif->auth_mode,
1521 vif->prwise_crypto,
1522 vif->prwise_crypto_len,
1523 vif->grp_crypto, vif->grp_crypto_len,
1524 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301525 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001526 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301527 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001528
1529 return 0;
1530}
1531
1532static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1533 struct net_device *dev)
1534{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301535 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001536
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301537 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001538 return -EIO;
1539
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301540 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301541 memset(vif->ssid, 0, sizeof(vif->ssid));
1542 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001543
1544 return 0;
1545}
1546
1547static const u32 cipher_suites[] = {
1548 WLAN_CIPHER_SUITE_WEP40,
1549 WLAN_CIPHER_SUITE_WEP104,
1550 WLAN_CIPHER_SUITE_TKIP,
1551 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001552 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001553 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001554};
1555
1556static bool is_rate_legacy(s32 rate)
1557{
1558 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1559 6000, 9000, 12000, 18000, 24000,
1560 36000, 48000, 54000
1561 };
1562 u8 i;
1563
1564 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1565 if (rate == legacy[i])
1566 return true;
1567
1568 return false;
1569}
1570
1571static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1572{
1573 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1574 52000, 58500, 65000, 72200
1575 };
1576 u8 i;
1577
1578 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1579 if (rate == ht20[i]) {
1580 if (i == ARRAY_SIZE(ht20) - 1)
1581 /* last rate uses sgi */
1582 *sgi = true;
1583 else
1584 *sgi = false;
1585
1586 *mcs = i;
1587 return true;
1588 }
1589 }
1590 return false;
1591}
1592
1593static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1594{
1595 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1596 81000, 108000, 121500, 135000,
1597 150000
1598 };
1599 u8 i;
1600
1601 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1602 if (rate == ht40[i]) {
1603 if (i == ARRAY_SIZE(ht40) - 1)
1604 /* last rate uses sgi */
1605 *sgi = true;
1606 else
1607 *sgi = false;
1608
1609 *mcs = i;
1610 return true;
1611 }
1612 }
1613
1614 return false;
1615}
1616
1617static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1618 u8 *mac, struct station_info *sinfo)
1619{
1620 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301621 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001622 long left;
1623 bool sgi;
1624 s32 rate;
1625 int ret;
1626 u8 mcs;
1627
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301628 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001629 return -ENOENT;
1630
1631 if (down_interruptible(&ar->sem))
1632 return -EBUSY;
1633
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301634 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001635
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301636 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001637
1638 if (ret != 0) {
1639 up(&ar->sem);
1640 return -EIO;
1641 }
1642
1643 left = wait_event_interruptible_timeout(ar->event_wq,
1644 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301645 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001646 WMI_TIMEOUT);
1647
1648 up(&ar->sem);
1649
1650 if (left == 0)
1651 return -ETIMEDOUT;
1652 else if (left < 0)
1653 return left;
1654
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301655 if (vif->target_stats.rx_byte) {
1656 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001657 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301658 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001659 sinfo->filled |= STATION_INFO_RX_PACKETS;
1660 }
1661
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301662 if (vif->target_stats.tx_byte) {
1663 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001664 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301665 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001666 sinfo->filled |= STATION_INFO_TX_PACKETS;
1667 }
1668
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301669 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001670 sinfo->filled |= STATION_INFO_SIGNAL;
1671
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301672 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001673
1674 if (is_rate_legacy(rate)) {
1675 sinfo->txrate.legacy = rate / 100;
1676 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1677 if (sgi) {
1678 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1679 sinfo->txrate.mcs = mcs - 1;
1680 } else {
1681 sinfo->txrate.mcs = mcs;
1682 }
1683
1684 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1685 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1686 if (sgi) {
1687 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1688 sinfo->txrate.mcs = mcs - 1;
1689 } else {
1690 sinfo->txrate.mcs = mcs;
1691 }
1692
1693 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1694 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1695 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001696 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1697 "invalid rate from stats: %d\n", rate);
1698 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001699 return 0;
1700 }
1701
1702 sinfo->filled |= STATION_INFO_TX_BITRATE;
1703
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301704 if (test_bit(CONNECTED, &vif->flags) &&
1705 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301706 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001707 sinfo->filled |= STATION_INFO_BSS_PARAM;
1708 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301709 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1710 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001711 }
1712
Kalle Valobdcd8172011-07-18 00:22:30 +03001713 return 0;
1714}
1715
1716static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1717 struct cfg80211_pmksa *pmksa)
1718{
1719 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301720 struct ath6kl_vif *vif = netdev_priv(netdev);
1721
1722 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001723 pmksa->pmkid, true);
1724}
1725
1726static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1727 struct cfg80211_pmksa *pmksa)
1728{
1729 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301730 struct ath6kl_vif *vif = netdev_priv(netdev);
1731
1732 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001733 pmksa->pmkid, false);
1734}
1735
1736static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1737{
1738 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301739 struct ath6kl_vif *vif = netdev_priv(netdev);
1740
1741 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301742 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1743 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001744 return 0;
1745}
1746
Raja Manid91e8ee2012-01-30 17:13:10 +05301747static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1748 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001749{
Raja Manid91e8ee2012-01-30 17:13:10 +05301750 int ret, pos;
1751 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001752 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001753
Raja Manid91e8ee2012-01-30 17:13:10 +05301754 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001755 for (i = 0; i < wow->n_patterns; i++) {
1756
1757 /*
1758 * Convert given nl80211 specific mask value to equivalent
1759 * driver specific mask value and send it to the chip along
1760 * with patterns. For example, If the mask value defined in
1761 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1762 * then equivalent driver specific mask value is
1763 * "0xFF 0x00 0xFF 0x00".
1764 */
1765 memset(&mask, 0, sizeof(mask));
1766 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1767 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1768 mask[pos] = 0xFF;
1769 }
1770 /*
1771 * Note: Pattern's offset is not passed as part of wowlan
1772 * parameter from CFG layer. So it's always passed as ZERO
1773 * to the firmware. It means, given WOW patterns are always
1774 * matched from the first byte of received pkt in the firmware.
1775 */
1776 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301777 vif->fw_vif_idx, WOW_LIST_ID,
1778 wow->patterns[i].pattern_len,
1779 0 /* pattern offset */,
1780 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001781 if (ret)
1782 return ret;
1783 }
1784
Raja Manid91e8ee2012-01-30 17:13:10 +05301785 if (wow->disconnect)
1786 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1787
1788 if (wow->magic_pkt)
1789 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1790
1791 if (wow->gtk_rekey_failure)
1792 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1793
1794 if (wow->eap_identity_req)
1795 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1796
1797 if (wow->four_way_handshake)
1798 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1799
1800 return 0;
1801}
1802
1803static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1804{
1805 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1806 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1807 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1808 0x00, 0x08 };
1809 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1810 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1811 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1812 0x00, 0x7f };
1813 u8 unicst_offset = 0;
1814 static const u8 arp_pattern[] = { 0x08, 0x06 };
1815 static const u8 arp_mask[] = { 0xff, 0xff };
1816 u8 arp_offset = 20;
1817 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1818 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1819 u8 discvr_offset = 38;
1820 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1821 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1822 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1823 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1824 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1825 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1826 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1827 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1828 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1829 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1830 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1831 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1832 u8 dhcp_offset = 0;
1833 int ret;
1834
1835 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1836 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1837 vif->fw_vif_idx, WOW_LIST_ID,
1838 sizeof(unicst_pattern), unicst_offset,
1839 unicst_pattern, unicst_mask);
1840 if (ret) {
1841 ath6kl_err("failed to add WOW unicast IP pattern\n");
1842 return ret;
1843 }
1844
1845 /* Setup all ARP pkt pattern */
1846 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1847 vif->fw_vif_idx, WOW_LIST_ID,
1848 sizeof(arp_pattern), arp_offset,
1849 arp_pattern, arp_mask);
1850 if (ret) {
1851 ath6kl_err("failed to add WOW ARP pattern\n");
1852 return ret;
1853 }
1854
1855 /*
1856 * Setup multicast pattern for mDNS 224.0.0.251,
1857 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1858 */
1859 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1860 vif->fw_vif_idx, WOW_LIST_ID,
1861 sizeof(discvr_pattern), discvr_offset,
1862 discvr_pattern, discvr_mask);
1863 if (ret) {
1864 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1865 return ret;
1866 }
1867
1868 /* Setup all DHCP broadcast pkt pattern */
1869 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1870 vif->fw_vif_idx, WOW_LIST_ID,
1871 sizeof(dhcp_pattern), dhcp_offset,
1872 dhcp_pattern, dhcp_mask);
1873 if (ret) {
1874 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1875 return ret;
1876 }
1877
1878 return 0;
1879}
1880
1881static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1882{
1883 struct net_device *ndev = vif->ndev;
1884 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1885 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1886 u8 discvr_offset = 38;
1887 u8 mac_mask[ETH_ALEN];
1888 int ret;
1889
1890 /* Setup unicast pkt pattern */
1891 memset(mac_mask, 0xff, ETH_ALEN);
1892 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1893 vif->fw_vif_idx, WOW_LIST_ID,
1894 ETH_ALEN, 0, ndev->dev_addr,
1895 mac_mask);
1896 if (ret) {
1897 ath6kl_err("failed to add WOW unicast pattern\n");
1898 return ret;
1899 }
1900
1901 /*
1902 * Setup multicast pattern for mDNS 224.0.0.251,
1903 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1904 */
1905 if ((ndev->flags & IFF_ALLMULTI) ||
1906 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1907 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1908 vif->fw_vif_idx, WOW_LIST_ID,
1909 sizeof(discvr_pattern), discvr_offset,
1910 discvr_pattern, discvr_mask);
1911 if (ret) {
1912 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1913 "pattern\n");
1914 return ret;
1915 }
1916 }
1917
1918 return 0;
1919}
1920
1921static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
1922{
1923 struct in_device *in_dev;
1924 struct in_ifaddr *ifa;
1925 struct ath6kl_vif *vif;
1926 int ret, left;
1927 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05301928 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05301929 u8 index = 0;
1930 __be32 ips[MAX_IP_ADDRS];
1931
1932 vif = ath6kl_vif_first(ar);
1933 if (!vif)
1934 return -EIO;
1935
1936 if (!ath6kl_cfg80211_ready(vif))
1937 return -EIO;
1938
1939 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05301940 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05301941
1942 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
1943 return -EINVAL;
1944
1945 /* Clear existing WOW patterns */
1946 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
1947 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
1948 WOW_LIST_ID, i);
1949
1950 /*
1951 * Skip the default WOW pattern configuration
1952 * if the driver receives any WOW patterns from
1953 * the user.
1954 */
1955 if (wow)
1956 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
1957 else if (vif->nw_type == AP_NETWORK)
1958 ret = ath6kl_wow_ap(ar, vif);
1959 else
1960 ret = ath6kl_wow_sta(ar, vif);
1961
1962 if (ret)
1963 return ret;
1964
Raja Mani390a8c82012-03-07 11:35:04 +05301965 netif_stop_queue(vif->ndev);
1966
Raja Manice0dc0c2012-02-20 19:08:08 +05301967 if (vif->nw_type != AP_NETWORK) {
1968 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
1969 ATH6KL_MAX_WOW_LISTEN_INTL,
1970 0);
1971 if (ret)
1972 return ret;
1973
1974 /* Set listen interval x 15 times as bmiss time */
1975 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
1976 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
1977 bmiss_time = ATH6KL_MAX_BMISS_TIME;
1978
1979 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
1980 bmiss_time, 0);
1981 if (ret)
1982 return ret;
1983
1984 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
1985 0xFFFF, 0, 0xFFFF, 0, 0, 0,
1986 0, 0, 0, 0);
1987 if (ret)
1988 return ret;
1989 }
1990
Raja Mani390a8c82012-03-07 11:35:04 +05301991 ar->state = ATH6KL_STATE_SUSPENDING;
1992
Raja Manic08631c2011-12-16 14:24:24 +05301993 /* Setup own IP addr for ARP agent. */
1994 in_dev = __in_dev_get_rtnl(vif->ndev);
1995 if (!in_dev)
1996 goto skip_arp;
1997
1998 ifa = in_dev->ifa_list;
1999 memset(&ips, 0, sizeof(ips));
2000
2001 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2002 while (index < MAX_IP_ADDRS && ifa) {
2003 ips[index] = ifa->ifa_local;
2004 ifa = ifa->ifa_next;
2005 index++;
2006 }
2007
2008 if (ifa) {
2009 ath6kl_err("total IP addr count is exceeding fw limit\n");
2010 return -EINVAL;
2011 }
2012
2013 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2014 if (ret) {
2015 ath6kl_err("fail to setup ip for arp agent\n");
2016 return ret;
2017 }
2018
2019skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002020 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2021 ATH6KL_WOW_MODE_ENABLE,
2022 filter,
2023 WOW_HOST_REQ_DELAY);
2024 if (ret)
2025 return ret;
2026
Raja Mani081c7a82012-01-30 17:13:11 +05302027 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2028
Raja Mani6cb3c712011-11-07 22:52:45 +02002029 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2030 ATH6KL_HOST_MODE_ASLEEP);
2031 if (ret)
2032 return ret;
2033
Raja Mani081c7a82012-01-30 17:13:11 +05302034 left = wait_event_interruptible_timeout(ar->event_wq,
2035 test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
2036 WMI_TIMEOUT);
2037 if (left == 0) {
2038 ath6kl_warn("timeout, didn't get host sleep cmd "
2039 "processed event\n");
2040 ret = -ETIMEDOUT;
2041 } else if (left < 0) {
2042 ath6kl_warn("error while waiting for host sleep cmd "
2043 "processed event %d\n", left);
2044 ret = left;
2045 }
2046
Raja Mani6cb3c712011-11-07 22:52:45 +02002047 if (ar->tx_pending[ar->ctrl_ep]) {
2048 left = wait_event_interruptible_timeout(ar->event_wq,
2049 ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
2050 if (left == 0) {
2051 ath6kl_warn("clear wmi ctrl data timeout\n");
2052 ret = -ETIMEDOUT;
2053 } else if (left < 0) {
2054 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2055 ret = left;
2056 }
2057 }
2058
2059 return ret;
2060}
2061
2062static int ath6kl_wow_resume(struct ath6kl *ar)
2063{
2064 struct ath6kl_vif *vif;
2065 int ret;
2066
2067 vif = ath6kl_vif_first(ar);
2068 if (!vif)
2069 return -EIO;
2070
Raja Mani390a8c82012-03-07 11:35:04 +05302071 ar->state = ATH6KL_STATE_RESUMING;
2072
Raja Mani6cb3c712011-11-07 22:52:45 +02002073 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2074 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302075 if (ret) {
2076 ath6kl_warn("Failed to configure host sleep mode for "
2077 "wow resume: %d\n", ret);
2078 ar->state = ATH6KL_STATE_WOW;
2079 return ret;
2080 }
2081
Raja Manice0dc0c2012-02-20 19:08:08 +05302082 if (vif->nw_type != AP_NETWORK) {
2083 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2084 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2085 if (ret)
2086 return ret;
2087
2088 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2089 vif->listen_intvl_t, 0);
2090 if (ret)
2091 return ret;
2092
2093 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2094 vif->bmiss_time_t, 0);
2095 if (ret)
2096 return ret;
2097 }
2098
Raja Mani390a8c82012-03-07 11:35:04 +05302099 ar->state = ATH6KL_STATE_ON;
2100
2101 netif_wake_queue(vif->ndev);
2102
2103 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002104}
2105
Kalle Valo52d81a62011-11-01 08:44:21 +02002106int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002107 enum ath6kl_cfg_suspend_mode mode,
2108 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002109{
Raja Mani390a8c82012-03-07 11:35:04 +05302110 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002111 int ret;
2112
Kalle Valo52d81a62011-11-01 08:44:21 +02002113 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002114 case ATH6KL_CFG_SUSPEND_WOW:
2115
2116 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2117
2118 /* Flush all non control pkts in TX path */
2119 ath6kl_tx_data_cleanup(ar);
2120
Raja Mani390a8c82012-03-07 11:35:04 +05302121 prev_state = ar->state;
2122
Raja Manid7c44e02011-11-07 22:52:46 +02002123 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302124 if (ret) {
2125 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002126 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302127 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302128
Raja Manid7c44e02011-11-07 22:52:46 +02002129 ar->state = ATH6KL_STATE_WOW;
2130 break;
2131
Kalle Valo52d81a62011-11-01 08:44:21 +02002132 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002133
Kalle Valo7125f012011-12-13 14:51:37 +02002134 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002135
Kalle Valo52d81a62011-11-01 08:44:21 +02002136 /* save the current power mode before enabling power save */
2137 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2138
2139 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2140 if (ret) {
2141 ath6kl_warn("wmi powermode command failed during suspend: %d\n",
2142 ret);
2143 }
2144
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002145 ar->state = ATH6KL_STATE_DEEPSLEEP;
2146
Kalle Valo52d81a62011-11-01 08:44:21 +02002147 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002148
2149 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002150
Kalle Valo7125f012011-12-13 14:51:37 +02002151 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002152
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002153 if (ar->state == ATH6KL_STATE_OFF) {
2154 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2155 "suspend hw off, no action for cutpower\n");
2156 break;
2157 }
2158
2159 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2160
2161 ret = ath6kl_init_hw_stop(ar);
2162 if (ret) {
2163 ath6kl_warn("failed to stop hw during suspend: %d\n",
2164 ret);
2165 }
2166
2167 ar->state = ATH6KL_STATE_CUTPOWER;
2168
2169 break;
2170
Kalle Valo10509f92011-12-13 14:52:07 +02002171 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2172 /*
2173 * Nothing needed for schedule scan, firmware is already in
2174 * wow mode and sleeping most of the time.
2175 */
2176 break;
2177
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002178 default:
2179 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002180 }
2181
2182 return 0;
2183}
Kalle Valod6a434d2012-01-17 20:09:36 +02002184EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002185
2186int ath6kl_cfg80211_resume(struct ath6kl *ar)
2187{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002188 int ret;
2189
2190 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002191 case ATH6KL_STATE_WOW:
2192 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2193
2194 ret = ath6kl_wow_resume(ar);
2195 if (ret) {
2196 ath6kl_warn("wow mode resume failed: %d\n", ret);
2197 return ret;
2198 }
2199
Raja Manid7c44e02011-11-07 22:52:46 +02002200 break;
2201
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002202 case ATH6KL_STATE_DEEPSLEEP:
2203 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2204 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2205 ar->wmi->saved_pwr_mode);
2206 if (ret) {
2207 ath6kl_warn("wmi powermode command failed during resume: %d\n",
2208 ret);
2209 }
2210 }
2211
2212 ar->state = ATH6KL_STATE_ON;
2213
2214 break;
2215
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002216 case ATH6KL_STATE_CUTPOWER:
2217 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2218
2219 ret = ath6kl_init_hw_start(ar);
2220 if (ret) {
2221 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2222 return ret;
2223 }
Raja Manid7c44e02011-11-07 22:52:46 +02002224 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002225
Kalle Valo10509f92011-12-13 14:52:07 +02002226 case ATH6KL_STATE_SCHED_SCAN:
2227 break;
2228
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002229 default:
2230 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002231 }
2232
2233 return 0;
2234}
Kalle Valod6a434d2012-01-17 20:09:36 +02002235EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002236
Kalle Valoabcb3442011-07-22 08:26:20 +03002237#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002238
2239/* hif layer decides what suspend mode to use */
2240static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002241 struct cfg80211_wowlan *wow)
2242{
2243 struct ath6kl *ar = wiphy_priv(wiphy);
2244
Raja Mani0f60e9f2011-11-07 22:52:45 +02002245 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002246}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002247
Kalle Valo52d81a62011-11-01 08:44:21 +02002248static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002249{
2250 struct ath6kl *ar = wiphy_priv(wiphy);
2251
2252 return ath6kl_hif_resume(ar);
2253}
Raja Mania918fb32011-11-07 22:52:46 +02002254
2255/*
2256 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2257 * both sdio irq wake up and keep power. The target pulls sdio data line to
2258 * wake up the host when WOW pattern matches. This causes sdio irq handler
2259 * is being called in the host side which internally hits ath6kl's RX path.
2260 *
2261 * Since sdio interrupt is not disabled, RX path executes even before
2262 * the host executes the actual resume operation from PM module.
2263 *
2264 * In the current scenario, WOW resume should happen before start processing
2265 * any data from the target. So It's required to perform WOW resume in RX path.
2266 * Ideally we should perform WOW resume only in the actual platform
2267 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2268 *
2269 * ath6kl_check_wow_status() is called from ath6kl_rx().
2270 */
2271void ath6kl_check_wow_status(struct ath6kl *ar)
2272{
Raja Mani390a8c82012-03-07 11:35:04 +05302273 if (ar->state == ATH6KL_STATE_SUSPENDING)
2274 return;
2275
Raja Mania918fb32011-11-07 22:52:46 +02002276 if (ar->state == ATH6KL_STATE_WOW)
2277 ath6kl_cfg80211_resume(ar);
2278}
2279
2280#else
2281
2282void ath6kl_check_wow_status(struct ath6kl *ar)
2283{
2284}
Kalle Valoabcb3442011-07-22 08:26:20 +03002285#endif
2286
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002287static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2288 struct ieee80211_channel *chan,
2289 enum nl80211_channel_type channel_type)
2290{
Sujith Manoharane68f6752011-12-22 12:15:27 +05302291 struct ath6kl_vif *vif;
2292
2293 /*
2294 * 'dev' could be NULL if a channel change is required for the hardware
2295 * device itself, instead of a particular VIF.
2296 *
2297 * FIXME: To be handled properly when monitor mode is supported.
2298 */
2299 if (!dev)
2300 return -EBUSY;
2301
2302 vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002303
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302304 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002305 return -EIO;
2306
2307 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2308 __func__, chan->center_freq, chan->hw_value);
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302309 vif->next_chan = chan->center_freq;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002310
2311 return 0;
2312}
2313
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002314static bool ath6kl_is_p2p_ie(const u8 *pos)
2315{
2316 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2317 pos[2] == 0x50 && pos[3] == 0x6f &&
2318 pos[4] == 0x9a && pos[5] == 0x09;
2319}
2320
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302321static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2322 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002323{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302324 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002325 const u8 *pos;
2326 u8 *buf = NULL;
2327 size_t len = 0;
2328 int ret;
2329
2330 /*
2331 * Filter out P2P IE(s) since they will be included depending on
2332 * the Probe Request frame in ath6kl_send_go_probe_resp().
2333 */
2334
2335 if (ies && ies_len) {
2336 buf = kmalloc(ies_len, GFP_KERNEL);
2337 if (buf == NULL)
2338 return -ENOMEM;
2339 pos = ies;
2340 while (pos + 1 < ies + ies_len) {
2341 if (pos + 2 + pos[1] > ies + ies_len)
2342 break;
2343 if (!ath6kl_is_p2p_ie(pos)) {
2344 memcpy(buf + len, pos, 2 + pos[1]);
2345 len += 2 + pos[1];
2346 }
2347 pos += 2 + pos[1];
2348 }
2349 }
2350
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302351 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2352 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002353 kfree(buf);
2354 return ret;
2355}
2356
Johannes Berg88600202012-02-13 15:17:18 +01002357static int ath6kl_set_ies(struct ath6kl_vif *vif,
2358 struct cfg80211_beacon_data *info)
2359{
2360 struct ath6kl *ar = vif->ar;
2361 int res;
2362
2363 if (info->beacon_ies) {
2364 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2365 WMI_FRAME_BEACON,
2366 info->beacon_ies,
2367 info->beacon_ies_len);
2368 if (res)
2369 return res;
2370 }
2371
2372 if (info->proberesp_ies) {
2373 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2374 info->proberesp_ies_len);
2375 if (res)
2376 return res;
2377 }
2378
2379 if (info->assocresp_ies) {
2380 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2381 WMI_FRAME_ASSOC_RESP,
2382 info->assocresp_ies,
2383 info->assocresp_ies_len);
2384 if (res)
2385 return res;
2386 }
2387
2388 return 0;
2389}
2390
2391static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2392 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002393{
2394 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302395 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002396 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002397 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002398 u8 *ies;
2399 int ies_len;
2400 struct wmi_connect_cmd p;
2401 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302402 int i, ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002403
Johannes Berg88600202012-02-13 15:17:18 +01002404 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002405
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302406 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002407 return -EIO;
2408
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302409 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002410 return -EOPNOTSUPP;
2411
Johannes Berg88600202012-02-13 15:17:18 +01002412 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002413
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002414 ar->ap_mode_bkey.valid = false;
2415
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002416 /* TODO:
2417 * info->interval
2418 * info->dtim_period
2419 */
2420
Johannes Berg88600202012-02-13 15:17:18 +01002421 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002422 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002423 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002424 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002425 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002426 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002427 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002428
2429 if (info->ssid == NULL)
2430 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302431 memcpy(vif->ssid, info->ssid, info->ssid_len);
2432 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002433 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002434 hidden = true;
2435
2436 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2437 if (res)
2438 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002439
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302440 ret = ath6kl_set_auth_type(vif, info->auth_type);
2441 if (ret)
2442 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002443
2444 memset(&p, 0, sizeof(p));
2445
2446 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2447 switch (info->crypto.akm_suites[i]) {
2448 case WLAN_AKM_SUITE_8021X:
2449 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2450 p.auth_mode |= WPA_AUTH;
2451 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2452 p.auth_mode |= WPA2_AUTH;
2453 break;
2454 case WLAN_AKM_SUITE_PSK:
2455 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2456 p.auth_mode |= WPA_PSK_AUTH;
2457 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2458 p.auth_mode |= WPA2_PSK_AUTH;
2459 break;
2460 }
2461 }
2462 if (p.auth_mode == 0)
2463 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302464 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002465
2466 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2467 switch (info->crypto.ciphers_pairwise[i]) {
2468 case WLAN_CIPHER_SUITE_WEP40:
2469 case WLAN_CIPHER_SUITE_WEP104:
2470 p.prwise_crypto_type |= WEP_CRYPT;
2471 break;
2472 case WLAN_CIPHER_SUITE_TKIP:
2473 p.prwise_crypto_type |= TKIP_CRYPT;
2474 break;
2475 case WLAN_CIPHER_SUITE_CCMP:
2476 p.prwise_crypto_type |= AES_CRYPT;
2477 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002478 case WLAN_CIPHER_SUITE_SMS4:
2479 p.prwise_crypto_type |= WAPI_CRYPT;
2480 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002481 }
2482 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002483 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002484 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302485 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002486 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302487 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002488
2489 switch (info->crypto.cipher_group) {
2490 case WLAN_CIPHER_SUITE_WEP40:
2491 case WLAN_CIPHER_SUITE_WEP104:
2492 p.grp_crypto_type = WEP_CRYPT;
2493 break;
2494 case WLAN_CIPHER_SUITE_TKIP:
2495 p.grp_crypto_type = TKIP_CRYPT;
2496 break;
2497 case WLAN_CIPHER_SUITE_CCMP:
2498 p.grp_crypto_type = AES_CRYPT;
2499 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002500 case WLAN_CIPHER_SUITE_SMS4:
2501 p.grp_crypto_type = WAPI_CRYPT;
2502 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002503 default:
2504 p.grp_crypto_type = NONE_CRYPT;
2505 break;
2506 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302507 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002508
2509 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302510 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002511
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302512 p.ssid_len = vif->ssid_len;
2513 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2514 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302515 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002516
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302517 /* Enable uAPSD support by default */
2518 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2519 if (res < 0)
2520 return res;
2521
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002522 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2523 p.nw_subtype = SUBTYPE_P2PGO;
2524 } else {
2525 /*
2526 * Due to firmware limitation, it is not possible to
2527 * do P2P mgmt operations in AP mode
2528 */
2529 p.nw_subtype = SUBTYPE_NONE;
2530 }
2531
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302532 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002533 if (res < 0)
2534 return res;
2535
2536 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002537}
2538
Johannes Berg88600202012-02-13 15:17:18 +01002539static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2540 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002541{
Johannes Berg88600202012-02-13 15:17:18 +01002542 struct ath6kl_vif *vif = netdev_priv(dev);
2543
2544 if (!ath6kl_cfg80211_ready(vif))
2545 return -EIO;
2546
2547 if (vif->next_mode != AP_NETWORK)
2548 return -EOPNOTSUPP;
2549
2550 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002551}
2552
Johannes Berg88600202012-02-13 15:17:18 +01002553static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002554{
2555 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302556 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002557
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302558 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002559 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302560 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002561 return -ENOTCONN;
2562
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302563 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302564 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002565
2566 return 0;
2567}
2568
Jouni Malinen33e53082011-12-27 11:02:56 +02002569static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2570
2571static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2572 u8 *mac)
2573{
2574 struct ath6kl *ar = ath6kl_priv(dev);
2575 struct ath6kl_vif *vif = netdev_priv(dev);
2576 const u8 *addr = mac ? mac : bcast_addr;
2577
2578 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2579 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2580}
2581
Jouni Malinen23875132011-08-30 21:57:53 +03002582static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2583 u8 *mac, struct station_parameters *params)
2584{
2585 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302586 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002587
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302588 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002589 return -EOPNOTSUPP;
2590
2591 /* Use this only for authorizing/unauthorizing a station */
2592 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2593 return -EOPNOTSUPP;
2594
2595 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302596 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2597 WMI_AP_MLME_AUTHORIZE, mac, 0);
2598 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2599 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002600}
2601
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002602static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2603 struct net_device *dev,
2604 struct ieee80211_channel *chan,
2605 enum nl80211_channel_type channel_type,
2606 unsigned int duration,
2607 u64 *cookie)
2608{
2609 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302610 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002611 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002612
2613 /* TODO: if already pending or ongoing remain-on-channel,
2614 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002615 id = ++vif->last_roc_id;
2616 if (id == 0) {
2617 /* Do not use 0 as the cookie value */
2618 id = ++vif->last_roc_id;
2619 }
2620 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002621
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302622 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2623 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002624}
2625
2626static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2627 struct net_device *dev,
2628 u64 cookie)
2629{
2630 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302631 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002632
Jouni Malinen10522612011-10-27 16:00:13 +03002633 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002634 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002635 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002636
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302637 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002638}
2639
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302640static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2641 const u8 *buf, size_t len,
2642 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002643{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302644 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002645 const u8 *pos;
2646 u8 *p2p;
2647 int p2p_len;
2648 int ret;
2649 const struct ieee80211_mgmt *mgmt;
2650
2651 mgmt = (const struct ieee80211_mgmt *) buf;
2652
2653 /* Include P2P IE(s) from the frame generated in user space. */
2654
2655 p2p = kmalloc(len, GFP_KERNEL);
2656 if (p2p == NULL)
2657 return -ENOMEM;
2658 p2p_len = 0;
2659
2660 pos = mgmt->u.probe_resp.variable;
2661 while (pos + 1 < buf + len) {
2662 if (pos + 2 + pos[1] > buf + len)
2663 break;
2664 if (ath6kl_is_p2p_ie(pos)) {
2665 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2666 p2p_len += 2 + pos[1];
2667 }
2668 pos += 2 + pos[1];
2669 }
2670
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302671 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2672 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002673 kfree(p2p);
2674 return ret;
2675}
2676
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002677static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2678 u32 id,
2679 u32 freq,
2680 u32 wait,
2681 const u8 *buf,
2682 size_t len,
2683 bool *more_data,
2684 bool no_cck)
2685{
2686 struct ieee80211_mgmt *mgmt;
2687 struct ath6kl_sta *conn;
2688 bool is_psq_empty = false;
2689 struct ath6kl_mgmt_buff *mgmt_buf;
2690 size_t mgmt_buf_size;
2691 struct ath6kl *ar = vif->ar;
2692
2693 mgmt = (struct ieee80211_mgmt *) buf;
2694 if (is_multicast_ether_addr(mgmt->da))
2695 return false;
2696
2697 conn = ath6kl_find_sta(vif, mgmt->da);
2698 if (!conn)
2699 return false;
2700
2701 if (conn->sta_flags & STA_PS_SLEEP) {
2702 if (!(conn->sta_flags & STA_PS_POLLED)) {
2703 /* Queue the frames if the STA is sleeping */
2704 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2705 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2706 if (!mgmt_buf)
2707 return false;
2708
2709 INIT_LIST_HEAD(&mgmt_buf->list);
2710 mgmt_buf->id = id;
2711 mgmt_buf->freq = freq;
2712 mgmt_buf->wait = wait;
2713 mgmt_buf->len = len;
2714 mgmt_buf->no_cck = no_cck;
2715 memcpy(mgmt_buf->buf, buf, len);
2716 spin_lock_bh(&conn->psq_lock);
2717 is_psq_empty = skb_queue_empty(&conn->psq) &&
2718 (conn->mgmt_psq_len == 0);
2719 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2720 conn->mgmt_psq_len++;
2721 spin_unlock_bh(&conn->psq_lock);
2722
2723 /*
2724 * If this is the first pkt getting queued
2725 * for this STA, update the PVB for this
2726 * STA.
2727 */
2728 if (is_psq_empty)
2729 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2730 conn->aid, 1);
2731 return true;
2732 }
2733
2734 /*
2735 * This tx is because of a PsPoll.
2736 * Determine if MoreData bit has to be set.
2737 */
2738 spin_lock_bh(&conn->psq_lock);
2739 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2740 *more_data = true;
2741 spin_unlock_bh(&conn->psq_lock);
2742 }
2743
2744 return false;
2745}
2746
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002747static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2748 struct ieee80211_channel *chan, bool offchan,
2749 enum nl80211_channel_type channel_type,
2750 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01002751 const u8 *buf, size_t len, bool no_cck,
2752 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002753{
2754 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302755 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002756 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002757 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002758 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002759
2760 mgmt = (const struct ieee80211_mgmt *) buf;
2761 if (buf + len >= mgmt->u.probe_resp.variable &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302762 vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002763 ieee80211_is_probe_resp(mgmt->frame_control)) {
2764 /*
2765 * Send Probe Response frame in AP mode using a separate WMI
2766 * command to allow the target to fill in the generic IEs.
2767 */
2768 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302769 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002770 chan->center_freq);
2771 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002772
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302773 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002774 if (id == 0) {
2775 /*
2776 * 0 is a reserved value in the WMI command and shall not be
2777 * used for the command.
2778 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302779 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002780 }
2781
2782 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002783
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002784 /* AP mode Power saving processing */
2785 if (vif->nw_type == AP_NETWORK) {
2786 queued = ath6kl_mgmt_powersave_ap(vif,
2787 id, chan->center_freq,
2788 wait, buf,
2789 len, &more_data, no_cck);
2790 if (queued)
2791 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002792 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002793
2794 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2795 chan->center_freq, wait,
2796 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002797}
2798
Jouni Malinenae32c302011-08-30 21:58:01 +03002799static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
2800 struct net_device *dev,
2801 u16 frame_type, bool reg)
2802{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302803 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03002804
2805 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
2806 __func__, frame_type, reg);
2807 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
2808 /*
2809 * Note: This notification callback is not allowed to sleep, so
2810 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
2811 * hardcode target to report Probe Request frames all the time.
2812 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302813 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03002814 }
2815}
2816
Kalle Valo10509f92011-12-13 14:52:07 +02002817static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
2818 struct net_device *dev,
2819 struct cfg80211_sched_scan_request *request)
2820{
2821 struct ath6kl *ar = ath6kl_priv(dev);
2822 struct ath6kl_vif *vif = netdev_priv(dev);
2823 u16 interval;
2824 int ret;
2825 u8 i;
2826
2827 if (ar->state != ATH6KL_STATE_ON)
2828 return -EIO;
2829
2830 if (vif->sme_state != SME_DISCONNECTED)
2831 return -EBUSY;
2832
2833 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
2834 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2835 i, DISABLE_SSID_FLAG,
2836 0, NULL);
2837 }
2838
2839 /* fw uses seconds, also make sure that it's >0 */
2840 interval = max_t(u16, 1, request->interval / 1000);
2841
2842 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2843 interval, interval,
2844 10, 0, 0, 0, 3, 0, 0, 0);
2845
2846 if (request->n_ssids && request->ssids[0].ssid_len) {
2847 for (i = 0; i < request->n_ssids; i++) {
2848 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
2849 i, SPECIFIC_SSID_FLAG,
2850 request->ssids[i].ssid_len,
2851 request->ssids[i].ssid);
2852 }
2853 }
2854
2855 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2856 ATH6KL_WOW_MODE_ENABLE,
2857 WOW_FILTER_SSID,
2858 WOW_HOST_REQ_DELAY);
2859 if (ret) {
2860 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
2861 return ret;
2862 }
2863
2864 /* this also clears IE in fw if it's not set */
2865 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2866 WMI_FRAME_PROBE_REQ,
2867 request->ie, request->ie_len);
2868 if (ret) {
2869 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
2870 ret);
2871 return ret;
2872 }
2873
2874 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2875 ATH6KL_HOST_MODE_ASLEEP);
2876 if (ret) {
2877 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
2878 ret);
2879 return ret;
2880 }
2881
2882 ar->state = ATH6KL_STATE_SCHED_SCAN;
2883
2884 return ret;
2885}
2886
2887static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
2888 struct net_device *dev)
2889{
2890 struct ath6kl_vif *vif = netdev_priv(dev);
2891 bool stopped;
2892
2893 stopped = __ath6kl_cfg80211_sscan_stop(vif);
2894
2895 if (!stopped)
2896 return -EIO;
2897
2898 return 0;
2899}
2900
Jouni Malinenf80574a2011-08-30 21:58:04 +03002901static const struct ieee80211_txrx_stypes
2902ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2903 [NL80211_IFTYPE_STATION] = {
2904 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2905 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2906 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2907 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2908 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02002909 [NL80211_IFTYPE_AP] = {
2910 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2911 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2912 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2913 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2914 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03002915 [NL80211_IFTYPE_P2P_CLIENT] = {
2916 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2917 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2918 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2919 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2920 },
2921 [NL80211_IFTYPE_P2P_GO] = {
2922 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2923 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2924 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2925 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2926 },
2927};
2928
Kalle Valobdcd8172011-07-18 00:22:30 +03002929static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05302930 .add_virtual_intf = ath6kl_cfg80211_add_iface,
2931 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03002932 .change_virtual_intf = ath6kl_cfg80211_change_iface,
2933 .scan = ath6kl_cfg80211_scan,
2934 .connect = ath6kl_cfg80211_connect,
2935 .disconnect = ath6kl_cfg80211_disconnect,
2936 .add_key = ath6kl_cfg80211_add_key,
2937 .get_key = ath6kl_cfg80211_get_key,
2938 .del_key = ath6kl_cfg80211_del_key,
2939 .set_default_key = ath6kl_cfg80211_set_default_key,
2940 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
2941 .set_tx_power = ath6kl_cfg80211_set_txpower,
2942 .get_tx_power = ath6kl_cfg80211_get_txpower,
2943 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
2944 .join_ibss = ath6kl_cfg80211_join_ibss,
2945 .leave_ibss = ath6kl_cfg80211_leave_ibss,
2946 .get_station = ath6kl_get_station,
2947 .set_pmksa = ath6kl_set_pmksa,
2948 .del_pmksa = ath6kl_del_pmksa,
2949 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03002950 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03002951#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002952 .suspend = __ath6kl_cfg80211_suspend,
2953 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03002954#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002955 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01002956 .start_ap = ath6kl_start_ap,
2957 .change_beacon = ath6kl_change_beacon,
2958 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02002959 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03002960 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002961 .remain_on_channel = ath6kl_remain_on_channel,
2962 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03002963 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03002964 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02002965 .sched_scan_start = ath6kl_cfg80211_sscan_start,
2966 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03002967};
2968
Kalle Valo7125f012011-12-13 14:51:37 +02002969void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02002970{
Kalle Valo10509f92011-12-13 14:52:07 +02002971 ath6kl_cfg80211_sscan_disable(vif);
2972
Kalle Valoec4b7f62011-11-01 08:44:04 +02002973 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02002974 case SME_DISCONNECTED:
2975 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02002976 case SME_CONNECTING:
2977 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
2978 NULL, 0,
2979 WLAN_STATUS_UNSPECIFIED_FAILURE,
2980 GFP_KERNEL);
2981 break;
2982 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02002983 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
2984 break;
2985 }
2986
2987 if (test_bit(CONNECTED, &vif->flags) ||
2988 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02002989 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002990
2991 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02002992 clear_bit(CONNECTED, &vif->flags);
2993 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02002994
2995 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02002996 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
2997 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
2998 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02002999
3000 ath6kl_cfg80211_scan_complete_event(vif, true);
3001}
3002
Kalle Valo7125f012011-12-13 14:51:37 +02003003void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3004{
3005 struct ath6kl_vif *vif;
3006
3007 vif = ath6kl_vif_first(ar);
3008 if (!vif) {
3009 /* save the current power mode before enabling power save */
3010 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3011
3012 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3013 ath6kl_warn("ath6kl_deep_sleep_enable: "
3014 "wmi_powermode_cmd failed\n");
3015 return;
3016 }
3017
3018 /*
3019 * FIXME: we should take ar->list_lock to protect changes in the
3020 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3021 * sleeps.
3022 */
3023 list_for_each_entry(vif, &ar->vif_list, list)
3024 ath6kl_cfg80211_stop(vif);
3025}
3026
Kalle Valoc25889e2012-01-17 20:08:27 +02003027static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003028{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303029 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303030 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303031 ath6kl_err("failed to initialize aggr\n");
3032 return -ENOMEM;
3033 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003034
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303035 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303036 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003037 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3038 (unsigned long) vif);
3039
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303040 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303041 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303042
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303043 INIT_LIST_HEAD(&vif->mc_filter);
3044
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303045 return 0;
3046}
3047
Kalle Valoc25889e2012-01-17 20:08:27 +02003048void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303049{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303050 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303051 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303052
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303053 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303054
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303055 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3056
3057 if (vif->nw_type == ADHOC_NETWORK)
3058 ar->ibss_if_active = false;
3059
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303060 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3061 list_del(&mc_filter->list);
3062 kfree(mc_filter);
3063 }
3064
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303065 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303066
3067 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303068}
3069
3070struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303071 enum nl80211_iftype type, u8 fw_vif_idx,
3072 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303073{
3074 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303075 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303076
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303077 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303078 if (!ndev)
3079 return NULL;
3080
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303081 vif = netdev_priv(ndev);
3082 ndev->ieee80211_ptr = &vif->wdev;
3083 vif->wdev.wiphy = ar->wiphy;
3084 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303085 vif->ndev = ndev;
3086 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3087 vif->wdev.netdev = ndev;
3088 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303089 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003090 vif->nw_type = nw_type;
3091 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