blob: 12f2f616d38061a17cefde84bad5580b5ec61c48 [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
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800579 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
580 nw_subtype = SUBTYPE_P2PCLIENT;
581
Kalle Valobdcd8172011-07-18 00:22:30 +0300582 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
583 "%s: connect called with authmode %d dot11 auth %d"
584 " PW crypto %d PW crypto len %d GRP crypto %d"
585 " GRP crypto len %d channel hint %u\n",
586 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530587 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
588 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530589 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300590
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530591 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530592
593 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200594 interval = max_t(u16, vif->listen_intvl_t,
595 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530596 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
597 interval,
598 0);
599 if (status) {
600 ath6kl_err("couldn't set listen intervel\n");
601 up(&ar->sem);
602 return status;
603 }
604 }
605
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530606 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530607 vif->dot11_auth_mode, vif->auth_mode,
608 vif->prwise_crypto,
609 vif->prwise_crypto_len,
610 vif->grp_crypto, vif->grp_crypto_len,
611 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530612 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800613 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300614
Bala Shanmugamf5993592012-03-27 12:17:32 +0530615 /* disable background scan if period is 0 */
616 if (sme->bg_scan_period == 0)
617 sme->bg_scan_period = 0xffff;
618
619 /* configure default value if not specified */
620 if (sme->bg_scan_period == -1)
621 sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
622
623 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
624 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
625
Kalle Valobdcd8172011-07-18 00:22:30 +0300626 up(&ar->sem);
627
628 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530629 memset(vif->ssid, 0, sizeof(vif->ssid));
630 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300631 ath6kl_err("invalid request\n");
632 return -ENOENT;
633 } else if (status) {
634 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
635 return -EIO;
636 }
637
638 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200639 ((vif->auth_mode == WPA_PSK_AUTH) ||
640 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530641 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300642 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
643 }
644
645 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530646 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300647
648 return 0;
649}
650
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530651static struct cfg80211_bss *
652ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
653 enum network_type nw_type,
654 const u8 *bssid,
655 struct ieee80211_channel *chan,
656 const u8 *beacon_ie,
657 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300658{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530659 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300660 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530661 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300662 u8 *ie;
663
Raja Mani4eab6f42011-11-09 17:02:23 +0530664 if (nw_type & ADHOC_NETWORK) {
665 cap_mask = WLAN_CAPABILITY_IBSS;
666 cap_val = WLAN_CAPABILITY_IBSS;
667 } else {
668 cap_mask = WLAN_CAPABILITY_ESS;
669 cap_val = WLAN_CAPABILITY_ESS;
670 }
671
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530672 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530673 vif->ssid, vif->ssid_len,
674 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300675 if (bss == NULL) {
676 /*
677 * Since cfg80211 may not yet know about the BSS,
678 * generate a partial entry until the first BSS info
679 * event becomes available.
680 *
681 * Prepend SSID element since it is not included in the Beacon
682 * IEs from the target.
683 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530684 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300685 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530686 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300687 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530688 ie[1] = vif->ssid_len;
689 memcpy(ie + 2, vif->ssid, vif->ssid_len);
690 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530691 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530692 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530693 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300694 0, GFP_KERNEL);
695 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530696 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
697 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300698 kfree(ie);
699 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530700 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300701
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530702 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300703}
704
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530705void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300706 u8 *bssid, u16 listen_intvl,
707 u16 beacon_intvl,
708 enum network_type nw_type,
709 u8 beacon_ie_len, u8 assoc_req_len,
710 u8 assoc_resp_len, u8 *assoc_info)
711{
Jouni Malinen01cac472011-09-19 19:14:59 +0300712 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530713 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530714 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300715
716 /* capinfo + listen interval */
717 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
718
719 /* capinfo + status code + associd */
720 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
721
722 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
723 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
724 assoc_resp_ie_offset;
725
726 assoc_req_len -= assoc_req_ie_offset;
727 assoc_resp_len -= assoc_resp_ie_offset;
728
Jouni Malinen32c10872011-09-19 19:15:07 +0300729 /*
730 * Store Beacon interval here; DTIM period will be available only once
731 * a Beacon frame from the AP is seen.
732 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530733 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530734 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300735
Kalle Valobdcd8172011-07-18 00:22:30 +0300736 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530737 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300738 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
739 "%s: ath6k not in ibss mode\n", __func__);
740 return;
741 }
742 }
743
744 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530745 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
746 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300747 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
748 "%s: ath6k not in station mode\n", __func__);
749 return;
750 }
751 }
752
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530753 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300754
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530755 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
756 assoc_info, beacon_ie_len);
757 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530758 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300759 return;
760 }
761
Raja Mani4eab6f42011-11-09 17:02:23 +0530762 if (nw_type & ADHOC_NETWORK) {
763 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
764 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
765 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530766 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300767 return;
768 }
769
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530770 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300771 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530772 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530773 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300774 assoc_req_ie, assoc_req_len,
775 assoc_resp_ie, assoc_resp_len,
776 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530777 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530778 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300779 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530780 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
781 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300782 }
783}
784
785static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
786 struct net_device *dev, u16 reason_code)
787{
Kalle Valod6d5c062011-11-25 13:17:37 +0200788 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530789 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300790
791 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
792 reason_code);
793
Kalle Valo10509f92011-12-13 14:52:07 +0200794 ath6kl_cfg80211_sscan_disable(vif);
795
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530796 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300797 return -EIO;
798
799 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
800 ath6kl_err("busy, destroy in progress\n");
801 return -EBUSY;
802 }
803
804 if (down_interruptible(&ar->sem)) {
805 ath6kl_err("busy, couldn't get access\n");
806 return -ERESTARTSYS;
807 }
808
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530809 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530810 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530811 memset(vif->ssid, 0, sizeof(vif->ssid));
812 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300813
814 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530815 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300816
817 up(&ar->sem);
818
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530819 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530820
Kalle Valobdcd8172011-07-18 00:22:30 +0300821 return 0;
822}
823
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530824void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300825 u8 *bssid, u8 assoc_resp_len,
826 u8 *assoc_info, u16 proto_reason)
827{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530828 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530829
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530830 if (vif->scan_req) {
831 cfg80211_scan_done(vif->scan_req, true);
832 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300833 }
834
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530835 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530836 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300837 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
838 "%s: ath6k not in ibss mode\n", __func__);
839 return;
840 }
841 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530842 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300843 return;
844 }
845
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530846 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530847 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
848 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300849 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
850 "%s: ath6k not in station mode\n", __func__);
851 return;
852 }
853 }
854
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530855 /*
856 * Send a disconnect command to target when a disconnect event is
857 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
858 * request from host) to make the firmware stop trying to connect even
859 * after giving disconnect event. There will be one more disconnect
860 * event for this disconnect command with reason code DISCONNECT_CMD
861 * which will be notified to cfg80211.
862 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300863
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530864 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530865 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300866 return;
867 }
868
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530869 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300870
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530871 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530872 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200873 bssid, NULL, 0,
874 NULL, 0,
875 WLAN_STATUS_UNSPECIFIED_FAILURE,
876 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530877 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530878 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200879 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300880 }
881
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530882 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300883}
884
Kalle Valobdcd8172011-07-18 00:22:30 +0300885static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
886 struct cfg80211_scan_request *request)
887{
Kalle Valod6d5c062011-11-25 13:17:37 +0200888 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530889 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300890 s8 n_channels = 0;
891 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300892 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530893 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300894
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530895 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300896 return -EIO;
897
Kalle Valo10509f92011-12-13 14:52:07 +0200898 ath6kl_cfg80211_sscan_disable(vif);
899
Kalle Valobdcd8172011-07-18 00:22:30 +0300900 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530901 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300902 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530903 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530904 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300905 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
906 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300907 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300908 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300909 }
910 }
911
912 if (request->n_ssids && request->ssids[0].ssid_len) {
913 u8 i;
914
915 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
916 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
917
918 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530919 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
920 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300921 request->ssids[i].ssid_len,
922 request->ssids[i].ssid);
923 }
924
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800925 /* this also clears IE in fw if it's not set */
926 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
927 WMI_FRAME_PROBE_REQ,
928 request->ie, request->ie_len);
929 if (ret) {
930 ath6kl_err("failed to set Probe Request appie for "
931 "scan");
932 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300933 }
934
Jouni Malinen11869be2011-09-02 20:07:06 +0300935 /*
936 * Scan only the requested channels if the request specifies a set of
937 * channels. If the list is longer than the target supports, do not
938 * configure the list and instead, scan all available channels.
939 */
940 if (request->n_channels > 0 &&
941 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300942 u8 i;
943
Jouni Malinen11869be2011-09-02 20:07:06 +0300944 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300945
946 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
947 if (channels == NULL) {
948 ath6kl_warn("failed to set scan channels, "
949 "scan all channels");
950 n_channels = 0;
951 }
952
953 for (i = 0; i < n_channels; i++)
954 channels[i] = request->channels[i]->center_freq;
955 }
956
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530957 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530958 force_fg_scan = 1;
959
Raja Mani5b35dff2012-03-28 18:50:35 +0530960 vif->scan_req = request;
961
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800962 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200963 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800964 /*
965 * If capable of doing P2P mgmt operations using
966 * station interface, send additional information like
967 * supported rates to advertise and xmit rates for
968 * probe requests
969 */
970 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
971 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530972 false, 0,
973 ATH6KL_FG_SCAN_INTERVAL,
974 n_channels, channels,
975 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800976 request->rates);
977 } else {
978 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
979 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530980 false, 0,
981 ATH6KL_FG_SCAN_INTERVAL,
982 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800983 }
Raja Mani5b35dff2012-03-28 18:50:35 +0530984 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300985 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +0530986 vif->scan_req = NULL;
987 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300988
Edward Lu1276c9e2011-08-30 21:58:00 +0300989 kfree(channels);
990
Kalle Valobdcd8172011-07-18 00:22:30 +0300991 return ret;
992}
993
Kalle Valo1c17d312011-11-01 08:43:56 +0200994void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300995{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530996 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300997 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300998
Kalle Valo1c17d312011-11-01 08:43:56 +0200999 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
1000 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +03001001
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301002 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001003 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001004
Kalle Valo1c17d312011-11-01 08:43:56 +02001005 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001006 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001007
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301008 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1009 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301010 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1011 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001012 0, NULL);
1013 }
1014 }
1015
1016out:
Kalle Valocb938212011-10-27 18:47:46 +03001017 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301018 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001019}
1020
Thomas Pedersenc4f78632012-04-06 13:35:48 -07001021void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
1022 enum wmi_phy_mode mode)
1023{
1024 enum nl80211_channel_type type;
1025
1026 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1027 "channel switch notify nw_type %d freq %d mode %d\n",
1028 vif->nw_type, freq, mode);
1029
1030 type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT;
1031
1032 cfg80211_ch_switch_notify(vif->ndev, freq, type);
1033}
1034
Kalle Valobdcd8172011-07-18 00:22:30 +03001035static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1036 u8 key_index, bool pairwise,
1037 const u8 *mac_addr,
1038 struct key_params *params)
1039{
Kalle Valod6d5c062011-11-25 13:17:37 +02001040 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301041 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001042 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301043 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001044 u8 key_usage;
1045 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001046
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301047 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001048 return -EIO;
1049
Jouni Malinen837cb972011-10-11 17:31:57 +03001050 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1051 if (params->key_len != WMI_KRK_LEN)
1052 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301053 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1054 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001055 }
1056
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301057 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001058 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1059 "%s: key index %d out of bounds\n", __func__,
1060 key_index);
1061 return -ENOENT;
1062 }
1063
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301064 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001065 memset(key, 0, sizeof(struct ath6kl_key));
1066
1067 if (pairwise)
1068 key_usage = PAIRWISE_USAGE;
1069 else
1070 key_usage = GROUP_USAGE;
1071
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301072 seq_len = params->seq_len;
1073 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1074 seq_len > ATH6KL_KEY_SEQ_LEN) {
1075 /* Only first half of the WPI PN is configured */
1076 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001077 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301078 if (params->key_len > WLAN_MAX_KEY_LEN ||
1079 seq_len > sizeof(key->seq))
1080 return -EINVAL;
1081
1082 key->key_len = params->key_len;
1083 memcpy(key->key, params->key, key->key_len);
1084 key->seq_len = seq_len;
1085 memcpy(key->seq, params->seq, key->seq_len);
1086 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001087
1088 switch (key->cipher) {
1089 case WLAN_CIPHER_SUITE_WEP40:
1090 case WLAN_CIPHER_SUITE_WEP104:
1091 key_type = WEP_CRYPT;
1092 break;
1093
1094 case WLAN_CIPHER_SUITE_TKIP:
1095 key_type = TKIP_CRYPT;
1096 break;
1097
1098 case WLAN_CIPHER_SUITE_CCMP:
1099 key_type = AES_CRYPT;
1100 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001101 case WLAN_CIPHER_SUITE_SMS4:
1102 key_type = WAPI_CRYPT;
1103 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001104
1105 default:
1106 return -ENOTSUPP;
1107 }
1108
Kalle Valoddc3d772012-03-07 20:03:58 +02001109 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1110 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1111 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301112 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001113
1114 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1115 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1116 __func__, key_index, key->key_len, key_type,
1117 key_usage, key->seq_len);
1118
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301119 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001120 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301121 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001122 ar->ap_mode_bkey.valid = true;
1123 ar->ap_mode_bkey.key_index = key_index;
1124 ar->ap_mode_bkey.key_type = key_type;
1125 ar->ap_mode_bkey.key_len = key->key_len;
1126 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301127 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001128 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1129 "key configuration until AP mode has been "
1130 "started\n");
1131 /*
1132 * The key will be set in ath6kl_connect_ap_mode() once
1133 * the connected event is received from the target.
1134 */
1135 return 0;
1136 }
1137 }
1138
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301139 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301140 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001141 /*
1142 * Store the key locally so that it can be re-configured after
1143 * the AP mode has properly started
1144 * (ath6kl_install_statioc_wep_keys).
1145 */
1146 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1147 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301148 vif->wep_key_list[key_index].key_len = key->key_len;
1149 memcpy(vif->wep_key_list[key_index].key, key->key,
1150 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001151 return 0;
1152 }
1153
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301154 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001155 key_type, key_usage, key->key_len,
1156 key->seq, key->seq_len, key->key,
1157 KEY_OP_INIT_VAL,
1158 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001159}
1160
1161static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1162 u8 key_index, bool pairwise,
1163 const u8 *mac_addr)
1164{
Kalle Valod6d5c062011-11-25 13:17:37 +02001165 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301166 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001167
1168 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1169
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301170 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001171 return -EIO;
1172
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301173 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001174 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1175 "%s: key index %d out of bounds\n", __func__,
1176 key_index);
1177 return -ENOENT;
1178 }
1179
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301180 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001181 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1182 "%s: index %d is empty\n", __func__, key_index);
1183 return 0;
1184 }
1185
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301186 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001187
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301188 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001189}
1190
1191static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1192 u8 key_index, bool pairwise,
1193 const u8 *mac_addr, void *cookie,
1194 void (*callback) (void *cookie,
1195 struct key_params *))
1196{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301197 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001198 struct ath6kl_key *key = NULL;
1199 struct key_params params;
1200
1201 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1202
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301203 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001204 return -EIO;
1205
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301206 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001207 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1208 "%s: key index %d out of bounds\n", __func__,
1209 key_index);
1210 return -ENOENT;
1211 }
1212
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301213 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001214 memset(&params, 0, sizeof(params));
1215 params.cipher = key->cipher;
1216 params.key_len = key->key_len;
1217 params.seq_len = key->seq_len;
1218 params.seq = key->seq;
1219 params.key = key->key;
1220
1221 callback(cookie, &params);
1222
1223 return key->key_len ? 0 : -ENOENT;
1224}
1225
1226static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1227 struct net_device *ndev,
1228 u8 key_index, bool unicast,
1229 bool multicast)
1230{
Kalle Valod6d5c062011-11-25 13:17:37 +02001231 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301232 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001233 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001234 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001235 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001236
1237 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1238
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301239 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001240 return -EIO;
1241
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301242 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001243 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1244 "%s: key index %d out of bounds\n",
1245 __func__, key_index);
1246 return -ENOENT;
1247 }
1248
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301249 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001250 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1251 __func__, key_index);
1252 return -EINVAL;
1253 }
1254
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301255 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301256 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001257 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301258 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001259 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001260 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301261 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001262 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301263 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001264
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301265 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001266 return 0; /* Delay until AP mode has been started */
1267
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001268 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1269 vif->def_txkey_index,
1270 key_type, key_usage,
1271 key->key_len, key->seq, key->seq_len,
1272 key->key,
1273 KEY_OP_INIT_VAL, NULL,
1274 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001275}
1276
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301277void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001278 bool ismcast)
1279{
1280 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1281 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1282
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301283 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001284 (ismcast ? NL80211_KEYTYPE_GROUP :
1285 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1286 GFP_KERNEL);
1287}
1288
1289static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1290{
1291 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301292 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001293 int ret;
1294
1295 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1296 changed);
1297
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301298 vif = ath6kl_vif_first(ar);
1299 if (!vif)
1300 return -EIO;
1301
1302 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001303 return -EIO;
1304
1305 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1306 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1307 if (ret != 0) {
1308 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1309 return -EIO;
1310 }
1311 }
1312
1313 return 0;
1314}
1315
1316/*
1317 * The type nl80211_tx_power_setting replaces the following
1318 * data type from 2.6.36 onwards
1319*/
1320static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1321 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001322 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001323{
1324 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301325 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001326 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001327
1328 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1329 type, dbm);
1330
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301331 vif = ath6kl_vif_first(ar);
1332 if (!vif)
1333 return -EIO;
1334
1335 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001336 return -EIO;
1337
1338 switch (type) {
1339 case NL80211_TX_POWER_AUTOMATIC:
1340 return 0;
1341 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001342 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001343 break;
1344 default:
1345 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1346 __func__, type);
1347 return -EOPNOTSUPP;
1348 }
1349
Kalle Valod0d670a2012-03-07 20:03:58 +02001350 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001351
1352 return 0;
1353}
1354
1355static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1356{
1357 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301358 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001359
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301360 vif = ath6kl_vif_first(ar);
1361 if (!vif)
1362 return -EIO;
1363
1364 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001365 return -EIO;
1366
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301367 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001368 ar->tx_pwr = 0;
1369
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301370 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001371 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1372 return -EIO;
1373 }
1374
1375 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1376 5 * HZ);
1377
1378 if (signal_pending(current)) {
1379 ath6kl_err("target did not respond\n");
1380 return -EINTR;
1381 }
1382 }
1383
1384 *dbm = ar->tx_pwr;
1385 return 0;
1386}
1387
1388static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1389 struct net_device *dev,
1390 bool pmgmt, int timeout)
1391{
1392 struct ath6kl *ar = ath6kl_priv(dev);
1393 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301394 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001395
1396 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1397 __func__, pmgmt, timeout);
1398
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301399 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001400 return -EIO;
1401
1402 if (pmgmt) {
1403 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1404 mode.pwr_mode = REC_POWER;
1405 } else {
1406 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1407 mode.pwr_mode = MAX_PERF_POWER;
1408 }
1409
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301410 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001411 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001412 ath6kl_err("wmi_powermode_cmd failed\n");
1413 return -EIO;
1414 }
1415
1416 return 0;
1417}
1418
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301419static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1420 char *name,
1421 enum nl80211_iftype type,
1422 u32 *flags,
1423 struct vif_params *params)
1424{
1425 struct ath6kl *ar = wiphy_priv(wiphy);
1426 struct net_device *ndev;
1427 u8 if_idx, nw_type;
1428
Kalle Valo71f96ee2011-11-14 19:31:30 +02001429 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301430 ath6kl_err("Reached maximum number of supported vif\n");
1431 return ERR_PTR(-EINVAL);
1432 }
1433
1434 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1435 ath6kl_err("Not a supported interface type\n");
1436 return ERR_PTR(-EINVAL);
1437 }
1438
1439 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1440 if (!ndev)
1441 return ERR_PTR(-ENOMEM);
1442
1443 ar->num_vif++;
1444
1445 return ndev;
1446}
1447
1448static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1449 struct net_device *ndev)
1450{
1451 struct ath6kl *ar = wiphy_priv(wiphy);
1452 struct ath6kl_vif *vif = netdev_priv(ndev);
1453
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301454 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301455 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301456 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301457
1458 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1459
Kalle Valoc25889e2012-01-17 20:08:27 +02001460 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301461
1462 return 0;
1463}
1464
Kalle Valobdcd8172011-07-18 00:22:30 +03001465static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1466 struct net_device *ndev,
1467 enum nl80211_iftype type, u32 *flags,
1468 struct vif_params *params)
1469{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301470 struct ath6kl_vif *vif = netdev_priv(ndev);
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301471 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001472
1473 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1474
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301475 /*
1476 * Don't bring up p2p on an interface which is not initialized
1477 * for p2p operation where fw does not have capability to switch
1478 * dynamically between non-p2p and p2p type interface.
1479 */
1480 if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
1481 vif->ar->fw_capabilities) &&
1482 (type == NL80211_IFTYPE_P2P_CLIENT ||
1483 type == NL80211_IFTYPE_P2P_GO)) {
1484 if (vif->ar->vif_max == 1) {
1485 if (vif->fw_vif_idx != 0)
1486 return -EINVAL;
1487 else
1488 goto set_iface_type;
1489 }
1490
1491 for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
1492 if (i == vif->fw_vif_idx)
1493 break;
1494 }
1495
1496 if (i == vif->ar->vif_max) {
1497 ath6kl_err("Invalid interface to bring up P2P\n");
1498 return -EINVAL;
1499 }
1500 }
1501
1502set_iface_type:
Kalle Valobdcd8172011-07-18 00:22:30 +03001503 switch (type) {
1504 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301505 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001506 break;
1507 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301508 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001509 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001510 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301511 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001512 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001513 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301514 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001515 break;
1516 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301517 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001518 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001519 default:
1520 ath6kl_err("invalid interface type %u\n", type);
1521 return -EOPNOTSUPP;
1522 }
1523
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301524 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001525
1526 return 0;
1527}
1528
1529static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1530 struct net_device *dev,
1531 struct cfg80211_ibss_params *ibss_param)
1532{
1533 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301534 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001535 int status;
1536
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301537 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001538 return -EIO;
1539
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301540 vif->ssid_len = ibss_param->ssid_len;
1541 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001542
1543 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301544 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001545
1546 if (ibss_param->channel_fixed) {
1547 /*
1548 * TODO: channel_fixed: The channel should be fixed, do not
1549 * search for IBSSs to join on other channels. Target
1550 * firmware does not support this feature, needs to be
1551 * updated.
1552 */
1553 return -EOPNOTSUPP;
1554 }
1555
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301556 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001557 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301558 memcpy(vif->req_bssid, ibss_param->bssid,
1559 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001560
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301561 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001562
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301563 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001564 if (status)
1565 return status;
1566
1567 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301568 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1569 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001570 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301571 ath6kl_set_cipher(vif, 0, true);
1572 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001573 }
1574
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301575 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001576
1577 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1578 "%s: connect called with authmode %d dot11 auth %d"
1579 " PW crypto %d PW crypto len %d GRP crypto %d"
1580 " GRP crypto len %d channel hint %u\n",
1581 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301582 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1583 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301584 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001585
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301586 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301587 vif->dot11_auth_mode, vif->auth_mode,
1588 vif->prwise_crypto,
1589 vif->prwise_crypto_len,
1590 vif->grp_crypto, vif->grp_crypto_len,
1591 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301592 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001593 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301594 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001595
1596 return 0;
1597}
1598
1599static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1600 struct net_device *dev)
1601{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301602 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001603
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301604 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001605 return -EIO;
1606
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301607 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301608 memset(vif->ssid, 0, sizeof(vif->ssid));
1609 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001610
1611 return 0;
1612}
1613
1614static const u32 cipher_suites[] = {
1615 WLAN_CIPHER_SUITE_WEP40,
1616 WLAN_CIPHER_SUITE_WEP104,
1617 WLAN_CIPHER_SUITE_TKIP,
1618 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001619 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001620 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001621};
1622
1623static bool is_rate_legacy(s32 rate)
1624{
1625 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1626 6000, 9000, 12000, 18000, 24000,
1627 36000, 48000, 54000
1628 };
1629 u8 i;
1630
1631 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1632 if (rate == legacy[i])
1633 return true;
1634
1635 return false;
1636}
1637
1638static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1639{
1640 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1641 52000, 58500, 65000, 72200
1642 };
1643 u8 i;
1644
1645 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1646 if (rate == ht20[i]) {
1647 if (i == ARRAY_SIZE(ht20) - 1)
1648 /* last rate uses sgi */
1649 *sgi = true;
1650 else
1651 *sgi = false;
1652
1653 *mcs = i;
1654 return true;
1655 }
1656 }
1657 return false;
1658}
1659
1660static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1661{
1662 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1663 81000, 108000, 121500, 135000,
1664 150000
1665 };
1666 u8 i;
1667
1668 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1669 if (rate == ht40[i]) {
1670 if (i == ARRAY_SIZE(ht40) - 1)
1671 /* last rate uses sgi */
1672 *sgi = true;
1673 else
1674 *sgi = false;
1675
1676 *mcs = i;
1677 return true;
1678 }
1679 }
1680
1681 return false;
1682}
1683
1684static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1685 u8 *mac, struct station_info *sinfo)
1686{
1687 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301688 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001689 long left;
1690 bool sgi;
1691 s32 rate;
1692 int ret;
1693 u8 mcs;
1694
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301695 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001696 return -ENOENT;
1697
1698 if (down_interruptible(&ar->sem))
1699 return -EBUSY;
1700
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301701 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001702
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301703 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001704
1705 if (ret != 0) {
1706 up(&ar->sem);
1707 return -EIO;
1708 }
1709
1710 left = wait_event_interruptible_timeout(ar->event_wq,
1711 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301712 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001713 WMI_TIMEOUT);
1714
1715 up(&ar->sem);
1716
1717 if (left == 0)
1718 return -ETIMEDOUT;
1719 else if (left < 0)
1720 return left;
1721
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301722 if (vif->target_stats.rx_byte) {
1723 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001724 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301725 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001726 sinfo->filled |= STATION_INFO_RX_PACKETS;
1727 }
1728
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301729 if (vif->target_stats.tx_byte) {
1730 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001731 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301732 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001733 sinfo->filled |= STATION_INFO_TX_PACKETS;
1734 }
1735
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301736 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001737 sinfo->filled |= STATION_INFO_SIGNAL;
1738
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301739 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001740
1741 if (is_rate_legacy(rate)) {
1742 sinfo->txrate.legacy = rate / 100;
1743 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1744 if (sgi) {
1745 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1746 sinfo->txrate.mcs = mcs - 1;
1747 } else {
1748 sinfo->txrate.mcs = mcs;
1749 }
1750
1751 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1752 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1753 if (sgi) {
1754 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1755 sinfo->txrate.mcs = mcs - 1;
1756 } else {
1757 sinfo->txrate.mcs = mcs;
1758 }
1759
1760 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1761 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1762 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001763 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1764 "invalid rate from stats: %d\n", rate);
1765 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001766 return 0;
1767 }
1768
1769 sinfo->filled |= STATION_INFO_TX_BITRATE;
1770
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301771 if (test_bit(CONNECTED, &vif->flags) &&
1772 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301773 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001774 sinfo->filled |= STATION_INFO_BSS_PARAM;
1775 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301776 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1777 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001778 }
1779
Kalle Valobdcd8172011-07-18 00:22:30 +03001780 return 0;
1781}
1782
1783static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1784 struct cfg80211_pmksa *pmksa)
1785{
1786 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301787 struct ath6kl_vif *vif = netdev_priv(netdev);
1788
1789 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001790 pmksa->pmkid, true);
1791}
1792
1793static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1794 struct cfg80211_pmksa *pmksa)
1795{
1796 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301797 struct ath6kl_vif *vif = netdev_priv(netdev);
1798
1799 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001800 pmksa->pmkid, false);
1801}
1802
1803static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1804{
1805 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301806 struct ath6kl_vif *vif = netdev_priv(netdev);
1807
1808 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301809 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1810 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001811 return 0;
1812}
1813
Raja Manid91e8ee2012-01-30 17:13:10 +05301814static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1815 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001816{
Raja Manid91e8ee2012-01-30 17:13:10 +05301817 int ret, pos;
1818 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001819 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001820
Raja Manid91e8ee2012-01-30 17:13:10 +05301821 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001822 for (i = 0; i < wow->n_patterns; i++) {
1823
1824 /*
1825 * Convert given nl80211 specific mask value to equivalent
1826 * driver specific mask value and send it to the chip along
1827 * with patterns. For example, If the mask value defined in
1828 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1829 * then equivalent driver specific mask value is
1830 * "0xFF 0x00 0xFF 0x00".
1831 */
1832 memset(&mask, 0, sizeof(mask));
1833 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1834 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1835 mask[pos] = 0xFF;
1836 }
1837 /*
1838 * Note: Pattern's offset is not passed as part of wowlan
1839 * parameter from CFG layer. So it's always passed as ZERO
1840 * to the firmware. It means, given WOW patterns are always
1841 * matched from the first byte of received pkt in the firmware.
1842 */
1843 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301844 vif->fw_vif_idx, WOW_LIST_ID,
1845 wow->patterns[i].pattern_len,
1846 0 /* pattern offset */,
1847 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001848 if (ret)
1849 return ret;
1850 }
1851
Raja Manid91e8ee2012-01-30 17:13:10 +05301852 if (wow->disconnect)
1853 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1854
1855 if (wow->magic_pkt)
1856 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1857
1858 if (wow->gtk_rekey_failure)
1859 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1860
1861 if (wow->eap_identity_req)
1862 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1863
1864 if (wow->four_way_handshake)
1865 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1866
1867 return 0;
1868}
1869
1870static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1871{
1872 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1873 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1874 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1875 0x00, 0x08 };
1876 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1877 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1878 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1879 0x00, 0x7f };
1880 u8 unicst_offset = 0;
1881 static const u8 arp_pattern[] = { 0x08, 0x06 };
1882 static const u8 arp_mask[] = { 0xff, 0xff };
1883 u8 arp_offset = 20;
1884 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1885 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1886 u8 discvr_offset = 38;
1887 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1888 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1892 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1893 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1894 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1899 u8 dhcp_offset = 0;
1900 int ret;
1901
1902 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1903 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1904 vif->fw_vif_idx, WOW_LIST_ID,
1905 sizeof(unicst_pattern), unicst_offset,
1906 unicst_pattern, unicst_mask);
1907 if (ret) {
1908 ath6kl_err("failed to add WOW unicast IP pattern\n");
1909 return ret;
1910 }
1911
1912 /* Setup all ARP pkt pattern */
1913 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1914 vif->fw_vif_idx, WOW_LIST_ID,
1915 sizeof(arp_pattern), arp_offset,
1916 arp_pattern, arp_mask);
1917 if (ret) {
1918 ath6kl_err("failed to add WOW ARP pattern\n");
1919 return ret;
1920 }
1921
1922 /*
1923 * Setup multicast pattern for mDNS 224.0.0.251,
1924 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1925 */
1926 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1927 vif->fw_vif_idx, WOW_LIST_ID,
1928 sizeof(discvr_pattern), discvr_offset,
1929 discvr_pattern, discvr_mask);
1930 if (ret) {
1931 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1932 return ret;
1933 }
1934
1935 /* Setup all DHCP broadcast pkt pattern */
1936 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1937 vif->fw_vif_idx, WOW_LIST_ID,
1938 sizeof(dhcp_pattern), dhcp_offset,
1939 dhcp_pattern, dhcp_mask);
1940 if (ret) {
1941 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1942 return ret;
1943 }
1944
1945 return 0;
1946}
1947
1948static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1949{
1950 struct net_device *ndev = vif->ndev;
1951 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1952 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1953 u8 discvr_offset = 38;
1954 u8 mac_mask[ETH_ALEN];
1955 int ret;
1956
1957 /* Setup unicast pkt pattern */
1958 memset(mac_mask, 0xff, ETH_ALEN);
1959 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1960 vif->fw_vif_idx, WOW_LIST_ID,
1961 ETH_ALEN, 0, ndev->dev_addr,
1962 mac_mask);
1963 if (ret) {
1964 ath6kl_err("failed to add WOW unicast pattern\n");
1965 return ret;
1966 }
1967
1968 /*
1969 * Setup multicast pattern for mDNS 224.0.0.251,
1970 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1971 */
1972 if ((ndev->flags & IFF_ALLMULTI) ||
1973 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1974 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1975 vif->fw_vif_idx, WOW_LIST_ID,
1976 sizeof(discvr_pattern), discvr_offset,
1977 discvr_pattern, discvr_mask);
1978 if (ret) {
1979 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1980 "pattern\n");
1981 return ret;
1982 }
1983 }
1984
1985 return 0;
1986}
1987
Raja Mani055bde42012-03-21 15:03:37 +05301988static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
1989{
1990 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1991}
1992
1993static bool is_ctrl_ep_empty(struct ath6kl *ar)
1994{
1995 return !ar->tx_pending[ar->ctrl_ep];
1996}
1997
1998static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
1999{
2000 int ret, left;
2001
2002 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
2003
2004 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2005 ATH6KL_HOST_MODE_ASLEEP);
2006 if (ret)
2007 return ret;
2008
2009 left = wait_event_interruptible_timeout(ar->event_wq,
2010 is_hsleep_mode_procsed(vif),
2011 WMI_TIMEOUT);
2012 if (left == 0) {
2013 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
2014 ret = -ETIMEDOUT;
2015 } else if (left < 0) {
2016 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
2017 left);
2018 ret = left;
2019 }
2020
2021 if (ar->tx_pending[ar->ctrl_ep]) {
2022 left = wait_event_interruptible_timeout(ar->event_wq,
2023 is_ctrl_ep_empty(ar),
2024 WMI_TIMEOUT);
2025 if (left == 0) {
2026 ath6kl_warn("clear wmi ctrl data timeout\n");
2027 ret = -ETIMEDOUT;
2028 } else if (left < 0) {
2029 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2030 ret = left;
2031 }
2032 }
2033
2034 return ret;
2035}
2036
Raja Manid91e8ee2012-01-30 17:13:10 +05302037static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2038{
2039 struct in_device *in_dev;
2040 struct in_ifaddr *ifa;
2041 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05302042 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05302043 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05302044 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302045 u8 index = 0;
2046 __be32 ips[MAX_IP_ADDRS];
2047
2048 vif = ath6kl_vif_first(ar);
2049 if (!vif)
2050 return -EIO;
2051
2052 if (!ath6kl_cfg80211_ready(vif))
2053 return -EIO;
2054
2055 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302056 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302057
2058 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2059 return -EINVAL;
2060
2061 /* Clear existing WOW patterns */
2062 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2063 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2064 WOW_LIST_ID, i);
2065
2066 /*
2067 * Skip the default WOW pattern configuration
2068 * if the driver receives any WOW patterns from
2069 * the user.
2070 */
2071 if (wow)
2072 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2073 else if (vif->nw_type == AP_NETWORK)
2074 ret = ath6kl_wow_ap(ar, vif);
2075 else
2076 ret = ath6kl_wow_sta(ar, vif);
2077
2078 if (ret)
2079 return ret;
2080
Raja Mani390a8c82012-03-07 11:35:04 +05302081 netif_stop_queue(vif->ndev);
2082
Raja Manice0dc0c2012-02-20 19:08:08 +05302083 if (vif->nw_type != AP_NETWORK) {
2084 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2085 ATH6KL_MAX_WOW_LISTEN_INTL,
2086 0);
2087 if (ret)
2088 return ret;
2089
2090 /* Set listen interval x 15 times as bmiss time */
2091 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2092 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2093 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2094
2095 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2096 bmiss_time, 0);
2097 if (ret)
2098 return ret;
2099
2100 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2101 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2102 0, 0, 0, 0);
2103 if (ret)
2104 return ret;
2105 }
2106
Raja Mani390a8c82012-03-07 11:35:04 +05302107 ar->state = ATH6KL_STATE_SUSPENDING;
2108
Raja Manic08631c2011-12-16 14:24:24 +05302109 /* Setup own IP addr for ARP agent. */
2110 in_dev = __in_dev_get_rtnl(vif->ndev);
2111 if (!in_dev)
2112 goto skip_arp;
2113
2114 ifa = in_dev->ifa_list;
2115 memset(&ips, 0, sizeof(ips));
2116
2117 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2118 while (index < MAX_IP_ADDRS && ifa) {
2119 ips[index] = ifa->ifa_local;
2120 ifa = ifa->ifa_next;
2121 index++;
2122 }
2123
2124 if (ifa) {
2125 ath6kl_err("total IP addr count is exceeding fw limit\n");
2126 return -EINVAL;
2127 }
2128
2129 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2130 if (ret) {
2131 ath6kl_err("fail to setup ip for arp agent\n");
2132 return ret;
2133 }
2134
2135skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002136 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2137 ATH6KL_WOW_MODE_ENABLE,
2138 filter,
2139 WOW_HOST_REQ_DELAY);
2140 if (ret)
2141 return ret;
2142
Raja Mani055bde42012-03-21 15:03:37 +05302143 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002144 if (ret)
2145 return ret;
2146
Raja Mani055bde42012-03-21 15:03:37 +05302147 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002148}
2149
2150static int ath6kl_wow_resume(struct ath6kl *ar)
2151{
2152 struct ath6kl_vif *vif;
2153 int ret;
2154
2155 vif = ath6kl_vif_first(ar);
2156 if (!vif)
2157 return -EIO;
2158
Raja Mani390a8c82012-03-07 11:35:04 +05302159 ar->state = ATH6KL_STATE_RESUMING;
2160
Raja Mani6cb3c712011-11-07 22:52:45 +02002161 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2162 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302163 if (ret) {
2164 ath6kl_warn("Failed to configure host sleep mode for "
2165 "wow resume: %d\n", ret);
2166 ar->state = ATH6KL_STATE_WOW;
2167 return ret;
2168 }
2169
Raja Manice0dc0c2012-02-20 19:08:08 +05302170 if (vif->nw_type != AP_NETWORK) {
2171 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2172 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2173 if (ret)
2174 return ret;
2175
2176 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2177 vif->listen_intvl_t, 0);
2178 if (ret)
2179 return ret;
2180
2181 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2182 vif->bmiss_time_t, 0);
2183 if (ret)
2184 return ret;
2185 }
2186
Raja Mani390a8c82012-03-07 11:35:04 +05302187 ar->state = ATH6KL_STATE_ON;
2188
2189 netif_wake_queue(vif->ndev);
2190
2191 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002192}
2193
Raja Mani40abc2d2012-03-21 15:03:38 +05302194static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2195{
2196 struct ath6kl_vif *vif;
2197 int ret;
2198
2199 vif = ath6kl_vif_first(ar);
2200 if (!vif)
2201 return -EIO;
2202
2203 if (!ath6kl_cfg80211_ready(vif))
2204 return -EIO;
2205
2206 ath6kl_cfg80211_stop_all(ar);
2207
2208 /* Save the current power mode before enabling power save */
2209 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2210
2211 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2212 if (ret)
2213 return ret;
2214
2215 /* Disable WOW mode */
2216 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2217 ATH6KL_WOW_MODE_DISABLE,
2218 0, 0);
2219 if (ret)
2220 return ret;
2221
2222 /* Flush all non control pkts in TX path */
2223 ath6kl_tx_data_cleanup(ar);
2224
2225 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2226 if (ret)
2227 return ret;
2228
2229 return 0;
2230}
2231
2232static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2233{
2234 struct ath6kl_vif *vif;
2235 int ret;
2236
2237 vif = ath6kl_vif_first(ar);
2238
2239 if (!vif)
2240 return -EIO;
2241
2242 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2243 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2244 ar->wmi->saved_pwr_mode);
2245 if (ret)
2246 return ret;
2247 }
2248
2249 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2250 ATH6KL_HOST_MODE_AWAKE);
2251 if (ret)
2252 return ret;
2253
2254 ar->state = ATH6KL_STATE_ON;
2255
2256 /* Reset scan parameter to default values */
2257 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2258 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2259 if (ret)
2260 return ret;
2261
2262 return 0;
2263}
2264
Kalle Valo52d81a62011-11-01 08:44:21 +02002265int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002266 enum ath6kl_cfg_suspend_mode mode,
2267 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002268{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302269 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302270 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002271 int ret;
2272
Kalle Valo52d81a62011-11-01 08:44:21 +02002273 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002274 case ATH6KL_CFG_SUSPEND_WOW:
2275
2276 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2277
2278 /* Flush all non control pkts in TX path */
2279 ath6kl_tx_data_cleanup(ar);
2280
Raja Mani390a8c82012-03-07 11:35:04 +05302281 prev_state = ar->state;
2282
Raja Manid7c44e02011-11-07 22:52:46 +02002283 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302284 if (ret) {
2285 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002286 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302287 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302288
Raja Manid7c44e02011-11-07 22:52:46 +02002289 ar->state = ATH6KL_STATE_WOW;
2290 break;
2291
Kalle Valo52d81a62011-11-01 08:44:21 +02002292 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002293
Raja Mani40abc2d2012-03-21 15:03:38 +05302294 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002295
Raja Mani40abc2d2012-03-21 15:03:38 +05302296 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002297 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302298 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2299 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002300 }
2301
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002302 ar->state = ATH6KL_STATE_DEEPSLEEP;
2303
Kalle Valo52d81a62011-11-01 08:44:21 +02002304 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002305
2306 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002307
Kalle Valo7125f012011-12-13 14:51:37 +02002308 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002309
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002310 if (ar->state == ATH6KL_STATE_OFF) {
2311 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2312 "suspend hw off, no action for cutpower\n");
2313 break;
2314 }
2315
2316 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2317
2318 ret = ath6kl_init_hw_stop(ar);
2319 if (ret) {
2320 ath6kl_warn("failed to stop hw during suspend: %d\n",
2321 ret);
2322 }
2323
2324 ar->state = ATH6KL_STATE_CUTPOWER;
2325
2326 break;
2327
Kalle Valo10509f92011-12-13 14:52:07 +02002328 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2329 /*
2330 * Nothing needed for schedule scan, firmware is already in
2331 * wow mode and sleeping most of the time.
2332 */
2333 break;
2334
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002335 default:
2336 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002337 }
2338
Vivek Natarajan3d794992012-03-28 19:21:26 +05302339 list_for_each_entry(vif, &ar->vif_list, list)
2340 ath6kl_cfg80211_scan_complete_event(vif, true);
2341
Kalle Valo52d81a62011-11-01 08:44:21 +02002342 return 0;
2343}
Kalle Valod6a434d2012-01-17 20:09:36 +02002344EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002345
2346int ath6kl_cfg80211_resume(struct ath6kl *ar)
2347{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002348 int ret;
2349
2350 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002351 case ATH6KL_STATE_WOW:
2352 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2353
2354 ret = ath6kl_wow_resume(ar);
2355 if (ret) {
2356 ath6kl_warn("wow mode resume failed: %d\n", ret);
2357 return ret;
2358 }
2359
Raja Manid7c44e02011-11-07 22:52:46 +02002360 break;
2361
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002362 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302363 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2364
2365 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2366 if (ret) {
2367 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2368 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002369 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002370 break;
2371
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002372 case ATH6KL_STATE_CUTPOWER:
2373 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2374
2375 ret = ath6kl_init_hw_start(ar);
2376 if (ret) {
2377 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2378 return ret;
2379 }
Raja Manid7c44e02011-11-07 22:52:46 +02002380 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002381
Kalle Valo10509f92011-12-13 14:52:07 +02002382 case ATH6KL_STATE_SCHED_SCAN:
2383 break;
2384
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002385 default:
2386 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002387 }
2388
2389 return 0;
2390}
Kalle Valod6a434d2012-01-17 20:09:36 +02002391EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002392
Kalle Valoabcb3442011-07-22 08:26:20 +03002393#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002394
2395/* hif layer decides what suspend mode to use */
2396static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002397 struct cfg80211_wowlan *wow)
2398{
2399 struct ath6kl *ar = wiphy_priv(wiphy);
2400
Raja Mani0f60e9f2011-11-07 22:52:45 +02002401 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002402}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002403
Kalle Valo52d81a62011-11-01 08:44:21 +02002404static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002405{
2406 struct ath6kl *ar = wiphy_priv(wiphy);
2407
2408 return ath6kl_hif_resume(ar);
2409}
Raja Mania918fb32011-11-07 22:52:46 +02002410
2411/*
2412 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2413 * both sdio irq wake up and keep power. The target pulls sdio data line to
2414 * wake up the host when WOW pattern matches. This causes sdio irq handler
2415 * is being called in the host side which internally hits ath6kl's RX path.
2416 *
2417 * Since sdio interrupt is not disabled, RX path executes even before
2418 * the host executes the actual resume operation from PM module.
2419 *
2420 * In the current scenario, WOW resume should happen before start processing
2421 * any data from the target. So It's required to perform WOW resume in RX path.
2422 * Ideally we should perform WOW resume only in the actual platform
2423 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2424 *
2425 * ath6kl_check_wow_status() is called from ath6kl_rx().
2426 */
2427void ath6kl_check_wow_status(struct ath6kl *ar)
2428{
Raja Mani390a8c82012-03-07 11:35:04 +05302429 if (ar->state == ATH6KL_STATE_SUSPENDING)
2430 return;
2431
Raja Mania918fb32011-11-07 22:52:46 +02002432 if (ar->state == ATH6KL_STATE_WOW)
2433 ath6kl_cfg80211_resume(ar);
2434}
2435
2436#else
2437
2438void ath6kl_check_wow_status(struct ath6kl *ar)
2439{
2440}
Kalle Valoabcb3442011-07-22 08:26:20 +03002441#endif
2442
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302443static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2444 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002445{
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302446 struct ath6kl_htcap *htcap = &vif->htcap;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302447
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302448 if (htcap->ht_enable == ht_enable)
2449 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302450
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302451 if (ht_enable) {
2452 /* Set default ht capabilities */
2453 htcap->ht_enable = true;
2454 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2455 ath6kl_g_htcap : ath6kl_a_htcap;
2456 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2457 } else /* Disable ht */
2458 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002459
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302460 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2461 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002462}
2463
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002464static bool ath6kl_is_p2p_ie(const u8 *pos)
2465{
2466 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2467 pos[2] == 0x50 && pos[3] == 0x6f &&
2468 pos[4] == 0x9a && pos[5] == 0x09;
2469}
2470
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302471static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2472 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002473{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302474 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002475 const u8 *pos;
2476 u8 *buf = NULL;
2477 size_t len = 0;
2478 int ret;
2479
2480 /*
2481 * Filter out P2P IE(s) since they will be included depending on
2482 * the Probe Request frame in ath6kl_send_go_probe_resp().
2483 */
2484
2485 if (ies && ies_len) {
2486 buf = kmalloc(ies_len, GFP_KERNEL);
2487 if (buf == NULL)
2488 return -ENOMEM;
2489 pos = ies;
2490 while (pos + 1 < ies + ies_len) {
2491 if (pos + 2 + pos[1] > ies + ies_len)
2492 break;
2493 if (!ath6kl_is_p2p_ie(pos)) {
2494 memcpy(buf + len, pos, 2 + pos[1]);
2495 len += 2 + pos[1];
2496 }
2497 pos += 2 + pos[1];
2498 }
2499 }
2500
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302501 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2502 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002503 kfree(buf);
2504 return ret;
2505}
2506
Johannes Berg88600202012-02-13 15:17:18 +01002507static int ath6kl_set_ies(struct ath6kl_vif *vif,
2508 struct cfg80211_beacon_data *info)
2509{
2510 struct ath6kl *ar = vif->ar;
2511 int res;
2512
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002513 /* this also clears IE in fw if it's not set */
2514 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2515 WMI_FRAME_BEACON,
2516 info->beacon_ies,
2517 info->beacon_ies_len);
2518 if (res)
2519 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002520
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002521 /* this also clears IE in fw if it's not set */
2522 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2523 info->proberesp_ies_len);
2524 if (res)
2525 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002526
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002527 /* this also clears IE in fw if it's not set */
2528 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2529 WMI_FRAME_ASSOC_RESP,
2530 info->assocresp_ies,
2531 info->assocresp_ies_len);
2532 if (res)
2533 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002534
2535 return 0;
2536}
2537
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302538static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2539 struct ieee80211_channel *chan,
2540 enum nl80211_channel_type channel_type)
2541{
2542 struct ath6kl_vif *vif;
2543
2544 /*
2545 * 'dev' could be NULL if a channel change is required for the hardware
2546 * device itself, instead of a particular VIF.
2547 *
2548 * FIXME: To be handled properly when monitor mode is supported.
2549 */
2550 if (!dev)
2551 return -EBUSY;
2552
2553 vif = netdev_priv(dev);
2554
2555 if (!ath6kl_cfg80211_ready(vif))
2556 return -EIO;
2557
2558 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2559 __func__, chan->center_freq, chan->hw_value);
2560 vif->next_chan = chan->center_freq;
2561 vif->next_ch_type = channel_type;
2562 vif->next_ch_band = chan->band;
2563
2564 return 0;
2565}
2566
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302567static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2568 u8 *rsn_capab)
2569{
2570 const u8 *rsn_ie;
2571 size_t rsn_ie_len;
2572 u16 cnt;
2573
2574 if (!beacon->tail)
2575 return -EINVAL;
2576
2577 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2578 if (!rsn_ie)
2579 return -EINVAL;
2580
2581 rsn_ie_len = *(rsn_ie + 1);
2582 /* skip element id and length */
2583 rsn_ie += 2;
2584
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302585 /* skip version */
2586 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302587 return -EINVAL;
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302588 rsn_ie += 2;
2589 rsn_ie_len -= 2;
2590
2591 /* skip group cipher suite */
2592 if (rsn_ie_len < 4)
2593 return 0;
2594 rsn_ie += 4;
2595 rsn_ie_len -= 4;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302596
2597 /* skip pairwise cipher suite */
2598 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302599 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302600 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302601 rsn_ie += (2 + cnt * 4);
2602 rsn_ie_len -= (2 + cnt * 4);
2603
2604 /* skip akm suite */
2605 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302606 return 0;
Vasanthakumar Thiagarajan798985c2012-04-10 13:35:47 +05302607 cnt = get_unaligned_le16(rsn_ie);
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302608 rsn_ie += (2 + cnt * 4);
2609 rsn_ie_len -= (2 + cnt * 4);
2610
2611 if (rsn_ie_len < 2)
Vasanthakumar Thiagarajan9e8b16d2012-04-10 14:27:47 +05302612 return 0;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302613
2614 memcpy(rsn_capab, rsn_ie, 2);
2615
2616 return 0;
2617}
2618
Johannes Berg88600202012-02-13 15:17:18 +01002619static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2620 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002621{
2622 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302623 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002624 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002625 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002626 u8 *ies;
2627 int ies_len;
2628 struct wmi_connect_cmd p;
2629 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302630 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302631 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002632
Johannes Berg88600202012-02-13 15:17:18 +01002633 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002634
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302635 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002636 return -EIO;
2637
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302638 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002639 return -EOPNOTSUPP;
2640
Johannes Berg88600202012-02-13 15:17:18 +01002641 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002642
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002643 ar->ap_mode_bkey.valid = false;
2644
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002645 /* TODO:
2646 * info->interval
2647 * info->dtim_period
2648 */
2649
Johannes Berg88600202012-02-13 15:17:18 +01002650 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002651 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002652 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002653 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002654 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002655 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002656 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002657
2658 if (info->ssid == NULL)
2659 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302660 memcpy(vif->ssid, info->ssid, info->ssid_len);
2661 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002662 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002663 hidden = true;
2664
2665 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2666 if (res)
2667 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002668
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302669 ret = ath6kl_set_auth_type(vif, info->auth_type);
2670 if (ret)
2671 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002672
2673 memset(&p, 0, sizeof(p));
2674
2675 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2676 switch (info->crypto.akm_suites[i]) {
2677 case WLAN_AKM_SUITE_8021X:
2678 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2679 p.auth_mode |= WPA_AUTH;
2680 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2681 p.auth_mode |= WPA2_AUTH;
2682 break;
2683 case WLAN_AKM_SUITE_PSK:
2684 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2685 p.auth_mode |= WPA_PSK_AUTH;
2686 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2687 p.auth_mode |= WPA2_PSK_AUTH;
2688 break;
2689 }
2690 }
2691 if (p.auth_mode == 0)
2692 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302693 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002694
2695 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2696 switch (info->crypto.ciphers_pairwise[i]) {
2697 case WLAN_CIPHER_SUITE_WEP40:
2698 case WLAN_CIPHER_SUITE_WEP104:
2699 p.prwise_crypto_type |= WEP_CRYPT;
2700 break;
2701 case WLAN_CIPHER_SUITE_TKIP:
2702 p.prwise_crypto_type |= TKIP_CRYPT;
2703 break;
2704 case WLAN_CIPHER_SUITE_CCMP:
2705 p.prwise_crypto_type |= AES_CRYPT;
2706 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002707 case WLAN_CIPHER_SUITE_SMS4:
2708 p.prwise_crypto_type |= WAPI_CRYPT;
2709 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002710 }
2711 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002712 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002713 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302714 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002715 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302716 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002717
2718 switch (info->crypto.cipher_group) {
2719 case WLAN_CIPHER_SUITE_WEP40:
2720 case WLAN_CIPHER_SUITE_WEP104:
2721 p.grp_crypto_type = WEP_CRYPT;
2722 break;
2723 case WLAN_CIPHER_SUITE_TKIP:
2724 p.grp_crypto_type = TKIP_CRYPT;
2725 break;
2726 case WLAN_CIPHER_SUITE_CCMP:
2727 p.grp_crypto_type = AES_CRYPT;
2728 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002729 case WLAN_CIPHER_SUITE_SMS4:
2730 p.grp_crypto_type = WAPI_CRYPT;
2731 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002732 default:
2733 p.grp_crypto_type = NONE_CRYPT;
2734 break;
2735 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302736 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002737
2738 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302739 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002740
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302741 p.ssid_len = vif->ssid_len;
2742 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2743 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302744 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002745
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302746 /* Enable uAPSD support by default */
2747 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2748 if (res < 0)
2749 return res;
2750
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002751 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2752 p.nw_subtype = SUBTYPE_P2PGO;
2753 } else {
2754 /*
2755 * Due to firmware limitation, it is not possible to
2756 * do P2P mgmt operations in AP mode
2757 */
2758 p.nw_subtype = SUBTYPE_NONE;
2759 }
2760
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302761 if (info->inactivity_timeout) {
2762 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2763 info->inactivity_timeout);
2764 if (res < 0)
2765 return res;
2766 }
2767
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302768 if (ath6kl_set_htcap(vif, vif->next_ch_band,
2769 vif->next_ch_type != NL80211_CHAN_NO_HT))
2770 return -EIO;
2771
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302772 /*
2773 * Get the PTKSA replay counter in the RSN IE. Supplicant
2774 * will use the RSN IE in M3 message and firmware has to
2775 * advertise the same in beacon/probe response. Send
2776 * the complete RSN IE capability field to firmware
2777 */
2778 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2779 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2780 ar->fw_capabilities)) {
2781 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2782 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2783 (const u8 *) &rsn_capab,
2784 sizeof(rsn_capab));
2785 if (res < 0)
2786 return res;
2787 }
2788
Thomas Pedersenc4f78632012-04-06 13:35:48 -07002789 memcpy(&vif->profile, &p, sizeof(p));
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302790 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002791 if (res < 0)
2792 return res;
2793
2794 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002795}
2796
Johannes Berg88600202012-02-13 15:17:18 +01002797static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2798 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002799{
Johannes Berg88600202012-02-13 15:17:18 +01002800 struct ath6kl_vif *vif = netdev_priv(dev);
2801
2802 if (!ath6kl_cfg80211_ready(vif))
2803 return -EIO;
2804
2805 if (vif->next_mode != AP_NETWORK)
2806 return -EOPNOTSUPP;
2807
2808 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002809}
2810
Johannes Berg88600202012-02-13 15:17:18 +01002811static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002812{
2813 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302814 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002815
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302816 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002817 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302818 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002819 return -ENOTCONN;
2820
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302821 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302822 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002823
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302824 /* Restore ht setting in firmware */
2825 if (ath6kl_set_htcap(vif, IEEE80211_BAND_2GHZ, true))
2826 return -EIO;
2827
2828 if (ath6kl_set_htcap(vif, IEEE80211_BAND_5GHZ, true))
2829 return -EIO;
2830
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002831 return 0;
2832}
2833
Jouni Malinen33e53082011-12-27 11:02:56 +02002834static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2835
2836static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2837 u8 *mac)
2838{
2839 struct ath6kl *ar = ath6kl_priv(dev);
2840 struct ath6kl_vif *vif = netdev_priv(dev);
2841 const u8 *addr = mac ? mac : bcast_addr;
2842
2843 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2844 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2845}
2846
Jouni Malinen23875132011-08-30 21:57:53 +03002847static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2848 u8 *mac, struct station_parameters *params)
2849{
2850 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302851 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002852
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302853 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002854 return -EOPNOTSUPP;
2855
2856 /* Use this only for authorizing/unauthorizing a station */
2857 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2858 return -EOPNOTSUPP;
2859
2860 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302861 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2862 WMI_AP_MLME_AUTHORIZE, mac, 0);
2863 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2864 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002865}
2866
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002867static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2868 struct net_device *dev,
2869 struct ieee80211_channel *chan,
2870 enum nl80211_channel_type channel_type,
2871 unsigned int duration,
2872 u64 *cookie)
2873{
2874 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302875 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002876 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002877
2878 /* TODO: if already pending or ongoing remain-on-channel,
2879 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002880 id = ++vif->last_roc_id;
2881 if (id == 0) {
2882 /* Do not use 0 as the cookie value */
2883 id = ++vif->last_roc_id;
2884 }
2885 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002886
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302887 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2888 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002889}
2890
2891static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2892 struct net_device *dev,
2893 u64 cookie)
2894{
2895 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302896 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002897
Jouni Malinen10522612011-10-27 16:00:13 +03002898 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002899 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002900 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002901
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302902 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002903}
2904
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302905static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2906 const u8 *buf, size_t len,
2907 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002908{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302909 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002910 const u8 *pos;
2911 u8 *p2p;
2912 int p2p_len;
2913 int ret;
2914 const struct ieee80211_mgmt *mgmt;
2915
2916 mgmt = (const struct ieee80211_mgmt *) buf;
2917
2918 /* Include P2P IE(s) from the frame generated in user space. */
2919
2920 p2p = kmalloc(len, GFP_KERNEL);
2921 if (p2p == NULL)
2922 return -ENOMEM;
2923 p2p_len = 0;
2924
2925 pos = mgmt->u.probe_resp.variable;
2926 while (pos + 1 < buf + len) {
2927 if (pos + 2 + pos[1] > buf + len)
2928 break;
2929 if (ath6kl_is_p2p_ie(pos)) {
2930 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2931 p2p_len += 2 + pos[1];
2932 }
2933 pos += 2 + pos[1];
2934 }
2935
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302936 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2937 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002938 kfree(p2p);
2939 return ret;
2940}
2941
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002942static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2943 u32 id,
2944 u32 freq,
2945 u32 wait,
2946 const u8 *buf,
2947 size_t len,
2948 bool *more_data,
2949 bool no_cck)
2950{
2951 struct ieee80211_mgmt *mgmt;
2952 struct ath6kl_sta *conn;
2953 bool is_psq_empty = false;
2954 struct ath6kl_mgmt_buff *mgmt_buf;
2955 size_t mgmt_buf_size;
2956 struct ath6kl *ar = vif->ar;
2957
2958 mgmt = (struct ieee80211_mgmt *) buf;
2959 if (is_multicast_ether_addr(mgmt->da))
2960 return false;
2961
2962 conn = ath6kl_find_sta(vif, mgmt->da);
2963 if (!conn)
2964 return false;
2965
2966 if (conn->sta_flags & STA_PS_SLEEP) {
2967 if (!(conn->sta_flags & STA_PS_POLLED)) {
2968 /* Queue the frames if the STA is sleeping */
2969 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2970 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2971 if (!mgmt_buf)
2972 return false;
2973
2974 INIT_LIST_HEAD(&mgmt_buf->list);
2975 mgmt_buf->id = id;
2976 mgmt_buf->freq = freq;
2977 mgmt_buf->wait = wait;
2978 mgmt_buf->len = len;
2979 mgmt_buf->no_cck = no_cck;
2980 memcpy(mgmt_buf->buf, buf, len);
2981 spin_lock_bh(&conn->psq_lock);
2982 is_psq_empty = skb_queue_empty(&conn->psq) &&
2983 (conn->mgmt_psq_len == 0);
2984 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2985 conn->mgmt_psq_len++;
2986 spin_unlock_bh(&conn->psq_lock);
2987
2988 /*
2989 * If this is the first pkt getting queued
2990 * for this STA, update the PVB for this
2991 * STA.
2992 */
2993 if (is_psq_empty)
2994 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2995 conn->aid, 1);
2996 return true;
2997 }
2998
2999 /*
3000 * This tx is because of a PsPoll.
3001 * Determine if MoreData bit has to be set.
3002 */
3003 spin_lock_bh(&conn->psq_lock);
3004 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
3005 *more_data = true;
3006 spin_unlock_bh(&conn->psq_lock);
3007 }
3008
3009 return false;
3010}
3011
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003012/* Check if SSID length is greater than DIRECT- */
3013static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
3014{
3015 const struct ieee80211_mgmt *mgmt;
3016 mgmt = (const struct ieee80211_mgmt *) buf;
3017
3018 /* variable[1] contains the SSID tag length */
3019 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
3020 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
3021 return true;
3022 }
3023
3024 return false;
3025}
3026
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003027static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
3028 struct ieee80211_channel *chan, bool offchan,
3029 enum nl80211_channel_type channel_type,
3030 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01003031 const u8 *buf, size_t len, bool no_cck,
3032 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003033{
3034 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303035 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003036 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003037 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003038 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003039
3040 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003041 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3042 ieee80211_is_probe_resp(mgmt->frame_control) &&
3043 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003044 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003045 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003046 * command to allow the target to fill in the generic IEs.
3047 */
3048 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303049 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003050 chan->center_freq);
3051 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003052
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303053 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003054 if (id == 0) {
3055 /*
3056 * 0 is a reserved value in the WMI command and shall not be
3057 * used for the command.
3058 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303059 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003060 }
3061
3062 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003063
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003064 /* AP mode Power saving processing */
3065 if (vif->nw_type == AP_NETWORK) {
3066 queued = ath6kl_mgmt_powersave_ap(vif,
3067 id, chan->center_freq,
3068 wait, buf,
3069 len, &more_data, no_cck);
3070 if (queued)
3071 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003072 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003073
3074 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3075 chan->center_freq, wait,
3076 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003077}
3078
Jouni Malinenae32c302011-08-30 21:58:01 +03003079static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
3080 struct net_device *dev,
3081 u16 frame_type, bool reg)
3082{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303083 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003084
3085 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3086 __func__, frame_type, reg);
3087 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3088 /*
3089 * Note: This notification callback is not allowed to sleep, so
3090 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3091 * hardcode target to report Probe Request frames all the time.
3092 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303093 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003094 }
3095}
3096
Kalle Valo10509f92011-12-13 14:52:07 +02003097static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3098 struct net_device *dev,
3099 struct cfg80211_sched_scan_request *request)
3100{
3101 struct ath6kl *ar = ath6kl_priv(dev);
3102 struct ath6kl_vif *vif = netdev_priv(dev);
3103 u16 interval;
3104 int ret;
3105 u8 i;
3106
3107 if (ar->state != ATH6KL_STATE_ON)
3108 return -EIO;
3109
3110 if (vif->sme_state != SME_DISCONNECTED)
3111 return -EBUSY;
3112
Kalle Valob4d13d32012-03-21 10:01:09 +02003113 ath6kl_cfg80211_scan_complete_event(vif, true);
3114
Kalle Valo10509f92011-12-13 14:52:07 +02003115 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
3116 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
3117 i, DISABLE_SSID_FLAG,
3118 0, NULL);
3119 }
3120
3121 /* fw uses seconds, also make sure that it's >0 */
3122 interval = max_t(u16, 1, request->interval / 1000);
3123
3124 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3125 interval, interval,
3126 10, 0, 0, 0, 3, 0, 0, 0);
3127
3128 if (request->n_ssids && request->ssids[0].ssid_len) {
3129 for (i = 0; i < request->n_ssids; i++) {
3130 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
3131 i, SPECIFIC_SSID_FLAG,
3132 request->ssids[i].ssid_len,
3133 request->ssids[i].ssid);
3134 }
3135 }
3136
3137 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3138 ATH6KL_WOW_MODE_ENABLE,
3139 WOW_FILTER_SSID,
3140 WOW_HOST_REQ_DELAY);
3141 if (ret) {
3142 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3143 return ret;
3144 }
3145
3146 /* this also clears IE in fw if it's not set */
3147 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3148 WMI_FRAME_PROBE_REQ,
3149 request->ie, request->ie_len);
3150 if (ret) {
3151 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
3152 ret);
3153 return ret;
3154 }
3155
3156 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3157 ATH6KL_HOST_MODE_ASLEEP);
3158 if (ret) {
3159 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3160 ret);
3161 return ret;
3162 }
3163
3164 ar->state = ATH6KL_STATE_SCHED_SCAN;
3165
3166 return ret;
3167}
3168
3169static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3170 struct net_device *dev)
3171{
3172 struct ath6kl_vif *vif = netdev_priv(dev);
3173 bool stopped;
3174
3175 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3176
3177 if (!stopped)
3178 return -EIO;
3179
3180 return 0;
3181}
3182
Jouni Malinenf80574a2011-08-30 21:58:04 +03003183static const struct ieee80211_txrx_stypes
3184ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3185 [NL80211_IFTYPE_STATION] = {
3186 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3187 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3188 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3189 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3190 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003191 [NL80211_IFTYPE_AP] = {
3192 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3193 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3194 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3195 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3196 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003197 [NL80211_IFTYPE_P2P_CLIENT] = {
3198 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3199 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3200 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3201 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3202 },
3203 [NL80211_IFTYPE_P2P_GO] = {
3204 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3205 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3206 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3207 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3208 },
3209};
3210
Kalle Valobdcd8172011-07-18 00:22:30 +03003211static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303212 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3213 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003214 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3215 .scan = ath6kl_cfg80211_scan,
3216 .connect = ath6kl_cfg80211_connect,
3217 .disconnect = ath6kl_cfg80211_disconnect,
3218 .add_key = ath6kl_cfg80211_add_key,
3219 .get_key = ath6kl_cfg80211_get_key,
3220 .del_key = ath6kl_cfg80211_del_key,
3221 .set_default_key = ath6kl_cfg80211_set_default_key,
3222 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3223 .set_tx_power = ath6kl_cfg80211_set_txpower,
3224 .get_tx_power = ath6kl_cfg80211_get_txpower,
3225 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3226 .join_ibss = ath6kl_cfg80211_join_ibss,
3227 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3228 .get_station = ath6kl_get_station,
3229 .set_pmksa = ath6kl_set_pmksa,
3230 .del_pmksa = ath6kl_del_pmksa,
3231 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003232 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003233#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003234 .suspend = __ath6kl_cfg80211_suspend,
3235 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003236#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003237 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003238 .start_ap = ath6kl_start_ap,
3239 .change_beacon = ath6kl_change_beacon,
3240 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003241 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003242 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003243 .remain_on_channel = ath6kl_remain_on_channel,
3244 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003245 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003246 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003247 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3248 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003249};
3250
Kalle Valo7125f012011-12-13 14:51:37 +02003251void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003252{
Kalle Valo10509f92011-12-13 14:52:07 +02003253 ath6kl_cfg80211_sscan_disable(vif);
3254
Kalle Valoec4b7f62011-11-01 08:44:04 +02003255 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003256 case SME_DISCONNECTED:
3257 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003258 case SME_CONNECTING:
3259 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3260 NULL, 0,
3261 WLAN_STATUS_UNSPECIFIED_FAILURE,
3262 GFP_KERNEL);
3263 break;
3264 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003265 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3266 break;
3267 }
3268
3269 if (test_bit(CONNECTED, &vif->flags) ||
3270 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003271 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003272
3273 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003274 clear_bit(CONNECTED, &vif->flags);
3275 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003276
3277 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003278 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3279 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3280 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003281
3282 ath6kl_cfg80211_scan_complete_event(vif, true);
3283}
3284
Kalle Valo7125f012011-12-13 14:51:37 +02003285void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3286{
3287 struct ath6kl_vif *vif;
3288
3289 vif = ath6kl_vif_first(ar);
3290 if (!vif) {
3291 /* save the current power mode before enabling power save */
3292 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3293
3294 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3295 ath6kl_warn("ath6kl_deep_sleep_enable: "
3296 "wmi_powermode_cmd failed\n");
3297 return;
3298 }
3299
3300 /*
3301 * FIXME: we should take ar->list_lock to protect changes in the
3302 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3303 * sleeps.
3304 */
3305 list_for_each_entry(vif, &ar->vif_list, list)
3306 ath6kl_cfg80211_stop(vif);
3307}
3308
Kalle Valoc25889e2012-01-17 20:08:27 +02003309static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003310{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303311 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303312 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303313 ath6kl_err("failed to initialize aggr\n");
3314 return -ENOMEM;
3315 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003316
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303317 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303318 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003319 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3320 (unsigned long) vif);
3321
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303322 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303323 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303324
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303325 INIT_LIST_HEAD(&vif->mc_filter);
3326
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303327 return 0;
3328}
3329
Kalle Valoc25889e2012-01-17 20:08:27 +02003330void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303331{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303332 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303333 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303334
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303335 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303336
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303337 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3338
3339 if (vif->nw_type == ADHOC_NETWORK)
3340 ar->ibss_if_active = false;
3341
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303342 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3343 list_del(&mc_filter->list);
3344 kfree(mc_filter);
3345 }
3346
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303347 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303348
3349 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303350}
3351
3352struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303353 enum nl80211_iftype type, u8 fw_vif_idx,
3354 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303355{
3356 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303357 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303358
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303359 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303360 if (!ndev)
3361 return NULL;
3362
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303363 vif = netdev_priv(ndev);
3364 ndev->ieee80211_ptr = &vif->wdev;
3365 vif->wdev.wiphy = ar->wiphy;
3366 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303367 vif->ndev = ndev;
3368 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3369 vif->wdev.netdev = ndev;
3370 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303371 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003372 vif->nw_type = nw_type;
3373 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303374 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303375 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05303376 vif->htcap.ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303377
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303378 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3379 if (fw_vif_idx != 0)
3380 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3381 0x2;
3382
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303383 init_netdev(ndev);
3384
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303385 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303386
Kalle Valoc25889e2012-01-17 20:08:27 +02003387 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303388 goto err;
3389
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303390 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303391 goto err;
3392
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303393 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303394 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303395 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303396 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303397 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303398
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303399 if (type == NL80211_IFTYPE_ADHOC)
3400 ar->ibss_if_active = true;
3401
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303402 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303403 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303404 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303405
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303406 return ndev;
3407
3408err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303409 aggr_module_destroy(vif->aggr_cntxt);
3410 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303411 return NULL;
3412}
3413
Kalle Valo46d33a22012-01-17 20:08:40 +02003414int ath6kl_cfg80211_init(struct ath6kl *ar)
3415{
3416 struct wiphy *wiphy = ar->wiphy;
3417 int ret;
3418
3419 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3420
3421 wiphy->max_remain_on_channel_duration = 5000;
3422
3423 /* set device pointer for wiphy */
3424 set_wiphy_dev(wiphy, ar->dev);
3425
3426 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3427 BIT(NL80211_IFTYPE_ADHOC) |
3428 BIT(NL80211_IFTYPE_AP);
3429 if (ar->p2p) {
3430 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3431 BIT(NL80211_IFTYPE_P2P_CLIENT);
3432 }
3433
3434 /* max num of ssids that can be probed during scanning */
3435 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3436 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3437 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3438 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3439 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3440
3441 wiphy->cipher_suites = cipher_suites;
3442 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3443
3444 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3445 WIPHY_WOWLAN_DISCONNECT |
3446 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3447 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3448 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3449 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3450 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3451 wiphy->wowlan.pattern_min_len = 1;
3452 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3453
3454 wiphy->max_sched_scan_ssids = 10;
3455
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303456 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3457 WIPHY_FLAG_HAVE_AP_SME |
3458 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3459 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3460
3461 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3462 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3463
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303464 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3465 ar->fw_capabilities))
3466 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3467
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303468 ar->wiphy->probe_resp_offload =
3469 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3470 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3471 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3472 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3473
Kalle Valo46d33a22012-01-17 20:08:40 +02003474 ret = wiphy_register(wiphy);
3475 if (ret < 0) {
3476 ath6kl_err("couldn't register wiphy device\n");
3477 return ret;
3478 }
3479
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303480 ar->wiphy_registered = true;
3481
Kalle Valo46d33a22012-01-17 20:08:40 +02003482 return 0;
3483}
3484
3485void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303486{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303487 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303488
3489 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003490}
Kalle Valo46d33a22012-01-17 20:08:40 +02003491
Kalle Valo45eaa782012-01-17 20:09:05 +02003492struct ath6kl *ath6kl_cfg80211_create(void)
3493{
3494 struct ath6kl *ar;
3495 struct wiphy *wiphy;
3496
3497 /* create a new wiphy for use with cfg80211 */
3498 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3499
3500 if (!wiphy) {
3501 ath6kl_err("couldn't allocate wiphy device\n");
3502 return NULL;
3503 }
3504
3505 ar = wiphy_priv(wiphy);
3506 ar->wiphy = wiphy;
3507
3508 return ar;
3509}
3510
3511/* Note: ar variable must not be accessed after calling this! */
3512void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3513{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303514 int i;
3515
3516 for (i = 0; i < AP_MAX_NUM_STA; i++)
3517 kfree(ar->sta_list[i].aggr_conn);
3518
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303519 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003520}
Kalle Valo45eaa782012-01-17 20:09:05 +02003521