blob: e68b1077816a3d24eaad484f3645c0c3822b41af [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
Joe Perches516304b2012-03-18 17:30:52 -070018#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040020#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053021#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020022#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040023
Kalle Valobdcd8172011-07-18 00:22:30 +030024#include "core.h"
25#include "cfg80211.h"
26#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030027#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030028#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030029
30#define RATETAB_ENT(_rate, _rateid, _flags) { \
31 .bitrate = (_rate), \
32 .flags = (_flags), \
33 .hw_value = (_rateid), \
34}
35
36#define CHAN2G(_channel, _freq, _flags) { \
37 .band = IEEE80211_BAND_2GHZ, \
38 .hw_value = (_channel), \
39 .center_freq = (_freq), \
40 .flags = (_flags), \
41 .max_antenna_gain = 0, \
42 .max_power = 30, \
43}
44
45#define CHAN5G(_channel, _flags) { \
46 .band = IEEE80211_BAND_5GHZ, \
47 .hw_value = (_channel), \
48 .center_freq = 5000 + (5 * (_channel)), \
49 .flags = (_flags), \
50 .max_antenna_gain = 0, \
51 .max_power = 30, \
52}
53
Bala Shanmugamf5993592012-03-27 12:17:32 +053054#define DEFAULT_BG_SCAN_PERIOD 60
55
Kalle Valobdcd8172011-07-18 00:22:30 +030056static struct ieee80211_rate ath6kl_rates[] = {
57 RATETAB_ENT(10, 0x1, 0),
58 RATETAB_ENT(20, 0x2, 0),
59 RATETAB_ENT(55, 0x4, 0),
60 RATETAB_ENT(110, 0x8, 0),
61 RATETAB_ENT(60, 0x10, 0),
62 RATETAB_ENT(90, 0x20, 0),
63 RATETAB_ENT(120, 0x40, 0),
64 RATETAB_ENT(180, 0x80, 0),
65 RATETAB_ENT(240, 0x100, 0),
66 RATETAB_ENT(360, 0x200, 0),
67 RATETAB_ENT(480, 0x400, 0),
68 RATETAB_ENT(540, 0x800, 0),
69};
70
71#define ath6kl_a_rates (ath6kl_rates + 4)
72#define ath6kl_a_rates_size 8
73#define ath6kl_g_rates (ath6kl_rates + 0)
74#define ath6kl_g_rates_size 12
75
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +053076#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
77#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053078 IEEE80211_HT_CAP_SGI_20 | \
79 IEEE80211_HT_CAP_SGI_40)
80
Kalle Valobdcd8172011-07-18 00:22:30 +030081static struct ieee80211_channel ath6kl_2ghz_channels[] = {
82 CHAN2G(1, 2412, 0),
83 CHAN2G(2, 2417, 0),
84 CHAN2G(3, 2422, 0),
85 CHAN2G(4, 2427, 0),
86 CHAN2G(5, 2432, 0),
87 CHAN2G(6, 2437, 0),
88 CHAN2G(7, 2442, 0),
89 CHAN2G(8, 2447, 0),
90 CHAN2G(9, 2452, 0),
91 CHAN2G(10, 2457, 0),
92 CHAN2G(11, 2462, 0),
93 CHAN2G(12, 2467, 0),
94 CHAN2G(13, 2472, 0),
95 CHAN2G(14, 2484, 0),
96};
97
98static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
99 CHAN5G(34, 0), CHAN5G(36, 0),
100 CHAN5G(38, 0), CHAN5G(40, 0),
101 CHAN5G(42, 0), CHAN5G(44, 0),
102 CHAN5G(46, 0), CHAN5G(48, 0),
103 CHAN5G(52, 0), CHAN5G(56, 0),
104 CHAN5G(60, 0), CHAN5G(64, 0),
105 CHAN5G(100, 0), CHAN5G(104, 0),
106 CHAN5G(108, 0), CHAN5G(112, 0),
107 CHAN5G(116, 0), CHAN5G(120, 0),
108 CHAN5G(124, 0), CHAN5G(128, 0),
109 CHAN5G(132, 0), CHAN5G(136, 0),
110 CHAN5G(140, 0), CHAN5G(149, 0),
111 CHAN5G(153, 0), CHAN5G(157, 0),
112 CHAN5G(161, 0), CHAN5G(165, 0),
113 CHAN5G(184, 0), CHAN5G(188, 0),
114 CHAN5G(192, 0), CHAN5G(196, 0),
115 CHAN5G(200, 0), CHAN5G(204, 0),
116 CHAN5G(208, 0), CHAN5G(212, 0),
117 CHAN5G(216, 0),
118};
119
120static struct ieee80211_supported_band ath6kl_band_2ghz = {
121 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
122 .channels = ath6kl_2ghz_channels,
123 .n_bitrates = ath6kl_g_rates_size,
124 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530125 .ht_cap.cap = ath6kl_g_htcap,
126 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300127};
128
129static struct ieee80211_supported_band ath6kl_band_5ghz = {
130 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
131 .channels = ath6kl_5ghz_a_channels,
132 .n_bitrates = ath6kl_a_rates_size,
133 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +0530134 .ht_cap.cap = ath6kl_a_htcap,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530135 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300136};
137
Jouni Malinen837cb972011-10-11 17:31:57 +0300138#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
139
Kalle Valo10509f92011-12-13 14:52:07 +0200140/* returns true if scheduled scan was stopped */
141static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
142{
143 struct ath6kl *ar = vif->ar;
144
145 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
146 return false;
147
148 del_timer_sync(&vif->sched_scan_timer);
149
150 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
151 ATH6KL_HOST_MODE_AWAKE);
152
153 ar->state = ATH6KL_STATE_ON;
154
155 return true;
156}
157
158static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
159{
160 struct ath6kl *ar = vif->ar;
161 bool stopped;
162
163 stopped = __ath6kl_cfg80211_sscan_stop(vif);
164
165 if (!stopped)
166 return;
167
168 cfg80211_sched_scan_stopped(ar->wiphy);
169}
170
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530171static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300172 enum nl80211_wpa_versions wpa_version)
173{
174 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
175
176 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530177 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300178 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530179 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300180 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530181 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300182 } else {
183 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
184 return -ENOTSUPP;
185 }
186
187 return 0;
188}
189
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530190static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300191 enum nl80211_auth_type auth_type)
192{
Kalle Valobdcd8172011-07-18 00:22:30 +0300193 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
194
195 switch (auth_type) {
196 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530197 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300198 break;
199 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530200 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300201 break;
202 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530203 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300204 break;
205
206 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530207 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300208 break;
209
210 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900211 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300212 return -ENOTSUPP;
213 }
214
215 return 0;
216}
217
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530218static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300219{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530220 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
221 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
222 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300223
224 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
225 __func__, cipher, ucast);
226
227 switch (cipher) {
228 case 0:
229 /* our own hack to use value 0 as no crypto used */
230 *ar_cipher = NONE_CRYPT;
231 *ar_cipher_len = 0;
232 break;
233 case WLAN_CIPHER_SUITE_WEP40:
234 *ar_cipher = WEP_CRYPT;
235 *ar_cipher_len = 5;
236 break;
237 case WLAN_CIPHER_SUITE_WEP104:
238 *ar_cipher = WEP_CRYPT;
239 *ar_cipher_len = 13;
240 break;
241 case WLAN_CIPHER_SUITE_TKIP:
242 *ar_cipher = TKIP_CRYPT;
243 *ar_cipher_len = 0;
244 break;
245 case WLAN_CIPHER_SUITE_CCMP:
246 *ar_cipher = AES_CRYPT;
247 *ar_cipher_len = 0;
248 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200249 case WLAN_CIPHER_SUITE_SMS4:
250 *ar_cipher = WAPI_CRYPT;
251 *ar_cipher_len = 0;
252 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300253 default:
254 ath6kl_err("cipher 0x%x not supported\n", cipher);
255 return -ENOTSUPP;
256 }
257
258 return 0;
259}
260
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530261static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300262{
263 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
264
265 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530266 if (vif->auth_mode == WPA_AUTH)
267 vif->auth_mode = WPA_PSK_AUTH;
268 else if (vif->auth_mode == WPA2_AUTH)
269 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300270 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530271 if (vif->auth_mode == WPA_AUTH)
272 vif->auth_mode = WPA_AUTH_CCKM;
273 else if (vif->auth_mode == WPA2_AUTH)
274 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300275 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530276 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300277 }
278}
279
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530280static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300281{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530282 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530283
Kalle Valobdcd8172011-07-18 00:22:30 +0300284 if (!test_bit(WMI_READY, &ar->flag)) {
285 ath6kl_err("wmi is not ready\n");
286 return false;
287 }
288
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530289 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300290 ath6kl_err("wlan disabled\n");
291 return false;
292 }
293
294 return true;
295}
296
Kevin Fang6981ffd2011-10-07 08:51:19 +0800297static bool ath6kl_is_wpa_ie(const u8 *pos)
298{
299 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
300 pos[2] == 0x00 && pos[3] == 0x50 &&
301 pos[4] == 0xf2 && pos[5] == 0x01;
302}
303
304static bool ath6kl_is_rsn_ie(const u8 *pos)
305{
306 return pos[0] == WLAN_EID_RSN;
307}
308
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700309static bool ath6kl_is_wps_ie(const u8 *pos)
310{
311 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
312 pos[1] >= 4 &&
313 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
314 pos[5] == 0x04);
315}
316
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530317static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
318 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800319{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530320 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800321 const u8 *pos;
322 u8 *buf = NULL;
323 size_t len = 0;
324 int ret;
325
326 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700327 * Clear previously set flag
328 */
329
330 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
331
332 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800333 * Filter out RSN/WPA IE(s)
334 */
335
336 if (ies && ies_len) {
337 buf = kmalloc(ies_len, GFP_KERNEL);
338 if (buf == NULL)
339 return -ENOMEM;
340 pos = ies;
341
342 while (pos + 1 < ies + ies_len) {
343 if (pos + 2 + pos[1] > ies + ies_len)
344 break;
345 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
346 memcpy(buf + len, pos, 2 + pos[1]);
347 len += 2 + pos[1];
348 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700349
350 if (ath6kl_is_wps_ie(pos))
351 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
352
Kevin Fang6981ffd2011-10-07 08:51:19 +0800353 pos += 2 + pos[1];
354 }
355 }
356
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530357 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
358 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800359 kfree(buf);
360 return ret;
361}
362
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530363static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
364{
365 switch (type) {
366 case NL80211_IFTYPE_STATION:
367 *nw_type = INFRA_NETWORK;
368 break;
369 case NL80211_IFTYPE_ADHOC:
370 *nw_type = ADHOC_NETWORK;
371 break;
372 case NL80211_IFTYPE_AP:
373 *nw_type = AP_NETWORK;
374 break;
375 case NL80211_IFTYPE_P2P_CLIENT:
376 *nw_type = INFRA_NETWORK;
377 break;
378 case NL80211_IFTYPE_P2P_GO:
379 *nw_type = AP_NETWORK;
380 break;
381 default:
382 ath6kl_err("invalid interface type %u\n", type);
383 return -ENOTSUPP;
384 }
385
386 return 0;
387}
388
389static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
390 u8 *if_idx, u8 *nw_type)
391{
392 int i;
393
394 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
395 return false;
396
397 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200398 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530399 return false;
400
401 if (type == NL80211_IFTYPE_STATION ||
402 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200403 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530404 if ((ar->avail_idx_map >> i) & BIT(0)) {
405 *if_idx = i;
406 return true;
407 }
408 }
409 }
410
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530411 if (type == NL80211_IFTYPE_P2P_CLIENT ||
412 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200413 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530414 if ((ar->avail_idx_map >> i) & BIT(0)) {
415 *if_idx = i;
416 return true;
417 }
418 }
419 }
420
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530421 return false;
422}
423
Kalle Valo8c9bb052012-03-07 20:04:00 +0200424static bool ath6kl_is_tx_pending(struct ath6kl *ar)
425{
426 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
427}
428
429
Kalle Valobdcd8172011-07-18 00:22:30 +0300430static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
431 struct cfg80211_connect_params *sme)
432{
433 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530434 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300435 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800436 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530437 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300438
Kalle Valo10509f92011-12-13 14:52:07 +0200439 ath6kl_cfg80211_sscan_disable(vif);
440
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530441 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300442
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530443 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300444 return -EIO;
445
446 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
447 ath6kl_err("destroy in progress\n");
448 return -EBUSY;
449 }
450
451 if (test_bit(SKIP_SCAN, &ar->flag) &&
452 ((sme->channel && sme->channel->center_freq == 0) ||
453 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
454 ath6kl_err("SkipScan: channel or bssid invalid\n");
455 return -EINVAL;
456 }
457
458 if (down_interruptible(&ar->sem)) {
459 ath6kl_err("busy, couldn't get access\n");
460 return -ERESTARTSYS;
461 }
462
463 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
464 ath6kl_err("busy, destroy in progress\n");
465 up(&ar->sem);
466 return -EBUSY;
467 }
468
469 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
470 /*
471 * sleep until the command queue drains
472 */
473 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200474 ath6kl_is_tx_pending(ar),
475 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300476 if (signal_pending(current)) {
477 ath6kl_err("cmd queue drain timeout\n");
478 up(&ar->sem);
479 return -EINTR;
480 }
481 }
482
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200483 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
484 if (status) {
485 up(&ar->sem);
486 return status;
487 }
488
489 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530490 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800491
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530492 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530493 vif->ssid_len == sme->ssid_len &&
494 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530495 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530496 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
497 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530498 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300499
500 up(&ar->sem);
501 if (status) {
502 ath6kl_err("wmi_reconnect_cmd failed\n");
503 return -EIO;
504 }
505 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530506 } else if (vif->ssid_len == sme->ssid_len &&
507 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530508 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300509 }
510
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530511 memset(vif->ssid, 0, sizeof(vif->ssid));
512 vif->ssid_len = sme->ssid_len;
513 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300514
515 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530516 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300517
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530518 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300519 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530520 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300521
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530522 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300523
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530524 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300525 if (status) {
526 up(&ar->sem);
527 return status;
528 }
529
530 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530531 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300532 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530533 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300534
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530535 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300536
537 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530538 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300539
540 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530541 (vif->auth_mode == NONE_AUTH) &&
542 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300543 struct ath6kl_key *key = NULL;
544
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530545 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300546 ath6kl_err("key index %d out of bounds\n",
547 sme->key_idx);
548 up(&ar->sem);
549 return -ENOENT;
550 }
551
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530552 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300553 key->key_len = sme->key_len;
554 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530555 key->cipher = vif->prwise_crypto;
556 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300557
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530558 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530559 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 GROUP_USAGE | TX_USAGE,
561 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200562 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300563 key->key, KEY_OP_INIT_VAL, NULL,
564 NO_SYNC_WMIFLAG);
565 }
566
567 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530568 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530569 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200570 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300571 ath6kl_err("couldn't set bss filtering\n");
572 up(&ar->sem);
573 return -EIO;
574 }
575 }
576
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530577 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300578
Thomas Pedersenc422d52d2012-05-15 00:09:23 -0700579 /* enable enhanced bmiss detection if applicable */
580 ath6kl_cfg80211_sta_bmiss_enhance(vif, true);
581
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800582 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
583 nw_subtype = SUBTYPE_P2PCLIENT;
584
Kalle Valobdcd8172011-07-18 00:22:30 +0300585 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
586 "%s: connect called with authmode %d dot11 auth %d"
587 " PW crypto %d PW crypto len %d GRP crypto %d"
588 " GRP crypto len %d channel hint %u\n",
589 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530590 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
591 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530592 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300593
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530594 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530595
596 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200597 interval = max_t(u16, vif->listen_intvl_t,
598 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530599 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
600 interval,
601 0);
602 if (status) {
603 ath6kl_err("couldn't set listen intervel\n");
604 up(&ar->sem);
605 return status;
606 }
607 }
608
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530609 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530610 vif->dot11_auth_mode, vif->auth_mode,
611 vif->prwise_crypto,
612 vif->prwise_crypto_len,
613 vif->grp_crypto, vif->grp_crypto_len,
614 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530615 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800616 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300617
Bala Shanmugamf5993592012-03-27 12:17:32 +0530618 /* disable background scan if period is 0 */
619 if (sme->bg_scan_period == 0)
620 sme->bg_scan_period = 0xffff;
621
622 /* configure default value if not specified */
623 if (sme->bg_scan_period == -1)
624 sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
625
626 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
627 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
628
Kalle Valobdcd8172011-07-18 00:22:30 +0300629 up(&ar->sem);
630
631 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530632 memset(vif->ssid, 0, sizeof(vif->ssid));
633 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300634 ath6kl_err("invalid request\n");
635 return -ENOENT;
636 } else if (status) {
637 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
638 return -EIO;
639 }
640
641 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200642 ((vif->auth_mode == WPA_PSK_AUTH) ||
643 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530644 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300645 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
646 }
647
648 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530649 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300650
651 return 0;
652}
653
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530654static struct cfg80211_bss *
655ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
656 enum network_type nw_type,
657 const u8 *bssid,
658 struct ieee80211_channel *chan,
659 const u8 *beacon_ie,
660 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300661{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530662 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300663 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530664 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300665 u8 *ie;
666
Raja Mani4eab6f42011-11-09 17:02:23 +0530667 if (nw_type & ADHOC_NETWORK) {
668 cap_mask = WLAN_CAPABILITY_IBSS;
669 cap_val = WLAN_CAPABILITY_IBSS;
670 } else {
671 cap_mask = WLAN_CAPABILITY_ESS;
672 cap_val = WLAN_CAPABILITY_ESS;
673 }
674
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530675 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530676 vif->ssid, vif->ssid_len,
677 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300678 if (bss == NULL) {
679 /*
680 * Since cfg80211 may not yet know about the BSS,
681 * generate a partial entry until the first BSS info
682 * event becomes available.
683 *
684 * Prepend SSID element since it is not included in the Beacon
685 * IEs from the target.
686 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530687 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300688 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530689 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300690 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530691 ie[1] = vif->ssid_len;
692 memcpy(ie + 2, vif->ssid, vif->ssid_len);
693 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530694 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530695 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530696 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300697 0, GFP_KERNEL);
698 if (bss)
Kalle Valocdeb8602012-04-12 11:02:18 +0300699 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
700 "added bss %pM to cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300701 kfree(ie);
702 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530703 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300704
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530705 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300706}
707
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530708void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300709 u8 *bssid, u16 listen_intvl,
710 u16 beacon_intvl,
711 enum network_type nw_type,
712 u8 beacon_ie_len, u8 assoc_req_len,
713 u8 assoc_resp_len, u8 *assoc_info)
714{
Jouni Malinen01cac472011-09-19 19:14:59 +0300715 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530716 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530717 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300718
719 /* capinfo + listen interval */
720 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
721
722 /* capinfo + status code + associd */
723 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
724
725 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
726 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
727 assoc_resp_ie_offset;
728
729 assoc_req_len -= assoc_req_ie_offset;
730 assoc_resp_len -= assoc_resp_ie_offset;
731
Jouni Malinen32c10872011-09-19 19:15:07 +0300732 /*
733 * Store Beacon interval here; DTIM period will be available only once
734 * a Beacon frame from the AP is seen.
735 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530736 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530737 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300738
Kalle Valobdcd8172011-07-18 00:22:30 +0300739 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530740 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300741 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
742 "%s: ath6k not in ibss mode\n", __func__);
743 return;
744 }
745 }
746
747 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530748 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
749 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300750 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
751 "%s: ath6k not in station mode\n", __func__);
752 return;
753 }
754 }
755
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530756 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300757
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530758 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
759 assoc_info, beacon_ie_len);
760 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530761 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300762 return;
763 }
764
Raja Mani4eab6f42011-11-09 17:02:23 +0530765 if (nw_type & ADHOC_NETWORK) {
766 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
767 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
768 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530769 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300770 return;
771 }
772
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530773 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300774 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530775 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530776 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300777 assoc_req_ie, assoc_req_len,
778 assoc_resp_ie, assoc_resp_len,
779 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530780 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530781 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300782 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530783 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
784 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300785 }
786}
787
788static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
789 struct net_device *dev, u16 reason_code)
790{
Kalle Valod6d5c062011-11-25 13:17:37 +0200791 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530792 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300793
794 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
795 reason_code);
796
Kalle Valo10509f92011-12-13 14:52:07 +0200797 ath6kl_cfg80211_sscan_disable(vif);
798
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530799 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300800 return -EIO;
801
802 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
803 ath6kl_err("busy, destroy in progress\n");
804 return -EBUSY;
805 }
806
807 if (down_interruptible(&ar->sem)) {
808 ath6kl_err("busy, couldn't get access\n");
809 return -ERESTARTSYS;
810 }
811
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530812 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530813 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530814 memset(vif->ssid, 0, sizeof(vif->ssid));
815 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300816
817 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530818 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300819
820 up(&ar->sem);
821
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530822 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530823
Kalle Valobdcd8172011-07-18 00:22:30 +0300824 return 0;
825}
826
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530827void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300828 u8 *bssid, u8 assoc_resp_len,
829 u8 *assoc_info, u16 proto_reason)
830{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530831 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530832
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530833 if (vif->scan_req) {
834 cfg80211_scan_done(vif->scan_req, true);
835 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300836 }
837
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530838 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530839 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300840 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
841 "%s: ath6k not in ibss mode\n", __func__);
842 return;
843 }
844 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530845 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300846 return;
847 }
848
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530849 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530850 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
851 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300852 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
853 "%s: ath6k not in station mode\n", __func__);
854 return;
855 }
856 }
857
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530858 /*
859 * Send a disconnect command to target when a disconnect event is
860 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
861 * request from host) to make the firmware stop trying to connect even
862 * after giving disconnect event. There will be one more disconnect
863 * event for this disconnect command with reason code DISCONNECT_CMD
864 * which will be notified to cfg80211.
865 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300866
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530867 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530868 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300869 return;
870 }
871
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530872 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300873
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530874 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530875 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200876 bssid, NULL, 0,
877 NULL, 0,
878 WLAN_STATUS_UNSPECIFIED_FAILURE,
879 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530880 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530881 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200882 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300883 }
884
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530885 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300886}
887
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300888static int ath6kl_set_probed_ssids(struct ath6kl *ar,
889 struct ath6kl_vif *vif,
890 struct cfg80211_ssid *ssids, int n_ssids)
891{
892 u8 i;
893
Jouni Malinen8ab54152012-05-09 22:14:51 +0300894 if (n_ssids > MAX_PROBED_SSIDS)
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300895 return -EINVAL;
896
897 for (i = 0; i < n_ssids; i++) {
898 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
899 ssids[i].ssid_len ?
900 SPECIFIC_SSID_FLAG : ANY_SSID_FLAG,
901 ssids[i].ssid_len,
902 ssids[i].ssid);
903 }
904
905 /* Make sure no old entries are left behind */
Jouni Malinen8ab54152012-05-09 22:14:51 +0300906 for (i = n_ssids; i < MAX_PROBED_SSIDS; i++) {
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300907 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
908 DISABLE_SSID_FLAG, 0, NULL);
909 }
910
911 return 0;
912}
913
Kalle Valobdcd8172011-07-18 00:22:30 +0300914static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
915 struct cfg80211_scan_request *request)
916{
Kalle Valod6d5c062011-11-25 13:17:37 +0200917 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530918 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300919 s8 n_channels = 0;
920 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300921 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530922 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300923
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530924 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300925 return -EIO;
926
Kalle Valo10509f92011-12-13 14:52:07 +0200927 ath6kl_cfg80211_sscan_disable(vif);
928
Kalle Valobdcd8172011-07-18 00:22:30 +0300929 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530930 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan954e6ce2012-04-25 12:39:06 +0530931 ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
932 ALL_BSS_FILTER, 0);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300933 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300934 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300935 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300936 }
937 }
938
Jouni Malinen3b8ffc62012-04-16 19:28:03 +0300939 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
940 request->n_ssids);
941 if (ret < 0)
942 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300943
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800944 /* this also clears IE in fw if it's not set */
945 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
946 WMI_FRAME_PROBE_REQ,
947 request->ie, request->ie_len);
948 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +0300949 ath6kl_err("failed to set Probe Request appie for scan");
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800950 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300951 }
952
Jouni Malinen11869be2011-09-02 20:07:06 +0300953 /*
954 * Scan only the requested channels if the request specifies a set of
955 * channels. If the list is longer than the target supports, do not
956 * configure the list and instead, scan all available channels.
957 */
958 if (request->n_channels > 0 &&
959 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300960 u8 i;
961
Jouni Malinen11869be2011-09-02 20:07:06 +0300962 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300963
964 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
965 if (channels == NULL) {
Kalle Valocdeb8602012-04-12 11:02:18 +0300966 ath6kl_warn("failed to set scan channels, scan all channels");
Edward Lu1276c9e2011-08-30 21:58:00 +0300967 n_channels = 0;
968 }
969
970 for (i = 0; i < n_channels; i++)
971 channels[i] = request->channels[i]->center_freq;
972 }
973
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530974 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530975 force_fg_scan = 1;
976
Raja Mani5b35dff2012-03-28 18:50:35 +0530977 vif->scan_req = request;
978
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800979 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200980 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800981 /*
982 * If capable of doing P2P mgmt operations using
983 * station interface, send additional information like
984 * supported rates to advertise and xmit rates for
985 * probe requests
986 */
987 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
988 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530989 false, 0,
990 ATH6KL_FG_SCAN_INTERVAL,
991 n_channels, channels,
992 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800993 request->rates);
994 } else {
995 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
996 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530997 false, 0,
998 ATH6KL_FG_SCAN_INTERVAL,
999 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001000 }
Raja Mani5b35dff2012-03-28 18:50:35 +05301001 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001002 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +05301003 vif->scan_req = NULL;
1004 }
Kalle Valobdcd8172011-07-18 00:22:30 +03001005
Edward Lu1276c9e2011-08-30 21:58:00 +03001006 kfree(channels);
1007
Kalle Valobdcd8172011-07-18 00:22:30 +03001008 return ret;
1009}
1010
Kalle Valo1c17d312011-11-01 08:43:56 +02001011void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +03001012{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301013 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001014 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001015
Kalle Valo1c17d312011-11-01 08:43:56 +02001016 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
1017 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +03001018
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301019 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001020 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001021
Kalle Valo1c17d312011-11-01 08:43:56 +02001022 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001023 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001024
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301025 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1026 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301027 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1028 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001029 0, NULL);
1030 }
1031 }
1032
1033out:
Kalle Valocb938212011-10-27 18:47:46 +03001034 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301035 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001036}
1037
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001038void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
1039 enum wmi_phy_mode mode)
1040{
1041 enum nl80211_channel_type type;
1042
1043 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1044 "channel switch notify nw_type %d freq %d mode %d\n",
1045 vif->nw_type, freq, mode);
1046
1047 type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT;
1048
1049 cfg80211_ch_switch_notify(vif->ndev, freq, type);
1050}
1051
Kalle Valobdcd8172011-07-18 00:22:30 +03001052static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1053 u8 key_index, bool pairwise,
1054 const u8 *mac_addr,
1055 struct key_params *params)
1056{
Kalle Valod6d5c062011-11-25 13:17:37 +02001057 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301058 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001059 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301060 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001061 u8 key_usage;
1062 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001063
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301064 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001065 return -EIO;
1066
Jouni Malinen837cb972011-10-11 17:31:57 +03001067 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1068 if (params->key_len != WMI_KRK_LEN)
1069 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301070 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1071 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001072 }
1073
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301074 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001075 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1076 "%s: key index %d out of bounds\n", __func__,
1077 key_index);
1078 return -ENOENT;
1079 }
1080
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301081 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001082 memset(key, 0, sizeof(struct ath6kl_key));
1083
1084 if (pairwise)
1085 key_usage = PAIRWISE_USAGE;
1086 else
1087 key_usage = GROUP_USAGE;
1088
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301089 seq_len = params->seq_len;
1090 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1091 seq_len > ATH6KL_KEY_SEQ_LEN) {
1092 /* Only first half of the WPI PN is configured */
1093 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001094 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301095 if (params->key_len > WLAN_MAX_KEY_LEN ||
1096 seq_len > sizeof(key->seq))
1097 return -EINVAL;
1098
1099 key->key_len = params->key_len;
1100 memcpy(key->key, params->key, key->key_len);
1101 key->seq_len = seq_len;
1102 memcpy(key->seq, params->seq, key->seq_len);
1103 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001104
1105 switch (key->cipher) {
1106 case WLAN_CIPHER_SUITE_WEP40:
1107 case WLAN_CIPHER_SUITE_WEP104:
1108 key_type = WEP_CRYPT;
1109 break;
1110
1111 case WLAN_CIPHER_SUITE_TKIP:
1112 key_type = TKIP_CRYPT;
1113 break;
1114
1115 case WLAN_CIPHER_SUITE_CCMP:
1116 key_type = AES_CRYPT;
1117 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001118 case WLAN_CIPHER_SUITE_SMS4:
1119 key_type = WAPI_CRYPT;
1120 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001121
1122 default:
1123 return -ENOTSUPP;
1124 }
1125
Kalle Valoddc3d772012-03-07 20:03:58 +02001126 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1127 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1128 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301129 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001130
1131 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1132 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1133 __func__, key_index, key->key_len, key_type,
1134 key_usage, key->seq_len);
1135
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301136 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001137 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301138 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001139 ar->ap_mode_bkey.valid = true;
1140 ar->ap_mode_bkey.key_index = key_index;
1141 ar->ap_mode_bkey.key_type = key_type;
1142 ar->ap_mode_bkey.key_len = key->key_len;
1143 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301144 if (!test_bit(CONNECTED, &vif->flags)) {
Kalle Valocdeb8602012-04-12 11:02:18 +03001145 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1146 "Delay initial group key configuration until AP mode has been started\n");
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001147 /*
1148 * The key will be set in ath6kl_connect_ap_mode() once
1149 * the connected event is received from the target.
1150 */
1151 return 0;
1152 }
1153 }
1154
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301155 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301156 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001157 /*
1158 * Store the key locally so that it can be re-configured after
1159 * the AP mode has properly started
1160 * (ath6kl_install_statioc_wep_keys).
1161 */
Kalle Valocdeb8602012-04-12 11:02:18 +03001162 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1163 "Delay WEP key configuration until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301164 vif->wep_key_list[key_index].key_len = key->key_len;
1165 memcpy(vif->wep_key_list[key_index].key, key->key,
1166 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001167 return 0;
1168 }
1169
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301170 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001171 key_type, key_usage, key->key_len,
1172 key->seq, key->seq_len, key->key,
1173 KEY_OP_INIT_VAL,
1174 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001175}
1176
1177static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1178 u8 key_index, bool pairwise,
1179 const u8 *mac_addr)
1180{
Kalle Valod6d5c062011-11-25 13:17:37 +02001181 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301182 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001183
1184 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1185
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301186 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001187 return -EIO;
1188
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301189 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001190 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1191 "%s: key index %d out of bounds\n", __func__,
1192 key_index);
1193 return -ENOENT;
1194 }
1195
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301196 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001197 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1198 "%s: index %d is empty\n", __func__, key_index);
1199 return 0;
1200 }
1201
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301202 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001203
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301204 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001205}
1206
1207static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1208 u8 key_index, bool pairwise,
1209 const u8 *mac_addr, void *cookie,
1210 void (*callback) (void *cookie,
1211 struct key_params *))
1212{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301213 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001214 struct ath6kl_key *key = NULL;
1215 struct key_params params;
1216
1217 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1218
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301219 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001220 return -EIO;
1221
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301222 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001223 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1224 "%s: key index %d out of bounds\n", __func__,
1225 key_index);
1226 return -ENOENT;
1227 }
1228
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301229 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001230 memset(&params, 0, sizeof(params));
1231 params.cipher = key->cipher;
1232 params.key_len = key->key_len;
1233 params.seq_len = key->seq_len;
1234 params.seq = key->seq;
1235 params.key = key->key;
1236
1237 callback(cookie, &params);
1238
1239 return key->key_len ? 0 : -ENOENT;
1240}
1241
1242static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1243 struct net_device *ndev,
1244 u8 key_index, bool unicast,
1245 bool multicast)
1246{
Kalle Valod6d5c062011-11-25 13:17:37 +02001247 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301248 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001249 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001250 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001251 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001252
1253 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1254
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301255 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001256 return -EIO;
1257
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301258 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001259 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1260 "%s: key index %d out of bounds\n",
1261 __func__, key_index);
1262 return -ENOENT;
1263 }
1264
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301265 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001266 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1267 __func__, key_index);
1268 return -EINVAL;
1269 }
1270
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301271 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301272 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001273 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301274 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001275 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001276 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301277 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001278 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301279 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001280
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301281 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001282 return 0; /* Delay until AP mode has been started */
1283
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001284 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1285 vif->def_txkey_index,
1286 key_type, key_usage,
1287 key->key_len, key->seq, key->seq_len,
1288 key->key,
1289 KEY_OP_INIT_VAL, NULL,
1290 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001291}
1292
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301293void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001294 bool ismcast)
1295{
1296 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1297 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1298
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301299 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001300 (ismcast ? NL80211_KEYTYPE_GROUP :
1301 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1302 GFP_KERNEL);
1303}
1304
1305static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1306{
1307 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301308 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001309 int ret;
1310
1311 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1312 changed);
1313
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301314 vif = ath6kl_vif_first(ar);
1315 if (!vif)
1316 return -EIO;
1317
1318 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001319 return -EIO;
1320
1321 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1322 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1323 if (ret != 0) {
1324 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1325 return -EIO;
1326 }
1327 }
1328
1329 return 0;
1330}
1331
1332/*
1333 * The type nl80211_tx_power_setting replaces the following
1334 * data type from 2.6.36 onwards
1335*/
1336static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1337 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001338 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001339{
1340 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301341 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001342 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001343
1344 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1345 type, dbm);
1346
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301347 vif = ath6kl_vif_first(ar);
1348 if (!vif)
1349 return -EIO;
1350
1351 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001352 return -EIO;
1353
1354 switch (type) {
1355 case NL80211_TX_POWER_AUTOMATIC:
1356 return 0;
1357 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001358 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001359 break;
1360 default:
1361 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1362 __func__, type);
1363 return -EOPNOTSUPP;
1364 }
1365
Kalle Valod0d670a2012-03-07 20:03:58 +02001366 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001367
1368 return 0;
1369}
1370
1371static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1372{
1373 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301374 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001375
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301376 vif = ath6kl_vif_first(ar);
1377 if (!vif)
1378 return -EIO;
1379
1380 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001381 return -EIO;
1382
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301383 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001384 ar->tx_pwr = 0;
1385
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301386 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001387 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1388 return -EIO;
1389 }
1390
1391 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1392 5 * HZ);
1393
1394 if (signal_pending(current)) {
1395 ath6kl_err("target did not respond\n");
1396 return -EINTR;
1397 }
1398 }
1399
1400 *dbm = ar->tx_pwr;
1401 return 0;
1402}
1403
1404static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1405 struct net_device *dev,
1406 bool pmgmt, int timeout)
1407{
1408 struct ath6kl *ar = ath6kl_priv(dev);
1409 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301410 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001411
1412 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1413 __func__, pmgmt, timeout);
1414
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301415 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001416 return -EIO;
1417
1418 if (pmgmt) {
1419 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1420 mode.pwr_mode = REC_POWER;
1421 } else {
1422 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1423 mode.pwr_mode = MAX_PERF_POWER;
1424 }
1425
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301426 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001427 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001428 ath6kl_err("wmi_powermode_cmd failed\n");
1429 return -EIO;
1430 }
1431
1432 return 0;
1433}
1434
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301435static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1436 char *name,
1437 enum nl80211_iftype type,
1438 u32 *flags,
1439 struct vif_params *params)
1440{
1441 struct ath6kl *ar = wiphy_priv(wiphy);
1442 struct net_device *ndev;
1443 u8 if_idx, nw_type;
1444
Kalle Valo71f96ee2011-11-14 19:31:30 +02001445 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301446 ath6kl_err("Reached maximum number of supported vif\n");
1447 return ERR_PTR(-EINVAL);
1448 }
1449
1450 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1451 ath6kl_err("Not a supported interface type\n");
1452 return ERR_PTR(-EINVAL);
1453 }
1454
1455 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1456 if (!ndev)
1457 return ERR_PTR(-ENOMEM);
1458
1459 ar->num_vif++;
1460
1461 return ndev;
1462}
1463
1464static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1465 struct net_device *ndev)
1466{
1467 struct ath6kl *ar = wiphy_priv(wiphy);
1468 struct ath6kl_vif *vif = netdev_priv(ndev);
1469
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301470 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301471 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301472 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301473
1474 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1475
Kalle Valoc25889e2012-01-17 20:08:27 +02001476 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301477
1478 return 0;
1479}
1480
Kalle Valobdcd8172011-07-18 00:22:30 +03001481static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1482 struct net_device *ndev,
1483 enum nl80211_iftype type, u32 *flags,
1484 struct vif_params *params)
1485{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301486 struct ath6kl_vif *vif = netdev_priv(ndev);
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301487 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001488
1489 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1490
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301491 /*
1492 * Don't bring up p2p on an interface which is not initialized
1493 * for p2p operation where fw does not have capability to switch
1494 * dynamically between non-p2p and p2p type interface.
1495 */
1496 if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
1497 vif->ar->fw_capabilities) &&
1498 (type == NL80211_IFTYPE_P2P_CLIENT ||
1499 type == NL80211_IFTYPE_P2P_GO)) {
1500 if (vif->ar->vif_max == 1) {
1501 if (vif->fw_vif_idx != 0)
1502 return -EINVAL;
1503 else
1504 goto set_iface_type;
1505 }
1506
1507 for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
1508 if (i == vif->fw_vif_idx)
1509 break;
1510 }
1511
1512 if (i == vif->ar->vif_max) {
1513 ath6kl_err("Invalid interface to bring up P2P\n");
1514 return -EINVAL;
1515 }
1516 }
1517
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07001518 /* need to clean up enhanced bmiss detection fw state */
1519 ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
1520
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301521set_iface_type:
Kalle Valobdcd8172011-07-18 00:22:30 +03001522 switch (type) {
1523 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301524 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001525 break;
1526 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301527 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001528 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001529 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301530 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001531 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001532 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301533 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001534 break;
1535 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301536 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001537 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001538 default:
1539 ath6kl_err("invalid interface type %u\n", type);
1540 return -EOPNOTSUPP;
1541 }
1542
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301543 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001544
1545 return 0;
1546}
1547
1548static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1549 struct net_device *dev,
1550 struct cfg80211_ibss_params *ibss_param)
1551{
1552 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301553 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001554 int status;
1555
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301556 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001557 return -EIO;
1558
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301559 vif->ssid_len = ibss_param->ssid_len;
1560 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001561
1562 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301563 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001564
1565 if (ibss_param->channel_fixed) {
1566 /*
1567 * TODO: channel_fixed: The channel should be fixed, do not
1568 * search for IBSSs to join on other channels. Target
1569 * firmware does not support this feature, needs to be
1570 * updated.
1571 */
1572 return -EOPNOTSUPP;
1573 }
1574
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301575 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001576 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301577 memcpy(vif->req_bssid, ibss_param->bssid,
1578 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001579
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301580 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001581
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301582 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001583 if (status)
1584 return status;
1585
1586 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301587 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1588 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001589 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301590 ath6kl_set_cipher(vif, 0, true);
1591 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001592 }
1593
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301594 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001595
1596 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1597 "%s: connect called with authmode %d dot11 auth %d"
1598 " PW crypto %d PW crypto len %d GRP crypto %d"
1599 " GRP crypto len %d channel hint %u\n",
1600 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301601 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1602 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301603 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001604
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301605 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301606 vif->dot11_auth_mode, vif->auth_mode,
1607 vif->prwise_crypto,
1608 vif->prwise_crypto_len,
1609 vif->grp_crypto, vif->grp_crypto_len,
1610 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301611 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001612 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301613 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001614
1615 return 0;
1616}
1617
1618static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1619 struct net_device *dev)
1620{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301621 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001622
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301623 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001624 return -EIO;
1625
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301626 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301627 memset(vif->ssid, 0, sizeof(vif->ssid));
1628 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001629
1630 return 0;
1631}
1632
1633static const u32 cipher_suites[] = {
1634 WLAN_CIPHER_SUITE_WEP40,
1635 WLAN_CIPHER_SUITE_WEP104,
1636 WLAN_CIPHER_SUITE_TKIP,
1637 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001638 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001639 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001640};
1641
1642static bool is_rate_legacy(s32 rate)
1643{
1644 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1645 6000, 9000, 12000, 18000, 24000,
1646 36000, 48000, 54000
1647 };
1648 u8 i;
1649
1650 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1651 if (rate == legacy[i])
1652 return true;
1653
1654 return false;
1655}
1656
1657static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1658{
1659 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1660 52000, 58500, 65000, 72200
1661 };
1662 u8 i;
1663
1664 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1665 if (rate == ht20[i]) {
1666 if (i == ARRAY_SIZE(ht20) - 1)
1667 /* last rate uses sgi */
1668 *sgi = true;
1669 else
1670 *sgi = false;
1671
1672 *mcs = i;
1673 return true;
1674 }
1675 }
1676 return false;
1677}
1678
1679static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1680{
1681 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1682 81000, 108000, 121500, 135000,
1683 150000
1684 };
1685 u8 i;
1686
1687 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1688 if (rate == ht40[i]) {
1689 if (i == ARRAY_SIZE(ht40) - 1)
1690 /* last rate uses sgi */
1691 *sgi = true;
1692 else
1693 *sgi = false;
1694
1695 *mcs = i;
1696 return true;
1697 }
1698 }
1699
1700 return false;
1701}
1702
1703static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1704 u8 *mac, struct station_info *sinfo)
1705{
1706 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301707 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001708 long left;
1709 bool sgi;
1710 s32 rate;
1711 int ret;
1712 u8 mcs;
1713
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301714 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001715 return -ENOENT;
1716
1717 if (down_interruptible(&ar->sem))
1718 return -EBUSY;
1719
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301720 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001721
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301722 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001723
1724 if (ret != 0) {
1725 up(&ar->sem);
1726 return -EIO;
1727 }
1728
1729 left = wait_event_interruptible_timeout(ar->event_wq,
1730 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301731 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001732 WMI_TIMEOUT);
1733
1734 up(&ar->sem);
1735
1736 if (left == 0)
1737 return -ETIMEDOUT;
1738 else if (left < 0)
1739 return left;
1740
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301741 if (vif->target_stats.rx_byte) {
1742 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001743 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301744 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001745 sinfo->filled |= STATION_INFO_RX_PACKETS;
1746 }
1747
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301748 if (vif->target_stats.tx_byte) {
1749 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001750 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301751 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001752 sinfo->filled |= STATION_INFO_TX_PACKETS;
1753 }
1754
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301755 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001756 sinfo->filled |= STATION_INFO_SIGNAL;
1757
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301758 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001759
1760 if (is_rate_legacy(rate)) {
1761 sinfo->txrate.legacy = rate / 100;
1762 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1763 if (sgi) {
1764 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1765 sinfo->txrate.mcs = mcs - 1;
1766 } else {
1767 sinfo->txrate.mcs = mcs;
1768 }
1769
1770 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1771 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1772 if (sgi) {
1773 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1774 sinfo->txrate.mcs = mcs - 1;
1775 } else {
1776 sinfo->txrate.mcs = mcs;
1777 }
1778
1779 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1780 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1781 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001782 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1783 "invalid rate from stats: %d\n", rate);
1784 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001785 return 0;
1786 }
1787
1788 sinfo->filled |= STATION_INFO_TX_BITRATE;
1789
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301790 if (test_bit(CONNECTED, &vif->flags) &&
1791 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301792 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001793 sinfo->filled |= STATION_INFO_BSS_PARAM;
1794 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301795 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1796 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001797 }
1798
Kalle Valobdcd8172011-07-18 00:22:30 +03001799 return 0;
1800}
1801
1802static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1803 struct cfg80211_pmksa *pmksa)
1804{
1805 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301806 struct ath6kl_vif *vif = netdev_priv(netdev);
1807
1808 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001809 pmksa->pmkid, true);
1810}
1811
1812static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1813 struct cfg80211_pmksa *pmksa)
1814{
1815 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301816 struct ath6kl_vif *vif = netdev_priv(netdev);
1817
1818 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001819 pmksa->pmkid, false);
1820}
1821
1822static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1823{
1824 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301825 struct ath6kl_vif *vif = netdev_priv(netdev);
1826
1827 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301828 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1829 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001830 return 0;
1831}
1832
Raja Manid91e8ee2012-01-30 17:13:10 +05301833static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1834 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001835{
Raja Manid91e8ee2012-01-30 17:13:10 +05301836 int ret, pos;
1837 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001838 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001839
Raja Manid91e8ee2012-01-30 17:13:10 +05301840 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001841 for (i = 0; i < wow->n_patterns; i++) {
1842
1843 /*
1844 * Convert given nl80211 specific mask value to equivalent
1845 * driver specific mask value and send it to the chip along
1846 * with patterns. For example, If the mask value defined in
1847 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1848 * then equivalent driver specific mask value is
1849 * "0xFF 0x00 0xFF 0x00".
1850 */
1851 memset(&mask, 0, sizeof(mask));
1852 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1853 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1854 mask[pos] = 0xFF;
1855 }
1856 /*
1857 * Note: Pattern's offset is not passed as part of wowlan
1858 * parameter from CFG layer. So it's always passed as ZERO
1859 * to the firmware. It means, given WOW patterns are always
1860 * matched from the first byte of received pkt in the firmware.
1861 */
1862 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301863 vif->fw_vif_idx, WOW_LIST_ID,
1864 wow->patterns[i].pattern_len,
1865 0 /* pattern offset */,
1866 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001867 if (ret)
1868 return ret;
1869 }
1870
Raja Manid91e8ee2012-01-30 17:13:10 +05301871 if (wow->disconnect)
1872 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1873
1874 if (wow->magic_pkt)
1875 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1876
1877 if (wow->gtk_rekey_failure)
1878 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1879
1880 if (wow->eap_identity_req)
1881 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1882
1883 if (wow->four_way_handshake)
1884 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1885
1886 return 0;
1887}
1888
1889static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1890{
1891 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1892 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1894 0x00, 0x08 };
1895 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x7f };
1899 u8 unicst_offset = 0;
1900 static const u8 arp_pattern[] = { 0x08, 0x06 };
1901 static const u8 arp_mask[] = { 0xff, 0xff };
1902 u8 arp_offset = 20;
1903 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1904 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1905 u8 discvr_offset = 38;
1906 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1907 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1911 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1912 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1913 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1917 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1918 u8 dhcp_offset = 0;
1919 int ret;
1920
1921 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1922 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1923 vif->fw_vif_idx, WOW_LIST_ID,
1924 sizeof(unicst_pattern), unicst_offset,
1925 unicst_pattern, unicst_mask);
1926 if (ret) {
1927 ath6kl_err("failed to add WOW unicast IP pattern\n");
1928 return ret;
1929 }
1930
1931 /* Setup all ARP pkt pattern */
1932 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1933 vif->fw_vif_idx, WOW_LIST_ID,
1934 sizeof(arp_pattern), arp_offset,
1935 arp_pattern, arp_mask);
1936 if (ret) {
1937 ath6kl_err("failed to add WOW ARP pattern\n");
1938 return ret;
1939 }
1940
1941 /*
1942 * Setup multicast pattern for mDNS 224.0.0.251,
1943 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1944 */
1945 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1946 vif->fw_vif_idx, WOW_LIST_ID,
1947 sizeof(discvr_pattern), discvr_offset,
1948 discvr_pattern, discvr_mask);
1949 if (ret) {
1950 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1951 return ret;
1952 }
1953
1954 /* Setup all DHCP broadcast pkt pattern */
1955 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1956 vif->fw_vif_idx, WOW_LIST_ID,
1957 sizeof(dhcp_pattern), dhcp_offset,
1958 dhcp_pattern, dhcp_mask);
1959 if (ret) {
1960 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1961 return ret;
1962 }
1963
1964 return 0;
1965}
1966
1967static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1968{
1969 struct net_device *ndev = vif->ndev;
1970 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1971 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1972 u8 discvr_offset = 38;
1973 u8 mac_mask[ETH_ALEN];
1974 int ret;
1975
1976 /* Setup unicast pkt pattern */
1977 memset(mac_mask, 0xff, ETH_ALEN);
1978 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1979 vif->fw_vif_idx, WOW_LIST_ID,
1980 ETH_ALEN, 0, ndev->dev_addr,
1981 mac_mask);
1982 if (ret) {
1983 ath6kl_err("failed to add WOW unicast pattern\n");
1984 return ret;
1985 }
1986
1987 /*
1988 * Setup multicast pattern for mDNS 224.0.0.251,
1989 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1990 */
1991 if ((ndev->flags & IFF_ALLMULTI) ||
1992 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1993 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1994 vif->fw_vif_idx, WOW_LIST_ID,
1995 sizeof(discvr_pattern), discvr_offset,
1996 discvr_pattern, discvr_mask);
1997 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03001998 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
Raja Manid91e8ee2012-01-30 17:13:10 +05301999 return ret;
2000 }
2001 }
2002
2003 return 0;
2004}
2005
Raja Mani055bde42012-03-21 15:03:37 +05302006static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
2007{
2008 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2009}
2010
2011static bool is_ctrl_ep_empty(struct ath6kl *ar)
2012{
2013 return !ar->tx_pending[ar->ctrl_ep];
2014}
2015
2016static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
2017{
2018 int ret, left;
2019
2020 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2021
2022 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2023 ATH6KL_HOST_MODE_ASLEEP);
2024 if (ret)
2025 return ret;
2026
2027 left = wait_event_interruptible_timeout(ar->event_wq,
2028 is_hsleep_mode_procsed(vif),
2029 WMI_TIMEOUT);
2030 if (left == 0) {
2031 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
2032 ret = -ETIMEDOUT;
2033 } else if (left < 0) {
2034 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
2035 left);
2036 ret = left;
2037 }
2038
2039 if (ar->tx_pending[ar->ctrl_ep]) {
2040 left = wait_event_interruptible_timeout(ar->event_wq,
2041 is_ctrl_ep_empty(ar),
2042 WMI_TIMEOUT);
2043 if (left == 0) {
2044 ath6kl_warn("clear wmi ctrl data timeout\n");
2045 ret = -ETIMEDOUT;
2046 } else if (left < 0) {
2047 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2048 ret = left;
2049 }
2050 }
2051
2052 return ret;
2053}
2054
Raja Manid91e8ee2012-01-30 17:13:10 +05302055static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2056{
2057 struct in_device *in_dev;
2058 struct in_ifaddr *ifa;
2059 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05302060 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05302061 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05302062 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302063 u8 index = 0;
2064 __be32 ips[MAX_IP_ADDRS];
2065
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07002066 /* The FW currently can't support multi-vif WoW properly. */
2067 if (ar->num_vif > 1)
2068 return -EIO;
2069
Raja Manid91e8ee2012-01-30 17:13:10 +05302070 vif = ath6kl_vif_first(ar);
2071 if (!vif)
2072 return -EIO;
2073
2074 if (!ath6kl_cfg80211_ready(vif))
2075 return -EIO;
2076
2077 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302078 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302079
2080 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2081 return -EINVAL;
2082
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002083 if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
2084 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2085 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002086 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2087 vif->fw_vif_idx, false);
2088 if (ret)
2089 return ret;
2090 }
2091
Raja Manid91e8ee2012-01-30 17:13:10 +05302092 /* Clear existing WOW patterns */
2093 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2094 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2095 WOW_LIST_ID, i);
2096
2097 /*
2098 * Skip the default WOW pattern configuration
2099 * if the driver receives any WOW patterns from
2100 * the user.
2101 */
2102 if (wow)
2103 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2104 else if (vif->nw_type == AP_NETWORK)
2105 ret = ath6kl_wow_ap(ar, vif);
2106 else
2107 ret = ath6kl_wow_sta(ar, vif);
2108
2109 if (ret)
2110 return ret;
2111
Raja Mani390a8c82012-03-07 11:35:04 +05302112 netif_stop_queue(vif->ndev);
2113
Raja Manice0dc0c2012-02-20 19:08:08 +05302114 if (vif->nw_type != AP_NETWORK) {
2115 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2116 ATH6KL_MAX_WOW_LISTEN_INTL,
2117 0);
2118 if (ret)
2119 return ret;
2120
2121 /* Set listen interval x 15 times as bmiss time */
2122 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2123 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2124 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2125
2126 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2127 bmiss_time, 0);
2128 if (ret)
2129 return ret;
2130
2131 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2132 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2133 0, 0, 0, 0);
2134 if (ret)
2135 return ret;
2136 }
2137
Raja Mani390a8c82012-03-07 11:35:04 +05302138 ar->state = ATH6KL_STATE_SUSPENDING;
2139
Raja Manic08631c2011-12-16 14:24:24 +05302140 /* Setup own IP addr for ARP agent. */
2141 in_dev = __in_dev_get_rtnl(vif->ndev);
2142 if (!in_dev)
2143 goto skip_arp;
2144
2145 ifa = in_dev->ifa_list;
2146 memset(&ips, 0, sizeof(ips));
2147
2148 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2149 while (index < MAX_IP_ADDRS && ifa) {
2150 ips[index] = ifa->ifa_local;
2151 ifa = ifa->ifa_next;
2152 index++;
2153 }
2154
2155 if (ifa) {
2156 ath6kl_err("total IP addr count is exceeding fw limit\n");
2157 return -EINVAL;
2158 }
2159
2160 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2161 if (ret) {
2162 ath6kl_err("fail to setup ip for arp agent\n");
2163 return ret;
2164 }
2165
2166skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002167 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2168 ATH6KL_WOW_MODE_ENABLE,
2169 filter,
2170 WOW_HOST_REQ_DELAY);
2171 if (ret)
2172 return ret;
2173
Raja Mani055bde42012-03-21 15:03:37 +05302174 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002175 if (ret)
2176 return ret;
2177
Raja Mani055bde42012-03-21 15:03:37 +05302178 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002179}
2180
2181static int ath6kl_wow_resume(struct ath6kl *ar)
2182{
2183 struct ath6kl_vif *vif;
2184 int ret;
2185
2186 vif = ath6kl_vif_first(ar);
2187 if (!vif)
2188 return -EIO;
2189
Raja Mani390a8c82012-03-07 11:35:04 +05302190 ar->state = ATH6KL_STATE_RESUMING;
2191
Raja Mani6cb3c712011-11-07 22:52:45 +02002192 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2193 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302194 if (ret) {
Kalle Valocdeb8602012-04-12 11:02:18 +03002195 ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
2196 ret);
Raja Mani390a8c82012-03-07 11:35:04 +05302197 ar->state = ATH6KL_STATE_WOW;
2198 return ret;
2199 }
2200
Raja Manice0dc0c2012-02-20 19:08:08 +05302201 if (vif->nw_type != AP_NETWORK) {
2202 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2203 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2204 if (ret)
2205 return ret;
2206
2207 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2208 vif->listen_intvl_t, 0);
2209 if (ret)
2210 return ret;
2211
2212 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2213 vif->bmiss_time_t, 0);
2214 if (ret)
2215 return ret;
2216 }
2217
Raja Mani390a8c82012-03-07 11:35:04 +05302218 ar->state = ATH6KL_STATE_ON;
2219
Naveen Gangadharan6821d4f2012-05-11 14:19:09 -07002220 if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
2221 test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
2222 ar->fw_capabilities)) {
Naveen Gangadharan6251d802012-04-20 12:46:56 -07002223 ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
2224 vif->fw_vif_idx, true);
2225 if (ret)
2226 return ret;
2227 }
2228
Raja Mani390a8c82012-03-07 11:35:04 +05302229 netif_wake_queue(vif->ndev);
2230
2231 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002232}
2233
Raja Mani40abc2d2012-03-21 15:03:38 +05302234static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2235{
2236 struct ath6kl_vif *vif;
2237 int ret;
2238
2239 vif = ath6kl_vif_first(ar);
2240 if (!vif)
2241 return -EIO;
2242
Ming Jiang48f27582012-04-13 21:09:25 +08002243 if (!test_bit(WMI_READY, &ar->flag)) {
2244 ath6kl_err("deepsleep failed as wmi is not ready\n");
Raja Mani40abc2d2012-03-21 15:03:38 +05302245 return -EIO;
Ming Jiang48f27582012-04-13 21:09:25 +08002246 }
Raja Mani40abc2d2012-03-21 15:03:38 +05302247
2248 ath6kl_cfg80211_stop_all(ar);
2249
2250 /* Save the current power mode before enabling power save */
2251 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2252
2253 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2254 if (ret)
2255 return ret;
2256
2257 /* Disable WOW mode */
2258 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2259 ATH6KL_WOW_MODE_DISABLE,
2260 0, 0);
2261 if (ret)
2262 return ret;
2263
2264 /* Flush all non control pkts in TX path */
2265 ath6kl_tx_data_cleanup(ar);
2266
2267 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2268 if (ret)
2269 return ret;
2270
2271 return 0;
2272}
2273
2274static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2275{
2276 struct ath6kl_vif *vif;
2277 int ret;
2278
2279 vif = ath6kl_vif_first(ar);
2280
2281 if (!vif)
2282 return -EIO;
2283
2284 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2285 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2286 ar->wmi->saved_pwr_mode);
2287 if (ret)
2288 return ret;
2289 }
2290
2291 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2292 ATH6KL_HOST_MODE_AWAKE);
2293 if (ret)
2294 return ret;
2295
2296 ar->state = ATH6KL_STATE_ON;
2297
2298 /* Reset scan parameter to default values */
2299 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2300 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2301 if (ret)
2302 return ret;
2303
2304 return 0;
2305}
2306
Kalle Valo52d81a62011-11-01 08:44:21 +02002307int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002308 enum ath6kl_cfg_suspend_mode mode,
2309 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002310{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302311 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302312 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002313 int ret;
2314
Kalle Valo52d81a62011-11-01 08:44:21 +02002315 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002316 case ATH6KL_CFG_SUSPEND_WOW:
2317
2318 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2319
2320 /* Flush all non control pkts in TX path */
2321 ath6kl_tx_data_cleanup(ar);
2322
Raja Mani390a8c82012-03-07 11:35:04 +05302323 prev_state = ar->state;
2324
Raja Manid7c44e02011-11-07 22:52:46 +02002325 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302326 if (ret) {
2327 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002328 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302329 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302330
Raja Manid7c44e02011-11-07 22:52:46 +02002331 ar->state = ATH6KL_STATE_WOW;
2332 break;
2333
Kalle Valo52d81a62011-11-01 08:44:21 +02002334 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002335
Raja Mani40abc2d2012-03-21 15:03:38 +05302336 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002337
Raja Mani40abc2d2012-03-21 15:03:38 +05302338 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002339 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302340 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2341 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002342 }
2343
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002344 ar->state = ATH6KL_STATE_DEEPSLEEP;
2345
Kalle Valo52d81a62011-11-01 08:44:21 +02002346 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002347
2348 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002349
Kalle Valo7125f012011-12-13 14:51:37 +02002350 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002351
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002352 if (ar->state == ATH6KL_STATE_OFF) {
2353 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2354 "suspend hw off, no action for cutpower\n");
2355 break;
2356 }
2357
2358 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2359
2360 ret = ath6kl_init_hw_stop(ar);
2361 if (ret) {
2362 ath6kl_warn("failed to stop hw during suspend: %d\n",
2363 ret);
2364 }
2365
2366 ar->state = ATH6KL_STATE_CUTPOWER;
2367
2368 break;
2369
Kalle Valo10509f92011-12-13 14:52:07 +02002370 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2371 /*
2372 * Nothing needed for schedule scan, firmware is already in
2373 * wow mode and sleeping most of the time.
2374 */
2375 break;
2376
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002377 default:
2378 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002379 }
2380
Vivek Natarajan3d794992012-03-28 19:21:26 +05302381 list_for_each_entry(vif, &ar->vif_list, list)
2382 ath6kl_cfg80211_scan_complete_event(vif, true);
2383
Kalle Valo52d81a62011-11-01 08:44:21 +02002384 return 0;
2385}
Kalle Valod6a434d2012-01-17 20:09:36 +02002386EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002387
2388int ath6kl_cfg80211_resume(struct ath6kl *ar)
2389{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002390 int ret;
2391
2392 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002393 case ATH6KL_STATE_WOW:
2394 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2395
2396 ret = ath6kl_wow_resume(ar);
2397 if (ret) {
2398 ath6kl_warn("wow mode resume failed: %d\n", ret);
2399 return ret;
2400 }
2401
Raja Manid7c44e02011-11-07 22:52:46 +02002402 break;
2403
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002404 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302405 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2406
2407 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2408 if (ret) {
2409 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2410 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002411 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002412 break;
2413
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002414 case ATH6KL_STATE_CUTPOWER:
2415 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2416
2417 ret = ath6kl_init_hw_start(ar);
2418 if (ret) {
2419 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2420 return ret;
2421 }
Raja Manid7c44e02011-11-07 22:52:46 +02002422 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002423
Kalle Valo10509f92011-12-13 14:52:07 +02002424 case ATH6KL_STATE_SCHED_SCAN:
2425 break;
2426
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002427 default:
2428 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002429 }
2430
2431 return 0;
2432}
Kalle Valod6a434d2012-01-17 20:09:36 +02002433EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002434
Kalle Valoabcb3442011-07-22 08:26:20 +03002435#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002436
2437/* hif layer decides what suspend mode to use */
2438static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002439 struct cfg80211_wowlan *wow)
2440{
2441 struct ath6kl *ar = wiphy_priv(wiphy);
2442
Raja Mani0f60e9f2011-11-07 22:52:45 +02002443 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002444}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002445
Kalle Valo52d81a62011-11-01 08:44:21 +02002446static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002447{
2448 struct ath6kl *ar = wiphy_priv(wiphy);
2449
2450 return ath6kl_hif_resume(ar);
2451}
Raja Mania918fb32011-11-07 22:52:46 +02002452
2453/*
2454 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2455 * both sdio irq wake up and keep power. The target pulls sdio data line to
2456 * wake up the host when WOW pattern matches. This causes sdio irq handler
2457 * is being called in the host side which internally hits ath6kl's RX path.
2458 *
2459 * Since sdio interrupt is not disabled, RX path executes even before
2460 * the host executes the actual resume operation from PM module.
2461 *
2462 * In the current scenario, WOW resume should happen before start processing
2463 * any data from the target. So It's required to perform WOW resume in RX path.
2464 * Ideally we should perform WOW resume only in the actual platform
2465 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2466 *
2467 * ath6kl_check_wow_status() is called from ath6kl_rx().
2468 */
2469void ath6kl_check_wow_status(struct ath6kl *ar)
2470{
Raja Mani390a8c82012-03-07 11:35:04 +05302471 if (ar->state == ATH6KL_STATE_SUSPENDING)
2472 return;
2473
Raja Mania918fb32011-11-07 22:52:46 +02002474 if (ar->state == ATH6KL_STATE_WOW)
2475 ath6kl_cfg80211_resume(ar);
2476}
2477
2478#else
2479
2480void ath6kl_check_wow_status(struct ath6kl *ar)
2481{
2482}
Kalle Valoabcb3442011-07-22 08:26:20 +03002483#endif
2484
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302485static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2486 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002487{
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302488 struct ath6kl_htcap *htcap = &vif->htcap;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302489
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302490 if (htcap->ht_enable == ht_enable)
2491 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302492
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302493 if (ht_enable) {
2494 /* Set default ht capabilities */
2495 htcap->ht_enable = true;
2496 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2497 ath6kl_g_htcap : ath6kl_a_htcap;
2498 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2499 } else /* Disable ht */
2500 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002501
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302502 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2503 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002504}
2505
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002506static int ath6kl_restore_htcap(struct ath6kl_vif *vif)
2507{
2508 struct wiphy *wiphy = vif->ar->wiphy;
2509 int band, ret = 0;
2510
2511 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2512 if (!wiphy->bands[band])
2513 continue;
2514
2515 ret = ath6kl_set_htcap(vif, band,
2516 wiphy->bands[band]->ht_cap.ht_supported);
2517 if (ret)
2518 return ret;
2519 }
2520
2521 return ret;
2522}
2523
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002524static bool ath6kl_is_p2p_ie(const u8 *pos)
2525{
2526 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2527 pos[2] == 0x50 && pos[3] == 0x6f &&
2528 pos[4] == 0x9a && pos[5] == 0x09;
2529}
2530
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302531static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2532 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002533{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302534 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002535 const u8 *pos;
2536 u8 *buf = NULL;
2537 size_t len = 0;
2538 int ret;
2539
2540 /*
2541 * Filter out P2P IE(s) since they will be included depending on
2542 * the Probe Request frame in ath6kl_send_go_probe_resp().
2543 */
2544
2545 if (ies && ies_len) {
2546 buf = kmalloc(ies_len, GFP_KERNEL);
2547 if (buf == NULL)
2548 return -ENOMEM;
2549 pos = ies;
2550 while (pos + 1 < ies + ies_len) {
2551 if (pos + 2 + pos[1] > ies + ies_len)
2552 break;
2553 if (!ath6kl_is_p2p_ie(pos)) {
2554 memcpy(buf + len, pos, 2 + pos[1]);
2555 len += 2 + pos[1];
2556 }
2557 pos += 2 + pos[1];
2558 }
2559 }
2560
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302561 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2562 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002563 kfree(buf);
2564 return ret;
2565}
2566
Johannes Berg88600202012-02-13 15:17:18 +01002567static int ath6kl_set_ies(struct ath6kl_vif *vif,
2568 struct cfg80211_beacon_data *info)
2569{
2570 struct ath6kl *ar = vif->ar;
2571 int res;
2572
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002573 /* this also clears IE in fw if it's not set */
2574 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2575 WMI_FRAME_BEACON,
2576 info->beacon_ies,
2577 info->beacon_ies_len);
2578 if (res)
2579 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002580
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002581 /* this also clears IE in fw if it's not set */
2582 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2583 info->proberesp_ies_len);
2584 if (res)
2585 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002586
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002587 /* this also clears IE in fw if it's not set */
2588 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2589 WMI_FRAME_ASSOC_RESP,
2590 info->assocresp_ies,
2591 info->assocresp_ies_len);
2592 if (res)
2593 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002594
2595 return 0;
2596}
2597
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302598static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2599 struct ieee80211_channel *chan,
2600 enum nl80211_channel_type channel_type)
2601{
2602 struct ath6kl_vif *vif;
2603
2604 /*
2605 * 'dev' could be NULL if a channel change is required for the hardware
2606 * device itself, instead of a particular VIF.
2607 *
2608 * FIXME: To be handled properly when monitor mode is supported.
2609 */
2610 if (!dev)
2611 return -EBUSY;
2612
2613 vif = netdev_priv(dev);
2614
2615 if (!ath6kl_cfg80211_ready(vif))
2616 return -EIO;
2617
2618 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2619 __func__, chan->center_freq, chan->hw_value);
2620 vif->next_chan = chan->center_freq;
2621 vif->next_ch_type = channel_type;
2622 vif->next_ch_band = chan->band;
2623
2624 return 0;
2625}
2626
Thomas Pedersenc422d52d2012-05-15 00:09:23 -07002627void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
2628{
2629 int err;
2630
2631 if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
2632 return;
2633
2634 if (vif->nw_type != INFRA_NETWORK)
2635 return;
2636
2637 if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
2638 vif->ar->fw_capabilities))
2639 return;
2640
2641 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
2642 enable ? "enable" : "disable");
2643
2644 err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
2645 vif->fw_vif_idx, enable);
2646 if (err)
2647 ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
2648 enable ? "enable" : "disable", err);
2649}
2650
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302651static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2652 u8 *rsn_capab)
2653{
2654 const u8 *rsn_ie;
2655 size_t rsn_ie_len;
2656 u16 cnt;
2657
2658 if (!beacon->tail)
2659 return -EINVAL;
2660
2661 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2662 if (!rsn_ie)
2663 return -EINVAL;
2664
2665 rsn_ie_len = *(rsn_ie + 1);
2666 /* skip element id and length */
2667 rsn_ie += 2;
2668
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302669 /* skip version */
2670 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302671 return -EINVAL;
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302672 rsn_ie += 2;
2673 rsn_ie_len -= 2;
2674
2675 /* skip group cipher suite */
2676 if (rsn_ie_len < 4)
2677 return 0;
2678 rsn_ie += 4;
2679 rsn_ie_len -= 4;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302680
2681 /* skip pairwise cipher suite */
2682 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302683 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302684 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302685 rsn_ie += (2 + cnt * 4);
2686 rsn_ie_len -= (2 + cnt * 4);
2687
2688 /* skip akm suite */
2689 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302690 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302691 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302692 rsn_ie += (2 + cnt * 4);
2693 rsn_ie_len -= (2 + cnt * 4);
2694
2695 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302696 return 0;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302697
2698 memcpy(rsn_capab, rsn_ie, 2);
2699
2700 return 0;
2701}
2702
Johannes Berg88600202012-02-13 15:17:18 +01002703static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2704 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002705{
2706 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302707 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002708 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002709 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002710 u8 *ies;
2711 int ies_len;
2712 struct wmi_connect_cmd p;
2713 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302714 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302715 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002716
Johannes Berg88600202012-02-13 15:17:18 +01002717 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002718
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302719 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002720 return -EIO;
2721
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302722 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002723 return -EOPNOTSUPP;
2724
Johannes Berg88600202012-02-13 15:17:18 +01002725 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002726
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002727 ar->ap_mode_bkey.valid = false;
2728
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002729 /* TODO:
2730 * info->interval
2731 * info->dtim_period
2732 */
2733
Johannes Berg88600202012-02-13 15:17:18 +01002734 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002735 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002736 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002737 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002738 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002739 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002740 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002741
2742 if (info->ssid == NULL)
2743 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302744 memcpy(vif->ssid, info->ssid, info->ssid_len);
2745 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002746 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002747 hidden = true;
2748
2749 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2750 if (res)
2751 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002752
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302753 ret = ath6kl_set_auth_type(vif, info->auth_type);
2754 if (ret)
2755 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002756
2757 memset(&p, 0, sizeof(p));
2758
2759 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2760 switch (info->crypto.akm_suites[i]) {
2761 case WLAN_AKM_SUITE_8021X:
2762 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2763 p.auth_mode |= WPA_AUTH;
2764 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2765 p.auth_mode |= WPA2_AUTH;
2766 break;
2767 case WLAN_AKM_SUITE_PSK:
2768 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2769 p.auth_mode |= WPA_PSK_AUTH;
2770 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2771 p.auth_mode |= WPA2_PSK_AUTH;
2772 break;
2773 }
2774 }
2775 if (p.auth_mode == 0)
2776 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302777 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002778
2779 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2780 switch (info->crypto.ciphers_pairwise[i]) {
2781 case WLAN_CIPHER_SUITE_WEP40:
2782 case WLAN_CIPHER_SUITE_WEP104:
2783 p.prwise_crypto_type |= WEP_CRYPT;
2784 break;
2785 case WLAN_CIPHER_SUITE_TKIP:
2786 p.prwise_crypto_type |= TKIP_CRYPT;
2787 break;
2788 case WLAN_CIPHER_SUITE_CCMP:
2789 p.prwise_crypto_type |= AES_CRYPT;
2790 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002791 case WLAN_CIPHER_SUITE_SMS4:
2792 p.prwise_crypto_type |= WAPI_CRYPT;
2793 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002794 }
2795 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002796 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002797 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302798 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002799 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302800 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002801
2802 switch (info->crypto.cipher_group) {
2803 case WLAN_CIPHER_SUITE_WEP40:
2804 case WLAN_CIPHER_SUITE_WEP104:
2805 p.grp_crypto_type = WEP_CRYPT;
2806 break;
2807 case WLAN_CIPHER_SUITE_TKIP:
2808 p.grp_crypto_type = TKIP_CRYPT;
2809 break;
2810 case WLAN_CIPHER_SUITE_CCMP:
2811 p.grp_crypto_type = AES_CRYPT;
2812 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002813 case WLAN_CIPHER_SUITE_SMS4:
2814 p.grp_crypto_type = WAPI_CRYPT;
2815 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002816 default:
2817 p.grp_crypto_type = NONE_CRYPT;
2818 break;
2819 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302820 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002821
2822 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302823 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002824
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302825 p.ssid_len = vif->ssid_len;
2826 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2827 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302828 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002829
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302830 /* Enable uAPSD support by default */
2831 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2832 if (res < 0)
2833 return res;
2834
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002835 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2836 p.nw_subtype = SUBTYPE_P2PGO;
2837 } else {
2838 /*
2839 * Due to firmware limitation, it is not possible to
2840 * do P2P mgmt operations in AP mode
2841 */
2842 p.nw_subtype = SUBTYPE_NONE;
2843 }
2844
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302845 if (info->inactivity_timeout) {
2846 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2847 info->inactivity_timeout);
2848 if (res < 0)
2849 return res;
2850 }
2851
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302852 if (ath6kl_set_htcap(vif, vif->next_ch_band,
2853 vif->next_ch_type != NL80211_CHAN_NO_HT))
2854 return -EIO;
2855
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302856 /*
2857 * Get the PTKSA replay counter in the RSN IE. Supplicant
2858 * will use the RSN IE in M3 message and firmware has to
2859 * advertise the same in beacon/probe response. Send
2860 * the complete RSN IE capability field to firmware
2861 */
2862 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2863 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2864 ar->fw_capabilities)) {
2865 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2866 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2867 (const u8 *) &rsn_capab,
2868 sizeof(rsn_capab));
2869 if (res < 0)
2870 return res;
2871 }
2872
Thomas Pedersenc4f78632012-04-06 13:35:48 -07002873 memcpy(&vif->profile, &p, sizeof(p));
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302874 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002875 if (res < 0)
2876 return res;
2877
2878 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002879}
2880
Johannes Berg88600202012-02-13 15:17:18 +01002881static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2882 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002883{
Johannes Berg88600202012-02-13 15:17:18 +01002884 struct ath6kl_vif *vif = netdev_priv(dev);
2885
2886 if (!ath6kl_cfg80211_ready(vif))
2887 return -EIO;
2888
2889 if (vif->next_mode != AP_NETWORK)
2890 return -EOPNOTSUPP;
2891
2892 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002893}
2894
Johannes Berg88600202012-02-13 15:17:18 +01002895static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002896{
2897 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302898 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002899
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302900 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002901 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302902 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002903 return -ENOTCONN;
2904
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302905 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302906 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002907
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302908 /* Restore ht setting in firmware */
Thomas Pedersen37a2f952012-04-19 15:31:57 -07002909 return ath6kl_restore_htcap(vif);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002910}
2911
Jouni Malinen33e53082011-12-27 11:02:56 +02002912static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2913
2914static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2915 u8 *mac)
2916{
2917 struct ath6kl *ar = ath6kl_priv(dev);
2918 struct ath6kl_vif *vif = netdev_priv(dev);
2919 const u8 *addr = mac ? mac : bcast_addr;
2920
2921 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2922 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2923}
2924
Jouni Malinen23875132011-08-30 21:57:53 +03002925static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2926 u8 *mac, struct station_parameters *params)
2927{
2928 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302929 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002930
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302931 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002932 return -EOPNOTSUPP;
2933
2934 /* Use this only for authorizing/unauthorizing a station */
2935 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2936 return -EOPNOTSUPP;
2937
2938 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302939 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2940 WMI_AP_MLME_AUTHORIZE, mac, 0);
2941 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2942 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002943}
2944
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002945static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2946 struct net_device *dev,
2947 struct ieee80211_channel *chan,
2948 enum nl80211_channel_type channel_type,
2949 unsigned int duration,
2950 u64 *cookie)
2951{
2952 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302953 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002954 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002955
2956 /* TODO: if already pending or ongoing remain-on-channel,
2957 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002958 id = ++vif->last_roc_id;
2959 if (id == 0) {
2960 /* Do not use 0 as the cookie value */
2961 id = ++vif->last_roc_id;
2962 }
2963 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002964
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302965 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2966 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002967}
2968
2969static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2970 struct net_device *dev,
2971 u64 cookie)
2972{
2973 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302974 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002975
Jouni Malinen10522612011-10-27 16:00:13 +03002976 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002977 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002978 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002979
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302980 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002981}
2982
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302983static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2984 const u8 *buf, size_t len,
2985 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002986{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302987 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002988 const u8 *pos;
2989 u8 *p2p;
2990 int p2p_len;
2991 int ret;
2992 const struct ieee80211_mgmt *mgmt;
2993
2994 mgmt = (const struct ieee80211_mgmt *) buf;
2995
2996 /* Include P2P IE(s) from the frame generated in user space. */
2997
2998 p2p = kmalloc(len, GFP_KERNEL);
2999 if (p2p == NULL)
3000 return -ENOMEM;
3001 p2p_len = 0;
3002
3003 pos = mgmt->u.probe_resp.variable;
3004 while (pos + 1 < buf + len) {
3005 if (pos + 2 + pos[1] > buf + len)
3006 break;
3007 if (ath6kl_is_p2p_ie(pos)) {
3008 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
3009 p2p_len += 2 + pos[1];
3010 }
3011 pos += 2 + pos[1];
3012 }
3013
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303014 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
3015 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003016 kfree(p2p);
3017 return ret;
3018}
3019
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003020static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
3021 u32 id,
3022 u32 freq,
3023 u32 wait,
3024 const u8 *buf,
3025 size_t len,
3026 bool *more_data,
3027 bool no_cck)
3028{
3029 struct ieee80211_mgmt *mgmt;
3030 struct ath6kl_sta *conn;
3031 bool is_psq_empty = false;
3032 struct ath6kl_mgmt_buff *mgmt_buf;
3033 size_t mgmt_buf_size;
3034 struct ath6kl *ar = vif->ar;
3035
3036 mgmt = (struct ieee80211_mgmt *) buf;
3037 if (is_multicast_ether_addr(mgmt->da))
3038 return false;
3039
3040 conn = ath6kl_find_sta(vif, mgmt->da);
3041 if (!conn)
3042 return false;
3043
3044 if (conn->sta_flags & STA_PS_SLEEP) {
3045 if (!(conn->sta_flags & STA_PS_POLLED)) {
3046 /* Queue the frames if the STA is sleeping */
3047 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
3048 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
3049 if (!mgmt_buf)
3050 return false;
3051
3052 INIT_LIST_HEAD(&mgmt_buf->list);
3053 mgmt_buf->id = id;
3054 mgmt_buf->freq = freq;
3055 mgmt_buf->wait = wait;
3056 mgmt_buf->len = len;
3057 mgmt_buf->no_cck = no_cck;
3058 memcpy(mgmt_buf->buf, buf, len);
3059 spin_lock_bh(&conn->psq_lock);
3060 is_psq_empty = skb_queue_empty(&conn->psq) &&
3061 (conn->mgmt_psq_len == 0);
3062 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
3063 conn->mgmt_psq_len++;
3064 spin_unlock_bh(&conn->psq_lock);
3065
3066 /*
3067 * If this is the first pkt getting queued
3068 * for this STA, update the PVB for this
3069 * STA.
3070 */
3071 if (is_psq_empty)
3072 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
3073 conn->aid, 1);
3074 return true;
3075 }
3076
3077 /*
3078 * This tx is because of a PsPoll.
3079 * Determine if MoreData bit has to be set.
3080 */
3081 spin_lock_bh(&conn->psq_lock);
3082 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
3083 *more_data = true;
3084 spin_unlock_bh(&conn->psq_lock);
3085 }
3086
3087 return false;
3088}
3089
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003090/* Check if SSID length is greater than DIRECT- */
3091static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
3092{
3093 const struct ieee80211_mgmt *mgmt;
3094 mgmt = (const struct ieee80211_mgmt *) buf;
3095
3096 /* variable[1] contains the SSID tag length */
3097 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
3098 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
3099 return true;
3100 }
3101
3102 return false;
3103}
3104
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003105static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
3106 struct ieee80211_channel *chan, bool offchan,
3107 enum nl80211_channel_type channel_type,
3108 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01003109 const u8 *buf, size_t len, bool no_cck,
3110 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003111{
3112 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303113 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003114 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003115 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003116 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003117
3118 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003119 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3120 ieee80211_is_probe_resp(mgmt->frame_control) &&
3121 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003122 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003123 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003124 * command to allow the target to fill in the generic IEs.
3125 */
3126 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303127 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003128 chan->center_freq);
3129 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003130
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303131 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003132 if (id == 0) {
3133 /*
3134 * 0 is a reserved value in the WMI command and shall not be
3135 * used for the command.
3136 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303137 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003138 }
3139
3140 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003141
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003142 /* AP mode Power saving processing */
3143 if (vif->nw_type == AP_NETWORK) {
3144 queued = ath6kl_mgmt_powersave_ap(vif,
3145 id, chan->center_freq,
3146 wait, buf,
3147 len, &more_data, no_cck);
3148 if (queued)
3149 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003150 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003151
3152 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3153 chan->center_freq, wait,
3154 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003155}
3156
Jouni Malinenae32c302011-08-30 21:58:01 +03003157static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
3158 struct net_device *dev,
3159 u16 frame_type, bool reg)
3160{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303161 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003162
3163 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3164 __func__, frame_type, reg);
3165 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3166 /*
3167 * Note: This notification callback is not allowed to sleep, so
3168 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3169 * hardcode target to report Probe Request frames all the time.
3170 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303171 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003172 }
3173}
3174
Kalle Valo10509f92011-12-13 14:52:07 +02003175static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3176 struct net_device *dev,
3177 struct cfg80211_sched_scan_request *request)
3178{
3179 struct ath6kl *ar = ath6kl_priv(dev);
3180 struct ath6kl_vif *vif = netdev_priv(dev);
3181 u16 interval;
3182 int ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003183
3184 if (ar->state != ATH6KL_STATE_ON)
3185 return -EIO;
3186
3187 if (vif->sme_state != SME_DISCONNECTED)
3188 return -EBUSY;
3189
Thomas Pedersen77ed4e42012-04-19 16:29:19 -07003190 /* The FW currently can't support multi-vif WoW properly. */
3191 if (ar->num_vif > 1)
3192 return -EIO;
3193
Kalle Valob4d13d32012-03-21 10:01:09 +02003194 ath6kl_cfg80211_scan_complete_event(vif, true);
3195
Jouni Malinen3b8ffc62012-04-16 19:28:03 +03003196 ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
3197 request->n_ssids);
3198 if (ret < 0)
3199 return ret;
Kalle Valo10509f92011-12-13 14:52:07 +02003200
3201 /* fw uses seconds, also make sure that it's >0 */
3202 interval = max_t(u16, 1, request->interval / 1000);
3203
3204 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3205 interval, interval,
Subramania Sharma Thandaveswarand472b5e2012-04-16 16:09:57 +05303206 vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
Kalle Valo10509f92011-12-13 14:52:07 +02003207
Kalle Valo10509f92011-12-13 14:52:07 +02003208 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3209 ATH6KL_WOW_MODE_ENABLE,
3210 WOW_FILTER_SSID,
3211 WOW_HOST_REQ_DELAY);
3212 if (ret) {
3213 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3214 return ret;
3215 }
3216
3217 /* this also clears IE in fw if it's not set */
3218 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3219 WMI_FRAME_PROBE_REQ,
3220 request->ie, request->ie_len);
3221 if (ret) {
3222 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
3223 ret);
3224 return ret;
3225 }
3226
3227 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3228 ATH6KL_HOST_MODE_ASLEEP);
3229 if (ret) {
3230 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3231 ret);
3232 return ret;
3233 }
3234
3235 ar->state = ATH6KL_STATE_SCHED_SCAN;
3236
3237 return ret;
3238}
3239
3240static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3241 struct net_device *dev)
3242{
3243 struct ath6kl_vif *vif = netdev_priv(dev);
3244 bool stopped;
3245
3246 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3247
3248 if (!stopped)
3249 return -EIO;
3250
3251 return 0;
3252}
3253
Jouni Malinenf80574a2011-08-30 21:58:04 +03003254static const struct ieee80211_txrx_stypes
3255ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3256 [NL80211_IFTYPE_STATION] = {
3257 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3258 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3259 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3260 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3261 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003262 [NL80211_IFTYPE_AP] = {
3263 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3264 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3265 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3266 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3267 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003268 [NL80211_IFTYPE_P2P_CLIENT] = {
3269 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3270 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3271 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3272 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3273 },
3274 [NL80211_IFTYPE_P2P_GO] = {
3275 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3276 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3277 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3278 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3279 },
3280};
3281
Kalle Valobdcd8172011-07-18 00:22:30 +03003282static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303283 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3284 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003285 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3286 .scan = ath6kl_cfg80211_scan,
3287 .connect = ath6kl_cfg80211_connect,
3288 .disconnect = ath6kl_cfg80211_disconnect,
3289 .add_key = ath6kl_cfg80211_add_key,
3290 .get_key = ath6kl_cfg80211_get_key,
3291 .del_key = ath6kl_cfg80211_del_key,
3292 .set_default_key = ath6kl_cfg80211_set_default_key,
3293 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3294 .set_tx_power = ath6kl_cfg80211_set_txpower,
3295 .get_tx_power = ath6kl_cfg80211_get_txpower,
3296 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3297 .join_ibss = ath6kl_cfg80211_join_ibss,
3298 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3299 .get_station = ath6kl_get_station,
3300 .set_pmksa = ath6kl_set_pmksa,
3301 .del_pmksa = ath6kl_del_pmksa,
3302 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003303 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003304#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003305 .suspend = __ath6kl_cfg80211_suspend,
3306 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003307#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003308 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003309 .start_ap = ath6kl_start_ap,
3310 .change_beacon = ath6kl_change_beacon,
3311 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003312 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003313 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003314 .remain_on_channel = ath6kl_remain_on_channel,
3315 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003316 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003317 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003318 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3319 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003320};
3321
Kalle Valo7125f012011-12-13 14:51:37 +02003322void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003323{
Kalle Valo10509f92011-12-13 14:52:07 +02003324 ath6kl_cfg80211_sscan_disable(vif);
3325
Kalle Valoec4b7f62011-11-01 08:44:04 +02003326 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003327 case SME_DISCONNECTED:
3328 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003329 case SME_CONNECTING:
3330 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3331 NULL, 0,
3332 WLAN_STATUS_UNSPECIFIED_FAILURE,
3333 GFP_KERNEL);
3334 break;
3335 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003336 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3337 break;
3338 }
3339
3340 if (test_bit(CONNECTED, &vif->flags) ||
3341 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003342 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003343
3344 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003345 clear_bit(CONNECTED, &vif->flags);
3346 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003347
3348 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003349 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3350 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3351 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003352
3353 ath6kl_cfg80211_scan_complete_event(vif, true);
3354}
3355
Kalle Valo7125f012011-12-13 14:51:37 +02003356void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3357{
3358 struct ath6kl_vif *vif;
3359
3360 vif = ath6kl_vif_first(ar);
3361 if (!vif) {
3362 /* save the current power mode before enabling power save */
3363 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3364
3365 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
Kalle Valocdeb8602012-04-12 11:02:18 +03003366 ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n");
Kalle Valo7125f012011-12-13 14:51:37 +02003367 return;
3368 }
3369
3370 /*
3371 * FIXME: we should take ar->list_lock to protect changes in the
3372 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3373 * sleeps.
3374 */
3375 list_for_each_entry(vif, &ar->vif_list, list)
3376 ath6kl_cfg80211_stop(vif);
3377}
3378
Kalle Valoc25889e2012-01-17 20:08:27 +02003379static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003380{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303381 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303382 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303383 ath6kl_err("failed to initialize aggr\n");
3384 return -ENOMEM;
3385 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003386
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303387 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303388 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003389 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3390 (unsigned long) vif);
3391
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303392 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303393 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303394
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303395 INIT_LIST_HEAD(&vif->mc_filter);
3396
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303397 return 0;
3398}
3399
Kalle Valoc25889e2012-01-17 20:08:27 +02003400void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303401{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303402 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303403 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303404
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303405 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303406
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303407 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3408
3409 if (vif->nw_type == ADHOC_NETWORK)
3410 ar->ibss_if_active = false;
3411
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303412 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3413 list_del(&mc_filter->list);
3414 kfree(mc_filter);
3415 }
3416
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303417 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303418
3419 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303420}
3421
3422struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303423 enum nl80211_iftype type, u8 fw_vif_idx,
3424 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303425{
3426 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303427 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303428
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303429 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303430 if (!ndev)
3431 return NULL;
3432
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303433 vif = netdev_priv(ndev);
3434 ndev->ieee80211_ptr = &vif->wdev;
3435 vif->wdev.wiphy = ar->wiphy;
3436 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303437 vif->ndev = ndev;
3438 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3439 vif->wdev.netdev = ndev;
3440 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303441 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003442 vif->nw_type = nw_type;
3443 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303444 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303445 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Raja Manieb389872012-04-16 16:09:56 +05303446 vif->bg_scan_period = 0;
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05303447 vif->htcap.ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303448
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303449 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3450 if (fw_vif_idx != 0)
3451 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3452 0x2;
3453
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303454 init_netdev(ndev);
3455
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303456 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303457
Kalle Valoc25889e2012-01-17 20:08:27 +02003458 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303459 goto err;
3460
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303461 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303462 goto err;
3463
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303464 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303465 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303466 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303467 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303468 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303469
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303470 if (type == NL80211_IFTYPE_ADHOC)
3471 ar->ibss_if_active = true;
3472
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303473 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303474 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303475 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303476
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303477 return ndev;
3478
3479err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303480 aggr_module_destroy(vif->aggr_cntxt);
3481 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303482 return NULL;
3483}
3484
Kalle Valo46d33a22012-01-17 20:08:40 +02003485int ath6kl_cfg80211_init(struct ath6kl *ar)
3486{
3487 struct wiphy *wiphy = ar->wiphy;
Thomas Pedersend92917e2012-04-19 15:31:56 -07003488 bool band_2gig = false, band_5gig = false, ht = false;
Kalle Valo46d33a22012-01-17 20:08:40 +02003489 int ret;
3490
3491 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3492
3493 wiphy->max_remain_on_channel_duration = 5000;
3494
3495 /* set device pointer for wiphy */
3496 set_wiphy_dev(wiphy, ar->dev);
3497
3498 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3499 BIT(NL80211_IFTYPE_ADHOC) |
3500 BIT(NL80211_IFTYPE_AP);
3501 if (ar->p2p) {
3502 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3503 BIT(NL80211_IFTYPE_P2P_CLIENT);
3504 }
3505
3506 /* max num of ssids that can be probed during scanning */
Jouni Malinen8ab54152012-05-09 22:14:51 +03003507 wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
Kalle Valo46d33a22012-01-17 20:08:40 +02003508 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
Thomas Pedersend92917e2012-04-19 15:31:56 -07003509 switch (ar->hw.cap) {
3510 case WMI_11AN_CAP:
3511 ht = true;
3512 case WMI_11A_CAP:
3513 band_5gig = true;
3514 break;
3515 case WMI_11GN_CAP:
3516 ht = true;
3517 case WMI_11G_CAP:
3518 band_2gig = true;
3519 break;
3520 case WMI_11AGN_CAP:
3521 ht = true;
3522 case WMI_11AG_CAP:
3523 band_2gig = true;
3524 band_5gig = true;
3525 break;
3526 default:
3527 ath6kl_err("invalid phy capability!\n");
3528 return -EINVAL;
3529 }
3530
Vasanthakumar Thiagarajan7fd1ce72012-04-25 12:38:18 +05303531 /*
3532 * Even if the fw has HT support, advertise HT cap only when
3533 * the firmware has support to override RSN capability, otherwise
3534 * 4-way handshake would fail.
3535 */
3536 if (!(ht &&
3537 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
3538 ar->fw_capabilities))) {
Thomas Pedersend92917e2012-04-19 15:31:56 -07003539 ath6kl_band_2ghz.ht_cap.cap = 0;
3540 ath6kl_band_2ghz.ht_cap.ht_supported = false;
3541 ath6kl_band_5ghz.ht_cap.cap = 0;
3542 ath6kl_band_5ghz.ht_cap.ht_supported = false;
3543 }
3544 if (band_2gig)
3545 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3546 if (band_5gig)
3547 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3548
Kalle Valo46d33a22012-01-17 20:08:40 +02003549 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3550
3551 wiphy->cipher_suites = cipher_suites;
3552 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3553
3554 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3555 WIPHY_WOWLAN_DISCONNECT |
3556 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3557 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3558 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3559 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3560 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3561 wiphy->wowlan.pattern_min_len = 1;
3562 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3563
Jouni Malinen8ab54152012-05-09 22:14:51 +03003564 wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
Kalle Valo46d33a22012-01-17 20:08:40 +02003565
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303566 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3567 WIPHY_FLAG_HAVE_AP_SME |
3568 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3569 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3570
3571 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3572 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3573
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303574 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3575 ar->fw_capabilities))
3576 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3577
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303578 ar->wiphy->probe_resp_offload =
3579 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3580 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
Jouni Malinena432e7c2012-04-16 19:25:35 +03003581 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303582
Kalle Valo46d33a22012-01-17 20:08:40 +02003583 ret = wiphy_register(wiphy);
3584 if (ret < 0) {
3585 ath6kl_err("couldn't register wiphy device\n");
3586 return ret;
3587 }
3588
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303589 ar->wiphy_registered = true;
3590
Kalle Valo46d33a22012-01-17 20:08:40 +02003591 return 0;
3592}
3593
3594void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303595{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303596 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303597
3598 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003599}
Kalle Valo46d33a22012-01-17 20:08:40 +02003600
Kalle Valo45eaa782012-01-17 20:09:05 +02003601struct ath6kl *ath6kl_cfg80211_create(void)
3602{
3603 struct ath6kl *ar;
3604 struct wiphy *wiphy;
3605
3606 /* create a new wiphy for use with cfg80211 */
3607 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3608
3609 if (!wiphy) {
3610 ath6kl_err("couldn't allocate wiphy device\n");
3611 return NULL;
3612 }
3613
3614 ar = wiphy_priv(wiphy);
3615 ar->wiphy = wiphy;
3616
3617 return ar;
3618}
3619
3620/* Note: ar variable must not be accessed after calling this! */
3621void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3622{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303623 int i;
3624
3625 for (i = 0; i < AP_MAX_NUM_STA; i++)
3626 kfree(ar->sta_list[i].aggr_conn);
3627
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303628 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003629}
Kalle Valo45eaa782012-01-17 20:09:05 +02003630