blob: 86d388f5770867d0a37355ea01867f91a5e7f7a4 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
Vasanthakumar Thiagarajan1b2df402012-02-06 20:15:53 +05303 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
Kalle Valobdcd8172011-07-18 00:22:30 +03004 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040018#include <linux/moduleparam.h>
Raja Manic08631c2011-12-16 14:24:24 +053019#include <linux/inetdevice.h>
Kalle Valod6a434d2012-01-17 20:09:36 +020020#include <linux/export.h>
Paul Gortmaker6eb07ca2011-09-15 19:46:05 -040021
Kalle Valobdcd8172011-07-18 00:22:30 +030022#include "core.h"
23#include "cfg80211.h"
24#include "debug.h"
Kalle Valoabcb3442011-07-22 08:26:20 +030025#include "hif-ops.h"
Kalle Valo003353b0d2011-09-01 10:14:21 +030026#include "testmode.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030027
28#define RATETAB_ENT(_rate, _rateid, _flags) { \
29 .bitrate = (_rate), \
30 .flags = (_flags), \
31 .hw_value = (_rateid), \
32}
33
34#define CHAN2G(_channel, _freq, _flags) { \
35 .band = IEEE80211_BAND_2GHZ, \
36 .hw_value = (_channel), \
37 .center_freq = (_freq), \
38 .flags = (_flags), \
39 .max_antenna_gain = 0, \
40 .max_power = 30, \
41}
42
43#define CHAN5G(_channel, _flags) { \
44 .band = IEEE80211_BAND_5GHZ, \
45 .hw_value = (_channel), \
46 .center_freq = 5000 + (5 * (_channel)), \
47 .flags = (_flags), \
48 .max_antenna_gain = 0, \
49 .max_power = 30, \
50}
51
Bala Shanmugamf5993592012-03-27 12:17:32 +053052#define DEFAULT_BG_SCAN_PERIOD 60
53
Kalle Valobdcd8172011-07-18 00:22:30 +030054static struct ieee80211_rate ath6kl_rates[] = {
55 RATETAB_ENT(10, 0x1, 0),
56 RATETAB_ENT(20, 0x2, 0),
57 RATETAB_ENT(55, 0x4, 0),
58 RATETAB_ENT(110, 0x8, 0),
59 RATETAB_ENT(60, 0x10, 0),
60 RATETAB_ENT(90, 0x20, 0),
61 RATETAB_ENT(120, 0x40, 0),
62 RATETAB_ENT(180, 0x80, 0),
63 RATETAB_ENT(240, 0x100, 0),
64 RATETAB_ENT(360, 0x200, 0),
65 RATETAB_ENT(480, 0x400, 0),
66 RATETAB_ENT(540, 0x800, 0),
67};
68
69#define ath6kl_a_rates (ath6kl_rates + 4)
70#define ath6kl_a_rates_size 8
71#define ath6kl_g_rates (ath6kl_rates + 0)
72#define ath6kl_g_rates_size 12
73
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +053074#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20
75#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +053076 IEEE80211_HT_CAP_SGI_20 | \
77 IEEE80211_HT_CAP_SGI_40)
78
Kalle Valobdcd8172011-07-18 00:22:30 +030079static struct ieee80211_channel ath6kl_2ghz_channels[] = {
80 CHAN2G(1, 2412, 0),
81 CHAN2G(2, 2417, 0),
82 CHAN2G(3, 2422, 0),
83 CHAN2G(4, 2427, 0),
84 CHAN2G(5, 2432, 0),
85 CHAN2G(6, 2437, 0),
86 CHAN2G(7, 2442, 0),
87 CHAN2G(8, 2447, 0),
88 CHAN2G(9, 2452, 0),
89 CHAN2G(10, 2457, 0),
90 CHAN2G(11, 2462, 0),
91 CHAN2G(12, 2467, 0),
92 CHAN2G(13, 2472, 0),
93 CHAN2G(14, 2484, 0),
94};
95
96static struct ieee80211_channel ath6kl_5ghz_a_channels[] = {
97 CHAN5G(34, 0), CHAN5G(36, 0),
98 CHAN5G(38, 0), CHAN5G(40, 0),
99 CHAN5G(42, 0), CHAN5G(44, 0),
100 CHAN5G(46, 0), CHAN5G(48, 0),
101 CHAN5G(52, 0), CHAN5G(56, 0),
102 CHAN5G(60, 0), CHAN5G(64, 0),
103 CHAN5G(100, 0), CHAN5G(104, 0),
104 CHAN5G(108, 0), CHAN5G(112, 0),
105 CHAN5G(116, 0), CHAN5G(120, 0),
106 CHAN5G(124, 0), CHAN5G(128, 0),
107 CHAN5G(132, 0), CHAN5G(136, 0),
108 CHAN5G(140, 0), CHAN5G(149, 0),
109 CHAN5G(153, 0), CHAN5G(157, 0),
110 CHAN5G(161, 0), CHAN5G(165, 0),
111 CHAN5G(184, 0), CHAN5G(188, 0),
112 CHAN5G(192, 0), CHAN5G(196, 0),
113 CHAN5G(200, 0), CHAN5G(204, 0),
114 CHAN5G(208, 0), CHAN5G(212, 0),
115 CHAN5G(216, 0),
116};
117
118static struct ieee80211_supported_band ath6kl_band_2ghz = {
119 .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels),
120 .channels = ath6kl_2ghz_channels,
121 .n_bitrates = ath6kl_g_rates_size,
122 .bitrates = ath6kl_g_rates,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530123 .ht_cap.cap = ath6kl_g_htcap,
124 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300125};
126
127static struct ieee80211_supported_band ath6kl_band_5ghz = {
128 .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels),
129 .channels = ath6kl_5ghz_a_channels,
130 .n_bitrates = ath6kl_a_rates_size,
131 .bitrates = ath6kl_a_rates,
Vasanthakumar Thiagarajanbed56e32012-04-09 19:03:57 +0530132 .ht_cap.cap = ath6kl_a_htcap,
Vasanthakumar Thiagarajanfaaf1922012-02-27 11:44:19 +0530133 .ht_cap.ht_supported = true,
Kalle Valobdcd8172011-07-18 00:22:30 +0300134};
135
Jouni Malinen837cb972011-10-11 17:31:57 +0300136#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
137
Kalle Valo10509f92011-12-13 14:52:07 +0200138/* returns true if scheduled scan was stopped */
139static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
140{
141 struct ath6kl *ar = vif->ar;
142
143 if (ar->state != ATH6KL_STATE_SCHED_SCAN)
144 return false;
145
146 del_timer_sync(&vif->sched_scan_timer);
147
148 ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
149 ATH6KL_HOST_MODE_AWAKE);
150
151 ar->state = ATH6KL_STATE_ON;
152
153 return true;
154}
155
156static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
157{
158 struct ath6kl *ar = vif->ar;
159 bool stopped;
160
161 stopped = __ath6kl_cfg80211_sscan_stop(vif);
162
163 if (!stopped)
164 return;
165
166 cfg80211_sched_scan_stopped(ar->wiphy);
167}
168
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530169static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300170 enum nl80211_wpa_versions wpa_version)
171{
172 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version);
173
174 if (!wpa_version) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530175 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300176 } else if (wpa_version & NL80211_WPA_VERSION_2) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530177 vif->auth_mode = WPA2_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300178 } else if (wpa_version & NL80211_WPA_VERSION_1) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530179 vif->auth_mode = WPA_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300180 } else {
181 ath6kl_err("%s: %u not supported\n", __func__, wpa_version);
182 return -ENOTSUPP;
183 }
184
185 return 0;
186}
187
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530188static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
Kalle Valobdcd8172011-07-18 00:22:30 +0300189 enum nl80211_auth_type auth_type)
190{
Kalle Valobdcd8172011-07-18 00:22:30 +0300191 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type);
192
193 switch (auth_type) {
194 case NL80211_AUTHTYPE_OPEN_SYSTEM:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530195 vif->dot11_auth_mode = OPEN_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300196 break;
197 case NL80211_AUTHTYPE_SHARED_KEY:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530198 vif->dot11_auth_mode = SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300199 break;
200 case NL80211_AUTHTYPE_NETWORK_EAP:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530201 vif->dot11_auth_mode = LEAP_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300202 break;
203
204 case NL80211_AUTHTYPE_AUTOMATIC:
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530205 vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300206 break;
207
208 default:
Masanari Iida3c325fb2012-01-31 23:32:55 +0900209 ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300210 return -ENOTSUPP;
211 }
212
213 return 0;
214}
215
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530216static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast)
Kalle Valobdcd8172011-07-18 00:22:30 +0300217{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530218 u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto;
219 u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len :
220 &vif->grp_crypto_len;
Kalle Valobdcd8172011-07-18 00:22:30 +0300221
222 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
223 __func__, cipher, ucast);
224
225 switch (cipher) {
226 case 0:
227 /* our own hack to use value 0 as no crypto used */
228 *ar_cipher = NONE_CRYPT;
229 *ar_cipher_len = 0;
230 break;
231 case WLAN_CIPHER_SUITE_WEP40:
232 *ar_cipher = WEP_CRYPT;
233 *ar_cipher_len = 5;
234 break;
235 case WLAN_CIPHER_SUITE_WEP104:
236 *ar_cipher = WEP_CRYPT;
237 *ar_cipher_len = 13;
238 break;
239 case WLAN_CIPHER_SUITE_TKIP:
240 *ar_cipher = TKIP_CRYPT;
241 *ar_cipher_len = 0;
242 break;
243 case WLAN_CIPHER_SUITE_CCMP:
244 *ar_cipher = AES_CRYPT;
245 *ar_cipher_len = 0;
246 break;
Dai Shuibing5e070212011-11-03 11:39:37 +0200247 case WLAN_CIPHER_SUITE_SMS4:
248 *ar_cipher = WAPI_CRYPT;
249 *ar_cipher_len = 0;
250 break;
Kalle Valobdcd8172011-07-18 00:22:30 +0300251 default:
252 ath6kl_err("cipher 0x%x not supported\n", cipher);
253 return -ENOTSUPP;
254 }
255
256 return 0;
257}
258
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530259static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
Kalle Valobdcd8172011-07-18 00:22:30 +0300260{
261 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt);
262
263 if (key_mgmt == WLAN_AKM_SUITE_PSK) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530264 if (vif->auth_mode == WPA_AUTH)
265 vif->auth_mode = WPA_PSK_AUTH;
266 else if (vif->auth_mode == WPA2_AUTH)
267 vif->auth_mode = WPA2_PSK_AUTH;
Jouni Malinen837cb972011-10-11 17:31:57 +0300268 } else if (key_mgmt == 0x00409600) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530269 if (vif->auth_mode == WPA_AUTH)
270 vif->auth_mode = WPA_AUTH_CCKM;
271 else if (vif->auth_mode == WPA2_AUTH)
272 vif->auth_mode = WPA2_AUTH_CCKM;
Kalle Valobdcd8172011-07-18 00:22:30 +0300273 } else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530274 vif->auth_mode = NONE_AUTH;
Kalle Valobdcd8172011-07-18 00:22:30 +0300275 }
276}
277
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530278static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +0300279{
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530280 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530281
Kalle Valobdcd8172011-07-18 00:22:30 +0300282 if (!test_bit(WMI_READY, &ar->flag)) {
283 ath6kl_err("wmi is not ready\n");
284 return false;
285 }
286
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530287 if (!test_bit(WLAN_ENABLED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300288 ath6kl_err("wlan disabled\n");
289 return false;
290 }
291
292 return true;
293}
294
Kevin Fang6981ffd2011-10-07 08:51:19 +0800295static bool ath6kl_is_wpa_ie(const u8 *pos)
296{
297 return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
298 pos[2] == 0x00 && pos[3] == 0x50 &&
299 pos[4] == 0xf2 && pos[5] == 0x01;
300}
301
302static bool ath6kl_is_rsn_ie(const u8 *pos)
303{
304 return pos[0] == WLAN_EID_RSN;
305}
306
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700307static bool ath6kl_is_wps_ie(const u8 *pos)
308{
309 return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
310 pos[1] >= 4 &&
311 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
312 pos[5] == 0x04);
313}
314
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530315static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies,
316 size_t ies_len)
Kevin Fang6981ffd2011-10-07 08:51:19 +0800317{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530318 struct ath6kl *ar = vif->ar;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800319 const u8 *pos;
320 u8 *buf = NULL;
321 size_t len = 0;
322 int ret;
323
324 /*
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700325 * Clear previously set flag
326 */
327
328 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
329
330 /*
Kevin Fang6981ffd2011-10-07 08:51:19 +0800331 * Filter out RSN/WPA IE(s)
332 */
333
334 if (ies && ies_len) {
335 buf = kmalloc(ies_len, GFP_KERNEL);
336 if (buf == NULL)
337 return -ENOMEM;
338 pos = ies;
339
340 while (pos + 1 < ies + ies_len) {
341 if (pos + 2 + pos[1] > ies + ies_len)
342 break;
343 if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
344 memcpy(buf + len, pos, 2 + pos[1]);
345 len += 2 + pos[1];
346 }
Aarthi Thiruvengadam63541212011-10-25 11:25:52 -0700347
348 if (ath6kl_is_wps_ie(pos))
349 ar->connect_ctrl_flags |= CONNECT_WPS_FLAG;
350
Kevin Fang6981ffd2011-10-07 08:51:19 +0800351 pos += 2 + pos[1];
352 }
353 }
354
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530355 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
356 WMI_FRAME_ASSOC_REQ, buf, len);
Kevin Fang6981ffd2011-10-07 08:51:19 +0800357 kfree(buf);
358 return ret;
359}
360
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530361static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type)
362{
363 switch (type) {
364 case NL80211_IFTYPE_STATION:
365 *nw_type = INFRA_NETWORK;
366 break;
367 case NL80211_IFTYPE_ADHOC:
368 *nw_type = ADHOC_NETWORK;
369 break;
370 case NL80211_IFTYPE_AP:
371 *nw_type = AP_NETWORK;
372 break;
373 case NL80211_IFTYPE_P2P_CLIENT:
374 *nw_type = INFRA_NETWORK;
375 break;
376 case NL80211_IFTYPE_P2P_GO:
377 *nw_type = AP_NETWORK;
378 break;
379 default:
380 ath6kl_err("invalid interface type %u\n", type);
381 return -ENOTSUPP;
382 }
383
384 return 0;
385}
386
387static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
388 u8 *if_idx, u8 *nw_type)
389{
390 int i;
391
392 if (ath6kl_nliftype_to_drv_iftype(type, nw_type))
393 return false;
394
395 if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
Kalle Valo96f1fad2012-03-07 20:03:57 +0200396 ar->num_vif))
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530397 return false;
398
399 if (type == NL80211_IFTYPE_STATION ||
400 type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200401 for (i = 0; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530402 if ((ar->avail_idx_map >> i) & BIT(0)) {
403 *if_idx = i;
404 return true;
405 }
406 }
407 }
408
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530409 if (type == NL80211_IFTYPE_P2P_CLIENT ||
410 type == NL80211_IFTYPE_P2P_GO) {
Kalle Valo71f96ee2011-11-14 19:31:30 +0200411 for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
Vasanthakumar Thiagarajan3226f68a2011-10-25 19:34:24 +0530412 if ((ar->avail_idx_map >> i) & BIT(0)) {
413 *if_idx = i;
414 return true;
415 }
416 }
417 }
418
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +0530419 return false;
420}
421
Kalle Valo8c9bb052012-03-07 20:04:00 +0200422static bool ath6kl_is_tx_pending(struct ath6kl *ar)
423{
424 return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0;
425}
426
427
Kalle Valobdcd8172011-07-18 00:22:30 +0300428static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
429 struct cfg80211_connect_params *sme)
430{
431 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530432 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300433 int status;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800434 u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
Raja Manice0dc0c2012-02-20 19:08:08 +0530435 u16 interval;
Kalle Valobdcd8172011-07-18 00:22:30 +0300436
Kalle Valo10509f92011-12-13 14:52:07 +0200437 ath6kl_cfg80211_sscan_disable(vif);
438
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530439 vif->sme_state = SME_CONNECTING;
Kalle Valobdcd8172011-07-18 00:22:30 +0300440
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530441 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300442 return -EIO;
443
444 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
445 ath6kl_err("destroy in progress\n");
446 return -EBUSY;
447 }
448
449 if (test_bit(SKIP_SCAN, &ar->flag) &&
450 ((sme->channel && sme->channel->center_freq == 0) ||
451 (sme->bssid && is_zero_ether_addr(sme->bssid)))) {
452 ath6kl_err("SkipScan: channel or bssid invalid\n");
453 return -EINVAL;
454 }
455
456 if (down_interruptible(&ar->sem)) {
457 ath6kl_err("busy, couldn't get access\n");
458 return -ERESTARTSYS;
459 }
460
461 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
462 ath6kl_err("busy, destroy in progress\n");
463 up(&ar->sem);
464 return -EBUSY;
465 }
466
467 if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) {
468 /*
469 * sleep until the command queue drains
470 */
471 wait_event_interruptible_timeout(ar->event_wq,
Kalle Valo8c9bb052012-03-07 20:04:00 +0200472 ath6kl_is_tx_pending(ar),
473 WMI_TIMEOUT);
Kalle Valobdcd8172011-07-18 00:22:30 +0300474 if (signal_pending(current)) {
475 ath6kl_err("cmd queue drain timeout\n");
476 up(&ar->sem);
477 return -EINTR;
478 }
479 }
480
Jouni Malinen6e786cb2011-12-15 14:16:00 +0200481 status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
482 if (status) {
483 up(&ar->sem);
484 return status;
485 }
486
487 if (sme->ie == NULL || sme->ie_len == 0)
Raja Mani542c5192011-11-15 14:14:56 +0530488 ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
Kevin Fang6981ffd2011-10-07 08:51:19 +0800489
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530490 if (test_bit(CONNECTED, &vif->flags) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530491 vif->ssid_len == sme->ssid_len &&
492 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530493 vif->reconnect_flag = true;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530494 status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx,
495 vif->req_bssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530496 vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300497
498 up(&ar->sem);
499 if (status) {
500 ath6kl_err("wmi_reconnect_cmd failed\n");
501 return -EIO;
502 }
503 return 0;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530504 } else if (vif->ssid_len == sme->ssid_len &&
505 !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530506 ath6kl_disconnect(vif);
Kalle Valobdcd8172011-07-18 00:22:30 +0300507 }
508
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530509 memset(vif->ssid, 0, sizeof(vif->ssid));
510 vif->ssid_len = sme->ssid_len;
511 memcpy(vif->ssid, sme->ssid, sme->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +0300512
513 if (sme->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530514 vif->ch_hint = sme->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +0300515
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530516 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300517 if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530518 memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300519
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530520 ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
Kalle Valobdcd8172011-07-18 00:22:30 +0300521
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530522 status = ath6kl_set_auth_type(vif, sme->auth_type);
Kalle Valobdcd8172011-07-18 00:22:30 +0300523 if (status) {
524 up(&ar->sem);
525 return status;
526 }
527
528 if (sme->crypto.n_ciphers_pairwise)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530529 ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300530 else
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530531 ath6kl_set_cipher(vif, 0, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300532
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530533 ath6kl_set_cipher(vif, sme->crypto.cipher_group, false);
Kalle Valobdcd8172011-07-18 00:22:30 +0300534
535 if (sme->crypto.n_akm_suites)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530536 ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]);
Kalle Valobdcd8172011-07-18 00:22:30 +0300537
538 if ((sme->key_len) &&
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530539 (vif->auth_mode == NONE_AUTH) &&
540 (vif->prwise_crypto == WEP_CRYPT)) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300541 struct ath6kl_key *key = NULL;
542
Vivek Natarajan792ecb32011-12-29 16:18:39 +0530543 if (sme->key_idx > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300544 ath6kl_err("key index %d out of bounds\n",
545 sme->key_idx);
546 up(&ar->sem);
547 return -ENOENT;
548 }
549
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +0530550 key = &vif->keys[sme->key_idx];
Kalle Valobdcd8172011-07-18 00:22:30 +0300551 key->key_len = sme->key_len;
552 memcpy(key->key, sme->key, key->key_len);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530553 key->cipher = vif->prwise_crypto;
554 vif->def_txkey_index = sme->key_idx;
Kalle Valobdcd8172011-07-18 00:22:30 +0300555
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530556 ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530557 vif->prwise_crypto,
Kalle Valobdcd8172011-07-18 00:22:30 +0300558 GROUP_USAGE | TX_USAGE,
559 key->key_len,
Jouni Malinenf4bb9a62011-11-02 23:45:55 +0200560 NULL, 0,
Kalle Valobdcd8172011-07-18 00:22:30 +0300561 key->key, KEY_OP_INIT_VAL, NULL,
562 NO_SYNC_WMIFLAG);
563 }
564
565 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530566 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530567 if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200568 ALL_BSS_FILTER, 0) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300569 ath6kl_err("couldn't set bss filtering\n");
570 up(&ar->sem);
571 return -EIO;
572 }
573 }
574
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530575 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +0300576
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800577 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
578 nw_subtype = SUBTYPE_P2PCLIENT;
579
Kalle Valobdcd8172011-07-18 00:22:30 +0300580 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
581 "%s: connect called with authmode %d dot11 auth %d"
582 " PW crypto %d PW crypto len %d GRP crypto %d"
583 " GRP crypto len %d channel hint %u\n",
584 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530585 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
586 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530587 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +0300588
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530589 vif->reconnect_flag = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +0530590
591 if (vif->nw_type == INFRA_NETWORK) {
Kalle Valob5283872012-03-12 13:23:23 +0200592 interval = max_t(u16, vif->listen_intvl_t,
593 ATH6KL_MAX_WOW_LISTEN_INTL);
Raja Manice0dc0c2012-02-20 19:08:08 +0530594 status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
595 interval,
596 0);
597 if (status) {
598 ath6kl_err("couldn't set listen intervel\n");
599 up(&ar->sem);
600 return status;
601 }
602 }
603
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530604 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530605 vif->dot11_auth_mode, vif->auth_mode,
606 vif->prwise_crypto,
607 vif->prwise_crypto_len,
608 vif->grp_crypto, vif->grp_crypto_len,
609 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +0530610 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800611 ar->connect_ctrl_flags, nw_subtype);
Kalle Valobdcd8172011-07-18 00:22:30 +0300612
Bala Shanmugamf5993592012-03-27 12:17:32 +0530613 /* disable background scan if period is 0 */
614 if (sme->bg_scan_period == 0)
615 sme->bg_scan_period = 0xffff;
616
617 /* configure default value if not specified */
618 if (sme->bg_scan_period == -1)
619 sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
620
621 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0,
622 sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0);
623
Kalle Valobdcd8172011-07-18 00:22:30 +0300624 up(&ar->sem);
625
626 if (status == -EINVAL) {
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530627 memset(vif->ssid, 0, sizeof(vif->ssid));
628 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300629 ath6kl_err("invalid request\n");
630 return -ENOENT;
631 } else if (status) {
632 ath6kl_err("ath6kl_wmi_connect_cmd failed\n");
633 return -EIO;
634 }
635
636 if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
Kalle Valoddc3d772012-03-07 20:03:58 +0200637 ((vif->auth_mode == WPA_PSK_AUTH) ||
638 (vif->auth_mode == WPA2_PSK_AUTH))) {
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +0530639 mod_timer(&vif->disconnect_timer,
Kalle Valobdcd8172011-07-18 00:22:30 +0300640 jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
641 }
642
643 ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530644 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300645
646 return 0;
647}
648
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530649static struct cfg80211_bss *
650ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
651 enum network_type nw_type,
652 const u8 *bssid,
653 struct ieee80211_channel *chan,
654 const u8 *beacon_ie,
655 size_t beacon_ie_len)
Jouni Malinen01cac472011-09-19 19:14:59 +0300656{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530657 struct ath6kl *ar = vif->ar;
Jouni Malinen01cac472011-09-19 19:14:59 +0300658 struct cfg80211_bss *bss;
Raja Mani4eab6f42011-11-09 17:02:23 +0530659 u16 cap_mask, cap_val;
Jouni Malinen01cac472011-09-19 19:14:59 +0300660 u8 *ie;
661
Raja Mani4eab6f42011-11-09 17:02:23 +0530662 if (nw_type & ADHOC_NETWORK) {
663 cap_mask = WLAN_CAPABILITY_IBSS;
664 cap_val = WLAN_CAPABILITY_IBSS;
665 } else {
666 cap_mask = WLAN_CAPABILITY_ESS;
667 cap_val = WLAN_CAPABILITY_ESS;
668 }
669
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530670 bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
Raja Mani4eab6f42011-11-09 17:02:23 +0530671 vif->ssid, vif->ssid_len,
672 cap_mask, cap_val);
Jouni Malinen01cac472011-09-19 19:14:59 +0300673 if (bss == NULL) {
674 /*
675 * Since cfg80211 may not yet know about the BSS,
676 * generate a partial entry until the first BSS info
677 * event becomes available.
678 *
679 * Prepend SSID element since it is not included in the Beacon
680 * IEs from the target.
681 */
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530682 ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
Jouni Malinen01cac472011-09-19 19:14:59 +0300683 if (ie == NULL)
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530684 return NULL;
Jouni Malinen01cac472011-09-19 19:14:59 +0300685 ie[0] = WLAN_EID_SSID;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530686 ie[1] = vif->ssid_len;
687 memcpy(ie + 2, vif->ssid, vif->ssid_len);
688 memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530689 bss = cfg80211_inform_bss(ar->wiphy, chan,
Raja Mani4eab6f42011-11-09 17:02:23 +0530690 bssid, 0, cap_val, 100,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530691 ie, 2 + vif->ssid_len + beacon_ie_len,
Jouni Malinen01cac472011-09-19 19:14:59 +0300692 0, GFP_KERNEL);
693 if (bss)
Raja Mani4eab6f42011-11-09 17:02:23 +0530694 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
695 "cfg80211\n", bssid);
Jouni Malinen01cac472011-09-19 19:14:59 +0300696 kfree(ie);
697 } else
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530698 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
Jouni Malinen01cac472011-09-19 19:14:59 +0300699
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530700 return bss;
Jouni Malinen01cac472011-09-19 19:14:59 +0300701}
702
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530703void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
Kalle Valobdcd8172011-07-18 00:22:30 +0300704 u8 *bssid, u16 listen_intvl,
705 u16 beacon_intvl,
706 enum network_type nw_type,
707 u8 beacon_ie_len, u8 assoc_req_len,
708 u8 assoc_resp_len, u8 *assoc_info)
709{
Jouni Malinen01cac472011-09-19 19:14:59 +0300710 struct ieee80211_channel *chan;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530711 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530712 struct cfg80211_bss *bss;
Kalle Valobdcd8172011-07-18 00:22:30 +0300713
714 /* capinfo + listen interval */
715 u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
716
717 /* capinfo + status code + associd */
718 u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16);
719
720 u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset;
721 u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
722 assoc_resp_ie_offset;
723
724 assoc_req_len -= assoc_req_ie_offset;
725 assoc_resp_len -= assoc_resp_ie_offset;
726
Jouni Malinen32c10872011-09-19 19:15:07 +0300727 /*
728 * Store Beacon interval here; DTIM period will be available only once
729 * a Beacon frame from the AP is seen.
730 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530731 vif->assoc_bss_beacon_int = beacon_intvl;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530732 clear_bit(DTIM_PERIOD_AVAIL, &vif->flags);
Jouni Malinen32c10872011-09-19 19:15:07 +0300733
Kalle Valobdcd8172011-07-18 00:22:30 +0300734 if (nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530735 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300736 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
737 "%s: ath6k not in ibss mode\n", __func__);
738 return;
739 }
740 }
741
742 if (nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530743 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
744 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300745 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
746 "%s: ath6k not in station mode\n", __func__);
747 return;
748 }
749 }
750
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +0530751 chan = ieee80211_get_channel(ar->wiphy, (int) channel);
Kalle Valobdcd8172011-07-18 00:22:30 +0300752
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530753 bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
754 assoc_info, beacon_ie_len);
755 if (!bss) {
Raja Mani4eab6f42011-11-09 17:02:23 +0530756 ath6kl_err("could not add cfg80211 bss entry\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300757 return;
758 }
759
Raja Mani4eab6f42011-11-09 17:02:23 +0530760 if (nw_type & ADHOC_NETWORK) {
761 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
762 nw_type & ADHOC_CREATOR ? "creator" : "joiner");
763 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530764 cfg80211_put_bss(bss);
Jouni Malinen01cac472011-09-19 19:14:59 +0300765 return;
766 }
767
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530768 if (vif->sme_state == SME_CONNECTING) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300769 /* inform connect result to cfg80211 */
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530770 vif->sme_state = SME_CONNECTED;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530771 cfg80211_connect_result(vif->ndev, bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +0300772 assoc_req_ie, assoc_req_len,
773 assoc_resp_ie, assoc_resp_len,
774 WLAN_STATUS_SUCCESS, GFP_KERNEL);
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530775 cfg80211_put_bss(bss);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530776 } else if (vif->sme_state == SME_CONNECTED) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300777 /* inform roam event to cfg80211 */
Vasanthakumar Thiagarajan5e13fd32011-12-13 17:19:57 +0530778 cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
779 assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300780 }
781}
782
783static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
784 struct net_device *dev, u16 reason_code)
785{
Kalle Valod6d5c062011-11-25 13:17:37 +0200786 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530787 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300788
789 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
790 reason_code);
791
Kalle Valo10509f92011-12-13 14:52:07 +0200792 ath6kl_cfg80211_sscan_disable(vif);
793
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530794 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300795 return -EIO;
796
797 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
798 ath6kl_err("busy, destroy in progress\n");
799 return -EBUSY;
800 }
801
802 if (down_interruptible(&ar->sem)) {
803 ath6kl_err("busy, couldn't get access\n");
804 return -ERESTARTSYS;
805 }
806
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +0530807 vif->reconnect_flag = 0;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530808 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +0530809 memset(vif->ssid, 0, sizeof(vif->ssid));
810 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300811
812 if (!test_bit(SKIP_SCAN, &ar->flag))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +0530813 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +0300814
815 up(&ar->sem);
816
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530817 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan170826d2011-09-10 15:26:35 +0530818
Kalle Valobdcd8172011-07-18 00:22:30 +0300819 return 0;
820}
821
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530822void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
Kalle Valobdcd8172011-07-18 00:22:30 +0300823 u8 *bssid, u8 assoc_resp_len,
824 u8 *assoc_info, u16 proto_reason)
825{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530826 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530827
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530828 if (vif->scan_req) {
829 cfg80211_scan_done(vif->scan_req, true);
830 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300831 }
832
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530833 if (vif->nw_type & ADHOC_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530834 if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300835 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
836 "%s: ath6k not in ibss mode\n", __func__);
837 return;
838 }
839 memset(bssid, 0, ETH_ALEN);
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530840 cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300841 return;
842 }
843
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +0530844 if (vif->nw_type & INFRA_NETWORK) {
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +0530845 if (vif->wdev.iftype != NL80211_IFTYPE_STATION &&
846 vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300847 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
848 "%s: ath6k not in station mode\n", __func__);
849 return;
850 }
851 }
852
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530853 /*
854 * Send a disconnect command to target when a disconnect event is
855 * received with reason code other than 3 (DISCONNECT_CMD - disconnect
856 * request from host) to make the firmware stop trying to connect even
857 * after giving disconnect event. There will be one more disconnect
858 * event for this disconnect command with reason code DISCONNECT_CMD
859 * which will be notified to cfg80211.
860 */
Kalle Valobdcd8172011-07-18 00:22:30 +0300861
Vasanthakumar Thiagarajan1de547d2011-09-23 10:57:50 +0530862 if (reason != DISCONNECT_CMD) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530863 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +0300864 return;
865 }
866
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530867 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +0300868
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530869 if (vif->sme_state == SME_CONNECTING) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530870 cfg80211_connect_result(vif->ndev,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200871 bssid, NULL, 0,
872 NULL, 0,
873 WLAN_STATUS_UNSPECIFIED_FAILURE,
874 GFP_KERNEL);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530875 } else if (vif->sme_state == SME_CONNECTED) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530876 cfg80211_disconnected(vif->ndev, reason,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200877 NULL, 0, GFP_KERNEL);
Kalle Valobdcd8172011-07-18 00:22:30 +0300878 }
879
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +0530880 vif->sme_state = SME_DISCONNECTED;
Kalle Valobdcd8172011-07-18 00:22:30 +0300881}
882
Kalle Valobdcd8172011-07-18 00:22:30 +0300883static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
884 struct cfg80211_scan_request *request)
885{
Kalle Valod6d5c062011-11-25 13:17:37 +0200886 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530887 struct ath6kl_vif *vif = netdev_priv(ndev);
Edward Lu1276c9e2011-08-30 21:58:00 +0300888 s8 n_channels = 0;
889 u16 *channels = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300890 int ret = 0;
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530891 u32 force_fg_scan = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300892
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +0530893 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +0300894 return -EIO;
895
Kalle Valo10509f92011-12-13 14:52:07 +0200896 ath6kl_cfg80211_sscan_disable(vif);
897
Kalle Valobdcd8172011-07-18 00:22:30 +0300898 if (!ar->usr_bss_filter) {
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530899 clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300900 ret = ath6kl_wmi_bssfilter_cmd(
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530901 ar->wmi, vif->fw_vif_idx,
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530902 (test_bit(CONNECTED, &vif->flags) ?
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300903 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
904 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300905 ath6kl_err("couldn't set bss filtering\n");
Jouni Malinen1b1e6ee2011-08-30 21:58:10 +0300906 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300907 }
908 }
909
910 if (request->n_ssids && request->ssids[0].ssid_len) {
911 u8 i;
912
913 if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
914 request->n_ssids = MAX_PROBED_SSID_INDEX - 1;
915
916 for (i = 0; i < request->n_ssids; i++)
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +0530917 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
918 i + 1, SPECIFIC_SSID_FLAG,
Kalle Valobdcd8172011-07-18 00:22:30 +0300919 request->ssids[i].ssid_len,
920 request->ssids[i].ssid);
921 }
922
Aarthi Thiruvengadam080eec42012-02-28 09:17:04 -0800923 /* this also clears IE in fw if it's not set */
924 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
925 WMI_FRAME_PROBE_REQ,
926 request->ie, request->ie_len);
927 if (ret) {
928 ath6kl_err("failed to set Probe Request appie for "
929 "scan");
930 return ret;
Jouni Malinenb84da8c2011-08-30 21:57:59 +0300931 }
932
Jouni Malinen11869be2011-09-02 20:07:06 +0300933 /*
934 * Scan only the requested channels if the request specifies a set of
935 * channels. If the list is longer than the target supports, do not
936 * configure the list and instead, scan all available channels.
937 */
938 if (request->n_channels > 0 &&
939 request->n_channels <= WMI_MAX_CHANNELS) {
Edward Lu1276c9e2011-08-30 21:58:00 +0300940 u8 i;
941
Jouni Malinen11869be2011-09-02 20:07:06 +0300942 n_channels = request->n_channels;
Edward Lu1276c9e2011-08-30 21:58:00 +0300943
944 channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
945 if (channels == NULL) {
946 ath6kl_warn("failed to set scan channels, "
947 "scan all channels");
948 n_channels = 0;
949 }
950
951 for (i = 0; i < n_channels; i++)
952 channels[i] = request->channels[i]->center_freq;
953 }
954
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +0530955 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajanf1f92172011-10-01 16:12:36 +0530956 force_fg_scan = 1;
957
Raja Mani5b35dff2012-03-28 18:50:35 +0530958 vif->scan_req = request;
959
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800960 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
Kalle Valo96f1fad2012-03-07 20:03:57 +0200961 ar->fw_capabilities)) {
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800962 /*
963 * If capable of doing P2P mgmt operations using
964 * station interface, send additional information like
965 * supported rates to advertise and xmit rates for
966 * probe requests
967 */
968 ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
969 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530970 false, 0,
971 ATH6KL_FG_SCAN_INTERVAL,
972 n_channels, channels,
973 request->no_cck,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800974 request->rates);
975 } else {
976 ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
977 WMI_LONG_SCAN, force_fg_scan,
Vasanthakumar Thiagarajan13423c32012-02-21 15:30:42 +0530978 false, 0,
979 ATH6KL_FG_SCAN_INTERVAL,
980 n_channels, channels);
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -0800981 }
Raja Mani5b35dff2012-03-28 18:50:35 +0530982 if (ret) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300983 ath6kl_err("wmi_startscan_cmd failed\n");
Raja Mani5b35dff2012-03-28 18:50:35 +0530984 vif->scan_req = NULL;
985 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300986
Edward Lu1276c9e2011-08-30 21:58:00 +0300987 kfree(channels);
988
Kalle Valobdcd8172011-07-18 00:22:30 +0300989 return ret;
990}
991
Kalle Valo1c17d312011-11-01 08:43:56 +0200992void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted)
Kalle Valobdcd8172011-07-18 00:22:30 +0300993{
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +0530994 struct ath6kl *ar = vif->ar;
Kalle Valo6fd1eac2011-07-21 10:22:50 +0300995 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +0300996
Kalle Valo1c17d312011-11-01 08:43:56 +0200997 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__,
998 aborted ? " aborted" : "");
Kalle Valobdcd8172011-07-18 00:22:30 +0300999
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301000 if (!vif->scan_req)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001001 return;
Kalle Valobdcd8172011-07-18 00:22:30 +03001002
Kalle Valo1c17d312011-11-01 08:43:56 +02001003 if (aborted)
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001004 goto out;
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001005
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301006 if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) {
1007 for (i = 0; i < vif->scan_req->n_ssids; i++) {
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301008 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
1009 i + 1, DISABLE_SSID_FLAG,
Kalle Valo6fd1eac2011-07-21 10:22:50 +03001010 0, NULL);
1011 }
1012 }
1013
1014out:
Kalle Valocb938212011-10-27 18:47:46 +03001015 cfg80211_scan_done(vif->scan_req, aborted);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05301016 vif->scan_req = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001017}
1018
1019static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1020 u8 key_index, bool pairwise,
1021 const u8 *mac_addr,
1022 struct key_params *params)
1023{
Kalle Valod6d5c062011-11-25 13:17:37 +02001024 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301025 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001026 struct ath6kl_key *key = NULL;
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301027 int seq_len;
Kalle Valobdcd8172011-07-18 00:22:30 +03001028 u8 key_usage;
1029 u8 key_type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001030
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301031 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001032 return -EIO;
1033
Jouni Malinen837cb972011-10-11 17:31:57 +03001034 if (params->cipher == CCKM_KRK_CIPHER_SUITE) {
1035 if (params->key_len != WMI_KRK_LEN)
1036 return -EINVAL;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301037 return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx,
1038 params->key);
Jouni Malinen837cb972011-10-11 17:31:57 +03001039 }
1040
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301041 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001042 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1043 "%s: key index %d out of bounds\n", __func__,
1044 key_index);
1045 return -ENOENT;
1046 }
1047
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301048 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001049 memset(key, 0, sizeof(struct ath6kl_key));
1050
1051 if (pairwise)
1052 key_usage = PAIRWISE_USAGE;
1053 else
1054 key_usage = GROUP_USAGE;
1055
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301056 seq_len = params->seq_len;
1057 if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
1058 seq_len > ATH6KL_KEY_SEQ_LEN) {
1059 /* Only first half of the WPI PN is configured */
1060 seq_len = ATH6KL_KEY_SEQ_LEN;
Kalle Valobdcd8172011-07-18 00:22:30 +03001061 }
Sujith Manoharan4a8ce2f2012-01-10 09:53:38 +05301062 if (params->key_len > WLAN_MAX_KEY_LEN ||
1063 seq_len > sizeof(key->seq))
1064 return -EINVAL;
1065
1066 key->key_len = params->key_len;
1067 memcpy(key->key, params->key, key->key_len);
1068 key->seq_len = seq_len;
1069 memcpy(key->seq, params->seq, key->seq_len);
1070 key->cipher = params->cipher;
Kalle Valobdcd8172011-07-18 00:22:30 +03001071
1072 switch (key->cipher) {
1073 case WLAN_CIPHER_SUITE_WEP40:
1074 case WLAN_CIPHER_SUITE_WEP104:
1075 key_type = WEP_CRYPT;
1076 break;
1077
1078 case WLAN_CIPHER_SUITE_TKIP:
1079 key_type = TKIP_CRYPT;
1080 break;
1081
1082 case WLAN_CIPHER_SUITE_CCMP:
1083 key_type = AES_CRYPT;
1084 break;
Dai Shuibing5e070212011-11-03 11:39:37 +02001085 case WLAN_CIPHER_SUITE_SMS4:
1086 key_type = WAPI_CRYPT;
1087 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001088
1089 default:
1090 return -ENOTSUPP;
1091 }
1092
Kalle Valoddc3d772012-03-07 20:03:58 +02001093 if (((vif->auth_mode == WPA_PSK_AUTH) ||
1094 (vif->auth_mode == WPA2_PSK_AUTH)) &&
1095 (key_usage & GROUP_USAGE))
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05301096 del_timer(&vif->disconnect_timer);
Kalle Valobdcd8172011-07-18 00:22:30 +03001097
1098 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1099 "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n",
1100 __func__, key_index, key->key_len, key_type,
1101 key_usage, key->seq_len);
1102
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301103 if (vif->nw_type == AP_NETWORK && !pairwise &&
Jouni Malinen47032902011-12-08 16:50:30 +02001104 (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
Vasanthakumar Thiagarajancc4d6232012-02-14 20:33:00 +05301105 key_type == WAPI_CRYPT)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001106 ar->ap_mode_bkey.valid = true;
1107 ar->ap_mode_bkey.key_index = key_index;
1108 ar->ap_mode_bkey.key_type = key_type;
1109 ar->ap_mode_bkey.key_len = key->key_len;
1110 memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301111 if (!test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001112 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
1113 "key configuration until AP mode has been "
1114 "started\n");
1115 /*
1116 * The key will be set in ath6kl_connect_ap_mode() once
1117 * the connected event is received from the target.
1118 */
1119 return 0;
1120 }
1121 }
1122
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301123 if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301124 !test_bit(CONNECTED, &vif->flags)) {
Jouni Malinen151411e2011-09-15 15:10:16 +03001125 /*
1126 * Store the key locally so that it can be re-configured after
1127 * the AP mode has properly started
1128 * (ath6kl_install_statioc_wep_keys).
1129 */
1130 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
1131 "until AP mode has been started\n");
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301132 vif->wep_key_list[key_index].key_len = key->key_len;
1133 memcpy(vif->wep_key_list[key_index].key, key->key,
1134 key->key_len);
Jouni Malinen151411e2011-09-15 15:10:16 +03001135 return 0;
1136 }
1137
Vasanthakumar Thiagarajan7cefa442011-11-11 20:33:00 +05301138 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001139 key_type, key_usage, key->key_len,
1140 key->seq, key->seq_len, key->key,
1141 KEY_OP_INIT_VAL,
1142 (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001143}
1144
1145static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1146 u8 key_index, bool pairwise,
1147 const u8 *mac_addr)
1148{
Kalle Valod6d5c062011-11-25 13:17:37 +02001149 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301150 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001151
1152 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1153
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301154 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001155 return -EIO;
1156
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301157 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001158 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1159 "%s: key index %d out of bounds\n", __func__,
1160 key_index);
1161 return -ENOENT;
1162 }
1163
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301164 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001165 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1166 "%s: index %d is empty\n", __func__, key_index);
1167 return 0;
1168 }
1169
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301170 vif->keys[key_index].key_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001171
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301172 return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index);
Kalle Valobdcd8172011-07-18 00:22:30 +03001173}
1174
1175static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1176 u8 key_index, bool pairwise,
1177 const u8 *mac_addr, void *cookie,
1178 void (*callback) (void *cookie,
1179 struct key_params *))
1180{
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301181 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001182 struct ath6kl_key *key = NULL;
1183 struct key_params params;
1184
1185 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1186
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301187 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001188 return -EIO;
1189
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301190 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001191 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1192 "%s: key index %d out of bounds\n", __func__,
1193 key_index);
1194 return -ENOENT;
1195 }
1196
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301197 key = &vif->keys[key_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001198 memset(&params, 0, sizeof(params));
1199 params.cipher = key->cipher;
1200 params.key_len = key->key_len;
1201 params.seq_len = key->seq_len;
1202 params.seq = key->seq;
1203 params.key = key->key;
1204
1205 callback(cookie, &params);
1206
1207 return key->key_len ? 0 : -ENOENT;
1208}
1209
1210static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
1211 struct net_device *ndev,
1212 u8 key_index, bool unicast,
1213 bool multicast)
1214{
Kalle Valod6d5c062011-11-25 13:17:37 +02001215 struct ath6kl *ar = ath6kl_priv(ndev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301216 struct ath6kl_vif *vif = netdev_priv(ndev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001217 struct ath6kl_key *key = NULL;
Kalle Valobdcd8172011-07-18 00:22:30 +03001218 u8 key_usage;
Edward Lu229ed6b2011-08-30 21:58:07 +03001219 enum crypto_type key_type = NONE_CRYPT;
Kalle Valobdcd8172011-07-18 00:22:30 +03001220
1221 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
1222
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301223 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001224 return -EIO;
1225
Vivek Natarajan792ecb32011-12-29 16:18:39 +05301226 if (key_index > WMI_MAX_KEY_INDEX) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001227 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1228 "%s: key index %d out of bounds\n",
1229 __func__, key_index);
1230 return -ENOENT;
1231 }
1232
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301233 if (!vif->keys[key_index].key_len) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001234 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n",
1235 __func__, key_index);
1236 return -EINVAL;
1237 }
1238
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301239 vif->def_txkey_index = key_index;
Vasanthakumar Thiagarajan6f2a73f2011-10-25 19:34:06 +05301240 key = &vif->keys[vif->def_txkey_index];
Kalle Valobdcd8172011-07-18 00:22:30 +03001241 key_usage = GROUP_USAGE;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301242 if (vif->prwise_crypto == WEP_CRYPT)
Kalle Valobdcd8172011-07-18 00:22:30 +03001243 key_usage |= TX_USAGE;
Edward Lu229ed6b2011-08-30 21:58:07 +03001244 if (unicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301245 key_type = vif->prwise_crypto;
Edward Lu229ed6b2011-08-30 21:58:07 +03001246 if (multicast)
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301247 key_type = vif->grp_crypto;
Kalle Valobdcd8172011-07-18 00:22:30 +03001248
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301249 if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags))
Jouni Malinen9a5b1312011-08-30 21:57:52 +03001250 return 0; /* Delay until AP mode has been started */
1251
Jouni Malinenf3e61ec2011-11-02 23:46:47 +02001252 return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
1253 vif->def_txkey_index,
1254 key_type, key_usage,
1255 key->key_len, key->seq, key->seq_len,
1256 key->key,
1257 KEY_OP_INIT_VAL, NULL,
1258 SYNC_BOTH_WMIFLAG);
Kalle Valobdcd8172011-07-18 00:22:30 +03001259}
1260
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301261void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001262 bool ismcast)
1263{
1264 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1265 "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast);
1266
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301267 cfg80211_michael_mic_failure(vif->ndev, vif->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001268 (ismcast ? NL80211_KEYTYPE_GROUP :
1269 NL80211_KEYTYPE_PAIRWISE), keyid, NULL,
1270 GFP_KERNEL);
1271}
1272
1273static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1274{
1275 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301276 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001277 int ret;
1278
1279 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__,
1280 changed);
1281
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301282 vif = ath6kl_vif_first(ar);
1283 if (!vif)
1284 return -EIO;
1285
1286 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001287 return -EIO;
1288
1289 if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
1290 ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold);
1291 if (ret != 0) {
1292 ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n");
1293 return -EIO;
1294 }
1295 }
1296
1297 return 0;
1298}
1299
1300/*
1301 * The type nl80211_tx_power_setting replaces the following
1302 * data type from 2.6.36 onwards
1303*/
1304static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
1305 enum nl80211_tx_power_setting type,
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001306 int mbm)
Kalle Valobdcd8172011-07-18 00:22:30 +03001307{
1308 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301309 struct ath6kl_vif *vif;
Luis R. Rodriguezb992a282011-11-23 11:08:14 -05001310 int dbm = MBM_TO_DBM(mbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001311
1312 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
1313 type, dbm);
1314
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301315 vif = ath6kl_vif_first(ar);
1316 if (!vif)
1317 return -EIO;
1318
1319 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001320 return -EIO;
1321
1322 switch (type) {
1323 case NL80211_TX_POWER_AUTOMATIC:
1324 return 0;
1325 case NL80211_TX_POWER_LIMITED:
Kalle Valod0d670a2012-03-07 20:03:58 +02001326 ar->tx_pwr = dbm;
Kalle Valobdcd8172011-07-18 00:22:30 +03001327 break;
1328 default:
1329 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
1330 __func__, type);
1331 return -EOPNOTSUPP;
1332 }
1333
Kalle Valod0d670a2012-03-07 20:03:58 +02001334 ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
Kalle Valobdcd8172011-07-18 00:22:30 +03001335
1336 return 0;
1337}
1338
1339static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
1340{
1341 struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301342 struct ath6kl_vif *vif;
Kalle Valobdcd8172011-07-18 00:22:30 +03001343
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301344 vif = ath6kl_vif_first(ar);
1345 if (!vif)
1346 return -EIO;
1347
1348 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001349 return -EIO;
1350
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301351 if (test_bit(CONNECTED, &vif->flags)) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001352 ar->tx_pwr = 0;
1353
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301354 if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001355 ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n");
1356 return -EIO;
1357 }
1358
1359 wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0,
1360 5 * HZ);
1361
1362 if (signal_pending(current)) {
1363 ath6kl_err("target did not respond\n");
1364 return -EINTR;
1365 }
1366 }
1367
1368 *dbm = ar->tx_pwr;
1369 return 0;
1370}
1371
1372static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1373 struct net_device *dev,
1374 bool pmgmt, int timeout)
1375{
1376 struct ath6kl *ar = ath6kl_priv(dev);
1377 struct wmi_power_mode_cmd mode;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301378 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001379
1380 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n",
1381 __func__, pmgmt, timeout);
1382
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301383 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001384 return -EIO;
1385
1386 if (pmgmt) {
1387 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__);
1388 mode.pwr_mode = REC_POWER;
1389 } else {
1390 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__);
1391 mode.pwr_mode = MAX_PERF_POWER;
1392 }
1393
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301394 if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
Kalle Valo96f1fad2012-03-07 20:03:57 +02001395 mode.pwr_mode) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +03001396 ath6kl_err("wmi_powermode_cmd failed\n");
1397 return -EIO;
1398 }
1399
1400 return 0;
1401}
1402
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301403static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
1404 char *name,
1405 enum nl80211_iftype type,
1406 u32 *flags,
1407 struct vif_params *params)
1408{
1409 struct ath6kl *ar = wiphy_priv(wiphy);
1410 struct net_device *ndev;
1411 u8 if_idx, nw_type;
1412
Kalle Valo71f96ee2011-11-14 19:31:30 +02001413 if (ar->num_vif == ar->vif_max) {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301414 ath6kl_err("Reached maximum number of supported vif\n");
1415 return ERR_PTR(-EINVAL);
1416 }
1417
1418 if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) {
1419 ath6kl_err("Not a supported interface type\n");
1420 return ERR_PTR(-EINVAL);
1421 }
1422
1423 ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
1424 if (!ndev)
1425 return ERR_PTR(-ENOMEM);
1426
1427 ar->num_vif++;
1428
1429 return ndev;
1430}
1431
1432static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
1433 struct net_device *ndev)
1434{
1435 struct ath6kl *ar = wiphy_priv(wiphy);
1436 struct ath6kl_vif *vif = netdev_priv(ndev);
1437
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301438 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301439 list_del(&vif->list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05301440 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301441
1442 ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
1443
Kalle Valoc25889e2012-01-17 20:08:27 +02001444 ath6kl_cfg80211_vif_cleanup(vif);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05301445
1446 return 0;
1447}
1448
Kalle Valobdcd8172011-07-18 00:22:30 +03001449static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
1450 struct net_device *ndev,
1451 enum nl80211_iftype type, u32 *flags,
1452 struct vif_params *params)
1453{
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301454 struct ath6kl_vif *vif = netdev_priv(ndev);
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301455 int i;
Kalle Valobdcd8172011-07-18 00:22:30 +03001456
1457 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
1458
Vasanthakumar Thiagarajan1e8d13b2012-04-06 20:24:30 +05301459 /*
1460 * Don't bring up p2p on an interface which is not initialized
1461 * for p2p operation where fw does not have capability to switch
1462 * dynamically between non-p2p and p2p type interface.
1463 */
1464 if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
1465 vif->ar->fw_capabilities) &&
1466 (type == NL80211_IFTYPE_P2P_CLIENT ||
1467 type == NL80211_IFTYPE_P2P_GO)) {
1468 if (vif->ar->vif_max == 1) {
1469 if (vif->fw_vif_idx != 0)
1470 return -EINVAL;
1471 else
1472 goto set_iface_type;
1473 }
1474
1475 for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) {
1476 if (i == vif->fw_vif_idx)
1477 break;
1478 }
1479
1480 if (i == vif->ar->vif_max) {
1481 ath6kl_err("Invalid interface to bring up P2P\n");
1482 return -EINVAL;
1483 }
1484 }
1485
1486set_iface_type:
Kalle Valobdcd8172011-07-18 00:22:30 +03001487 switch (type) {
1488 case NL80211_IFTYPE_STATION:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301489 vif->next_mode = INFRA_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001490 break;
1491 case NL80211_IFTYPE_ADHOC:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301492 vif->next_mode = ADHOC_NETWORK;
Kalle Valobdcd8172011-07-18 00:22:30 +03001493 break;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001494 case NL80211_IFTYPE_AP:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301495 vif->next_mode = AP_NETWORK;
Jouni Malinen6e4604c2011-09-05 17:38:46 +03001496 break;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001497 case NL80211_IFTYPE_P2P_CLIENT:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301498 vif->next_mode = INFRA_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001499 break;
1500 case NL80211_IFTYPE_P2P_GO:
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301501 vif->next_mode = AP_NETWORK;
Jouni Malinen6b5e5d22011-08-30 21:58:05 +03001502 break;
Kalle Valobdcd8172011-07-18 00:22:30 +03001503 default:
1504 ath6kl_err("invalid interface type %u\n", type);
1505 return -EOPNOTSUPP;
1506 }
1507
Vasanthakumar Thiagarajan551959d2011-10-25 19:34:26 +05301508 vif->wdev.iftype = type;
Kalle Valobdcd8172011-07-18 00:22:30 +03001509
1510 return 0;
1511}
1512
1513static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
1514 struct net_device *dev,
1515 struct cfg80211_ibss_params *ibss_param)
1516{
1517 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301518 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001519 int status;
1520
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301521 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001522 return -EIO;
1523
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301524 vif->ssid_len = ibss_param->ssid_len;
1525 memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len);
Kalle Valobdcd8172011-07-18 00:22:30 +03001526
1527 if (ibss_param->channel)
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301528 vif->ch_hint = ibss_param->channel->center_freq;
Kalle Valobdcd8172011-07-18 00:22:30 +03001529
1530 if (ibss_param->channel_fixed) {
1531 /*
1532 * TODO: channel_fixed: The channel should be fixed, do not
1533 * search for IBSSs to join on other channels. Target
1534 * firmware does not support this feature, needs to be
1535 * updated.
1536 */
1537 return -EOPNOTSUPP;
1538 }
1539
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301540 memset(vif->req_bssid, 0, sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001541 if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid))
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301542 memcpy(vif->req_bssid, ibss_param->bssid,
1543 sizeof(vif->req_bssid));
Kalle Valobdcd8172011-07-18 00:22:30 +03001544
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301545 ath6kl_set_wpa_version(vif, 0);
Kalle Valobdcd8172011-07-18 00:22:30 +03001546
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301547 status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM);
Kalle Valobdcd8172011-07-18 00:22:30 +03001548 if (status)
1549 return status;
1550
1551 if (ibss_param->privacy) {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301552 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true);
1553 ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001554 } else {
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301555 ath6kl_set_cipher(vif, 0, true);
1556 ath6kl_set_cipher(vif, 0, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001557 }
1558
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301559 vif->nw_type = vif->next_mode;
Kalle Valobdcd8172011-07-18 00:22:30 +03001560
1561 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1562 "%s: connect called with authmode %d dot11 auth %d"
1563 " PW crypto %d PW crypto len %d GRP crypto %d"
1564 " GRP crypto len %d channel hint %u\n",
1565 __func__,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301566 vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto,
1567 vif->prwise_crypto_len, vif->grp_crypto,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301568 vif->grp_crypto_len, vif->ch_hint);
Kalle Valobdcd8172011-07-18 00:22:30 +03001569
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301570 status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301571 vif->dot11_auth_mode, vif->auth_mode,
1572 vif->prwise_crypto,
1573 vif->prwise_crypto_len,
1574 vif->grp_crypto, vif->grp_crypto_len,
1575 vif->ssid_len, vif->ssid,
Vasanthakumar Thiagarajanf74bac52011-10-25 19:34:05 +05301576 vif->req_bssid, vif->ch_hint,
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08001577 ar->connect_ctrl_flags, SUBTYPE_NONE);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301578 set_bit(CONNECT_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001579
1580 return 0;
1581}
1582
1583static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy,
1584 struct net_device *dev)
1585{
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301586 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001587
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05301588 if (!ath6kl_cfg80211_ready(vif))
Kalle Valobdcd8172011-07-18 00:22:30 +03001589 return -EIO;
1590
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05301591 ath6kl_disconnect(vif);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05301592 memset(vif->ssid, 0, sizeof(vif->ssid));
1593 vif->ssid_len = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +03001594
1595 return 0;
1596}
1597
1598static const u32 cipher_suites[] = {
1599 WLAN_CIPHER_SUITE_WEP40,
1600 WLAN_CIPHER_SUITE_WEP104,
1601 WLAN_CIPHER_SUITE_TKIP,
1602 WLAN_CIPHER_SUITE_CCMP,
Jouni Malinen837cb972011-10-11 17:31:57 +03001603 CCKM_KRK_CIPHER_SUITE,
Dai Shuibing5e070212011-11-03 11:39:37 +02001604 WLAN_CIPHER_SUITE_SMS4,
Kalle Valobdcd8172011-07-18 00:22:30 +03001605};
1606
1607static bool is_rate_legacy(s32 rate)
1608{
1609 static const s32 legacy[] = { 1000, 2000, 5500, 11000,
1610 6000, 9000, 12000, 18000, 24000,
1611 36000, 48000, 54000
1612 };
1613 u8 i;
1614
1615 for (i = 0; i < ARRAY_SIZE(legacy); i++)
1616 if (rate == legacy[i])
1617 return true;
1618
1619 return false;
1620}
1621
1622static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi)
1623{
1624 static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000,
1625 52000, 58500, 65000, 72200
1626 };
1627 u8 i;
1628
1629 for (i = 0; i < ARRAY_SIZE(ht20); i++) {
1630 if (rate == ht20[i]) {
1631 if (i == ARRAY_SIZE(ht20) - 1)
1632 /* last rate uses sgi */
1633 *sgi = true;
1634 else
1635 *sgi = false;
1636
1637 *mcs = i;
1638 return true;
1639 }
1640 }
1641 return false;
1642}
1643
1644static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
1645{
1646 static const s32 ht40[] = { 13500, 27000, 40500, 54000,
1647 81000, 108000, 121500, 135000,
1648 150000
1649 };
1650 u8 i;
1651
1652 for (i = 0; i < ARRAY_SIZE(ht40); i++) {
1653 if (rate == ht40[i]) {
1654 if (i == ARRAY_SIZE(ht40) - 1)
1655 /* last rate uses sgi */
1656 *sgi = true;
1657 else
1658 *sgi = false;
1659
1660 *mcs = i;
1661 return true;
1662 }
1663 }
1664
1665 return false;
1666}
1667
1668static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
1669 u8 *mac, struct station_info *sinfo)
1670{
1671 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301672 struct ath6kl_vif *vif = netdev_priv(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +03001673 long left;
1674 bool sgi;
1675 s32 rate;
1676 int ret;
1677 u8 mcs;
1678
Vasanthakumar Thiagarajan8c8b65e2011-10-25 19:34:04 +05301679 if (memcmp(mac, vif->bssid, ETH_ALEN) != 0)
Kalle Valobdcd8172011-07-18 00:22:30 +03001680 return -ENOENT;
1681
1682 if (down_interruptible(&ar->sem))
1683 return -EBUSY;
1684
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301685 set_bit(STATS_UPDATE_PEND, &vif->flags);
Kalle Valobdcd8172011-07-18 00:22:30 +03001686
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301687 ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx);
Kalle Valobdcd8172011-07-18 00:22:30 +03001688
1689 if (ret != 0) {
1690 up(&ar->sem);
1691 return -EIO;
1692 }
1693
1694 left = wait_event_interruptible_timeout(ar->event_wq,
1695 !test_bit(STATS_UPDATE_PEND,
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301696 &vif->flags),
Kalle Valobdcd8172011-07-18 00:22:30 +03001697 WMI_TIMEOUT);
1698
1699 up(&ar->sem);
1700
1701 if (left == 0)
1702 return -ETIMEDOUT;
1703 else if (left < 0)
1704 return left;
1705
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301706 if (vif->target_stats.rx_byte) {
1707 sinfo->rx_bytes = vif->target_stats.rx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001708 sinfo->filled |= STATION_INFO_RX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301709 sinfo->rx_packets = vif->target_stats.rx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001710 sinfo->filled |= STATION_INFO_RX_PACKETS;
1711 }
1712
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301713 if (vif->target_stats.tx_byte) {
1714 sinfo->tx_bytes = vif->target_stats.tx_byte;
Kalle Valobdcd8172011-07-18 00:22:30 +03001715 sinfo->filled |= STATION_INFO_TX_BYTES;
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301716 sinfo->tx_packets = vif->target_stats.tx_pkt;
Kalle Valobdcd8172011-07-18 00:22:30 +03001717 sinfo->filled |= STATION_INFO_TX_PACKETS;
1718 }
1719
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301720 sinfo->signal = vif->target_stats.cs_rssi;
Kalle Valobdcd8172011-07-18 00:22:30 +03001721 sinfo->filled |= STATION_INFO_SIGNAL;
1722
Vasanthakumar Thiagarajanb95907a2011-10-25 19:34:11 +05301723 rate = vif->target_stats.tx_ucast_rate;
Kalle Valobdcd8172011-07-18 00:22:30 +03001724
1725 if (is_rate_legacy(rate)) {
1726 sinfo->txrate.legacy = rate / 100;
1727 } else if (is_rate_ht20(rate, &mcs, &sgi)) {
1728 if (sgi) {
1729 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1730 sinfo->txrate.mcs = mcs - 1;
1731 } else {
1732 sinfo->txrate.mcs = mcs;
1733 }
1734
1735 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1736 } else if (is_rate_ht40(rate, &mcs, &sgi)) {
1737 if (sgi) {
1738 sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1739 sinfo->txrate.mcs = mcs - 1;
1740 } else {
1741 sinfo->txrate.mcs = mcs;
1742 }
1743
1744 sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
1745 sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
1746 } else {
Kalle Valo9a730832011-09-27 23:33:28 +03001747 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
1748 "invalid rate from stats: %d\n", rate);
1749 ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
Kalle Valobdcd8172011-07-18 00:22:30 +03001750 return 0;
1751 }
1752
1753 sinfo->filled |= STATION_INFO_TX_BITRATE;
1754
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301755 if (test_bit(CONNECTED, &vif->flags) &&
1756 test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05301757 vif->nw_type == INFRA_NETWORK) {
Jouni Malinen32c10872011-09-19 19:15:07 +03001758 sinfo->filled |= STATION_INFO_BSS_PARAM;
1759 sinfo->bss_param.flags = 0;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05301760 sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
1761 sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
Jouni Malinen32c10872011-09-19 19:15:07 +03001762 }
1763
Kalle Valobdcd8172011-07-18 00:22:30 +03001764 return 0;
1765}
1766
1767static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1768 struct cfg80211_pmksa *pmksa)
1769{
1770 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301771 struct ath6kl_vif *vif = netdev_priv(netdev);
1772
1773 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001774 pmksa->pmkid, true);
1775}
1776
1777static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
1778 struct cfg80211_pmksa *pmksa)
1779{
1780 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301781 struct ath6kl_vif *vif = netdev_priv(netdev);
1782
1783 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid,
Kalle Valobdcd8172011-07-18 00:22:30 +03001784 pmksa->pmkid, false);
1785}
1786
1787static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
1788{
1789 struct ath6kl *ar = ath6kl_priv(netdev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05301790 struct ath6kl_vif *vif = netdev_priv(netdev);
1791
1792 if (test_bit(CONNECTED, &vif->flags))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05301793 return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx,
1794 vif->bssid, NULL, false);
Kalle Valobdcd8172011-07-18 00:22:30 +03001795 return 0;
1796}
1797
Raja Manid91e8ee2012-01-30 17:13:10 +05301798static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
1799 struct cfg80211_wowlan *wow, u32 *filter)
Raja Mani6cb3c712011-11-07 22:52:45 +02001800{
Raja Manid91e8ee2012-01-30 17:13:10 +05301801 int ret, pos;
1802 u8 mask[WOW_MASK_SIZE];
Raja Mani6cb3c712011-11-07 22:52:45 +02001803 u16 i;
Raja Mani6cb3c712011-11-07 22:52:45 +02001804
Raja Manid91e8ee2012-01-30 17:13:10 +05301805 /* Configure the patterns that we received from the user. */
Raja Mani6cb3c712011-11-07 22:52:45 +02001806 for (i = 0; i < wow->n_patterns; i++) {
1807
1808 /*
1809 * Convert given nl80211 specific mask value to equivalent
1810 * driver specific mask value and send it to the chip along
1811 * with patterns. For example, If the mask value defined in
1812 * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
1813 * then equivalent driver specific mask value is
1814 * "0xFF 0x00 0xFF 0x00".
1815 */
1816 memset(&mask, 0, sizeof(mask));
1817 for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
1818 if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
1819 mask[pos] = 0xFF;
1820 }
1821 /*
1822 * Note: Pattern's offset is not passed as part of wowlan
1823 * parameter from CFG layer. So it's always passed as ZERO
1824 * to the firmware. It means, given WOW patterns are always
1825 * matched from the first byte of received pkt in the firmware.
1826 */
1827 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
Raja Manid91e8ee2012-01-30 17:13:10 +05301828 vif->fw_vif_idx, WOW_LIST_ID,
1829 wow->patterns[i].pattern_len,
1830 0 /* pattern offset */,
1831 wow->patterns[i].pattern, mask);
Raja Mani6cb3c712011-11-07 22:52:45 +02001832 if (ret)
1833 return ret;
1834 }
1835
Raja Manid91e8ee2012-01-30 17:13:10 +05301836 if (wow->disconnect)
1837 *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
1838
1839 if (wow->magic_pkt)
1840 *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
1841
1842 if (wow->gtk_rekey_failure)
1843 *filter |= WOW_FILTER_OPTION_GTK_ERROR;
1844
1845 if (wow->eap_identity_req)
1846 *filter |= WOW_FILTER_OPTION_EAP_REQ;
1847
1848 if (wow->four_way_handshake)
1849 *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
1850
1851 return 0;
1852}
1853
1854static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
1855{
1856 static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
1857 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1858 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1859 0x00, 0x08 };
1860 static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
1861 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1862 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1863 0x00, 0x7f };
1864 u8 unicst_offset = 0;
1865 static const u8 arp_pattern[] = { 0x08, 0x06 };
1866 static const u8 arp_mask[] = { 0xff, 0xff };
1867 u8 arp_offset = 20;
1868 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1869 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1870 u8 discvr_offset = 38;
1871 static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
1872 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1873 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
1874 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1875 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1876 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
1877 static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
1878 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1879 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1880 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1881 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1882 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
1883 u8 dhcp_offset = 0;
1884 int ret;
1885
1886 /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
1887 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1888 vif->fw_vif_idx, WOW_LIST_ID,
1889 sizeof(unicst_pattern), unicst_offset,
1890 unicst_pattern, unicst_mask);
1891 if (ret) {
1892 ath6kl_err("failed to add WOW unicast IP pattern\n");
1893 return ret;
1894 }
1895
1896 /* Setup all ARP pkt pattern */
1897 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1898 vif->fw_vif_idx, WOW_LIST_ID,
1899 sizeof(arp_pattern), arp_offset,
1900 arp_pattern, arp_mask);
1901 if (ret) {
1902 ath6kl_err("failed to add WOW ARP pattern\n");
1903 return ret;
1904 }
1905
1906 /*
1907 * Setup multicast pattern for mDNS 224.0.0.251,
1908 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1909 */
1910 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1911 vif->fw_vif_idx, WOW_LIST_ID,
1912 sizeof(discvr_pattern), discvr_offset,
1913 discvr_pattern, discvr_mask);
1914 if (ret) {
1915 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
1916 return ret;
1917 }
1918
1919 /* Setup all DHCP broadcast pkt pattern */
1920 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1921 vif->fw_vif_idx, WOW_LIST_ID,
1922 sizeof(dhcp_pattern), dhcp_offset,
1923 dhcp_pattern, dhcp_mask);
1924 if (ret) {
1925 ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
1926 return ret;
1927 }
1928
1929 return 0;
1930}
1931
1932static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
1933{
1934 struct net_device *ndev = vif->ndev;
1935 static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
1936 static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
1937 u8 discvr_offset = 38;
1938 u8 mac_mask[ETH_ALEN];
1939 int ret;
1940
1941 /* Setup unicast pkt pattern */
1942 memset(mac_mask, 0xff, ETH_ALEN);
1943 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1944 vif->fw_vif_idx, WOW_LIST_ID,
1945 ETH_ALEN, 0, ndev->dev_addr,
1946 mac_mask);
1947 if (ret) {
1948 ath6kl_err("failed to add WOW unicast pattern\n");
1949 return ret;
1950 }
1951
1952 /*
1953 * Setup multicast pattern for mDNS 224.0.0.251,
1954 * SSDP 239.255.255.250 and LLMNR 224.0.0.252
1955 */
1956 if ((ndev->flags & IFF_ALLMULTI) ||
1957 (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
1958 ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
1959 vif->fw_vif_idx, WOW_LIST_ID,
1960 sizeof(discvr_pattern), discvr_offset,
1961 discvr_pattern, discvr_mask);
1962 if (ret) {
1963 ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
1964 "pattern\n");
1965 return ret;
1966 }
1967 }
1968
1969 return 0;
1970}
1971
Raja Mani055bde42012-03-21 15:03:37 +05301972static int is_hsleep_mode_procsed(struct ath6kl_vif *vif)
1973{
1974 return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1975}
1976
1977static bool is_ctrl_ep_empty(struct ath6kl *ar)
1978{
1979 return !ar->tx_pending[ar->ctrl_ep];
1980}
1981
1982static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
1983{
1984 int ret, left;
1985
1986 clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
1987
1988 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
1989 ATH6KL_HOST_MODE_ASLEEP);
1990 if (ret)
1991 return ret;
1992
1993 left = wait_event_interruptible_timeout(ar->event_wq,
1994 is_hsleep_mode_procsed(vif),
1995 WMI_TIMEOUT);
1996 if (left == 0) {
1997 ath6kl_warn("timeout, didn't get host sleep cmd processed event\n");
1998 ret = -ETIMEDOUT;
1999 } else if (left < 0) {
2000 ath6kl_warn("error while waiting for host sleep cmd processed event %d\n",
2001 left);
2002 ret = left;
2003 }
2004
2005 if (ar->tx_pending[ar->ctrl_ep]) {
2006 left = wait_event_interruptible_timeout(ar->event_wq,
2007 is_ctrl_ep_empty(ar),
2008 WMI_TIMEOUT);
2009 if (left == 0) {
2010 ath6kl_warn("clear wmi ctrl data timeout\n");
2011 ret = -ETIMEDOUT;
2012 } else if (left < 0) {
2013 ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
2014 ret = left;
2015 }
2016 }
2017
2018 return ret;
2019}
2020
Raja Manid91e8ee2012-01-30 17:13:10 +05302021static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
2022{
2023 struct in_device *in_dev;
2024 struct in_ifaddr *ifa;
2025 struct ath6kl_vif *vif;
Raja Mani055bde42012-03-21 15:03:37 +05302026 int ret;
Raja Manid91e8ee2012-01-30 17:13:10 +05302027 u32 filter = 0;
Raja Manice0dc0c2012-02-20 19:08:08 +05302028 u16 i, bmiss_time;
Raja Manid91e8ee2012-01-30 17:13:10 +05302029 u8 index = 0;
2030 __be32 ips[MAX_IP_ADDRS];
2031
2032 vif = ath6kl_vif_first(ar);
2033 if (!vif)
2034 return -EIO;
2035
2036 if (!ath6kl_cfg80211_ready(vif))
2037 return -EIO;
2038
2039 if (!test_bit(CONNECTED, &vif->flags))
Raja Mani3c411a42012-01-30 17:13:12 +05302040 return -ENOTCONN;
Raja Manid91e8ee2012-01-30 17:13:10 +05302041
2042 if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
2043 return -EINVAL;
2044
2045 /* Clear existing WOW patterns */
2046 for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
2047 ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
2048 WOW_LIST_ID, i);
2049
2050 /*
2051 * Skip the default WOW pattern configuration
2052 * if the driver receives any WOW patterns from
2053 * the user.
2054 */
2055 if (wow)
2056 ret = ath6kl_wow_usr(ar, vif, wow, &filter);
2057 else if (vif->nw_type == AP_NETWORK)
2058 ret = ath6kl_wow_ap(ar, vif);
2059 else
2060 ret = ath6kl_wow_sta(ar, vif);
2061
2062 if (ret)
2063 return ret;
2064
Raja Mani390a8c82012-03-07 11:35:04 +05302065 netif_stop_queue(vif->ndev);
2066
Raja Manice0dc0c2012-02-20 19:08:08 +05302067 if (vif->nw_type != AP_NETWORK) {
2068 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2069 ATH6KL_MAX_WOW_LISTEN_INTL,
2070 0);
2071 if (ret)
2072 return ret;
2073
2074 /* Set listen interval x 15 times as bmiss time */
2075 bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
2076 if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
2077 bmiss_time = ATH6KL_MAX_BMISS_TIME;
2078
2079 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2080 bmiss_time, 0);
2081 if (ret)
2082 return ret;
2083
2084 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2085 0xFFFF, 0, 0xFFFF, 0, 0, 0,
2086 0, 0, 0, 0);
2087 if (ret)
2088 return ret;
2089 }
2090
Raja Mani390a8c82012-03-07 11:35:04 +05302091 ar->state = ATH6KL_STATE_SUSPENDING;
2092
Raja Manic08631c2011-12-16 14:24:24 +05302093 /* Setup own IP addr for ARP agent. */
2094 in_dev = __in_dev_get_rtnl(vif->ndev);
2095 if (!in_dev)
2096 goto skip_arp;
2097
2098 ifa = in_dev->ifa_list;
2099 memset(&ips, 0, sizeof(ips));
2100
2101 /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
2102 while (index < MAX_IP_ADDRS && ifa) {
2103 ips[index] = ifa->ifa_local;
2104 ifa = ifa->ifa_next;
2105 index++;
2106 }
2107
2108 if (ifa) {
2109 ath6kl_err("total IP addr count is exceeding fw limit\n");
2110 return -EINVAL;
2111 }
2112
2113 ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
2114 if (ret) {
2115 ath6kl_err("fail to setup ip for arp agent\n");
2116 return ret;
2117 }
2118
2119skip_arp:
Raja Mani6cb3c712011-11-07 22:52:45 +02002120 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2121 ATH6KL_WOW_MODE_ENABLE,
2122 filter,
2123 WOW_HOST_REQ_DELAY);
2124 if (ret)
2125 return ret;
2126
Raja Mani055bde42012-03-21 15:03:37 +05302127 ret = ath6kl_cfg80211_host_sleep(ar, vif);
Raja Mani6cb3c712011-11-07 22:52:45 +02002128 if (ret)
2129 return ret;
2130
Raja Mani055bde42012-03-21 15:03:37 +05302131 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002132}
2133
2134static int ath6kl_wow_resume(struct ath6kl *ar)
2135{
2136 struct ath6kl_vif *vif;
2137 int ret;
2138
2139 vif = ath6kl_vif_first(ar);
2140 if (!vif)
2141 return -EIO;
2142
Raja Mani390a8c82012-03-07 11:35:04 +05302143 ar->state = ATH6KL_STATE_RESUMING;
2144
Raja Mani6cb3c712011-11-07 22:52:45 +02002145 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2146 ATH6KL_HOST_MODE_AWAKE);
Raja Mani390a8c82012-03-07 11:35:04 +05302147 if (ret) {
2148 ath6kl_warn("Failed to configure host sleep mode for "
2149 "wow resume: %d\n", ret);
2150 ar->state = ATH6KL_STATE_WOW;
2151 return ret;
2152 }
2153
Raja Manice0dc0c2012-02-20 19:08:08 +05302154 if (vif->nw_type != AP_NETWORK) {
2155 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2156 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2157 if (ret)
2158 return ret;
2159
2160 ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
2161 vif->listen_intvl_t, 0);
2162 if (ret)
2163 return ret;
2164
2165 ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
2166 vif->bmiss_time_t, 0);
2167 if (ret)
2168 return ret;
2169 }
2170
Raja Mani390a8c82012-03-07 11:35:04 +05302171 ar->state = ATH6KL_STATE_ON;
2172
2173 netif_wake_queue(vif->ndev);
2174
2175 return 0;
Raja Mani6cb3c712011-11-07 22:52:45 +02002176}
2177
Raja Mani40abc2d2012-03-21 15:03:38 +05302178static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
2179{
2180 struct ath6kl_vif *vif;
2181 int ret;
2182
2183 vif = ath6kl_vif_first(ar);
2184 if (!vif)
2185 return -EIO;
2186
2187 if (!ath6kl_cfg80211_ready(vif))
2188 return -EIO;
2189
2190 ath6kl_cfg80211_stop_all(ar);
2191
2192 /* Save the current power mode before enabling power save */
2193 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
2194
2195 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER);
2196 if (ret)
2197 return ret;
2198
2199 /* Disable WOW mode */
2200 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
2201 ATH6KL_WOW_MODE_DISABLE,
2202 0, 0);
2203 if (ret)
2204 return ret;
2205
2206 /* Flush all non control pkts in TX path */
2207 ath6kl_tx_data_cleanup(ar);
2208
2209 ret = ath6kl_cfg80211_host_sleep(ar, vif);
2210 if (ret)
2211 return ret;
2212
2213 return 0;
2214}
2215
2216static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar)
2217{
2218 struct ath6kl_vif *vif;
2219 int ret;
2220
2221 vif = ath6kl_vif_first(ar);
2222
2223 if (!vif)
2224 return -EIO;
2225
2226 if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) {
2227 ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0,
2228 ar->wmi->saved_pwr_mode);
2229 if (ret)
2230 return ret;
2231 }
2232
2233 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
2234 ATH6KL_HOST_MODE_AWAKE);
2235 if (ret)
2236 return ret;
2237
2238 ar->state = ATH6KL_STATE_ON;
2239
2240 /* Reset scan parameter to default values */
2241 ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
2242 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
2243 if (ret)
2244 return ret;
2245
2246 return 0;
2247}
2248
Kalle Valo52d81a62011-11-01 08:44:21 +02002249int ath6kl_cfg80211_suspend(struct ath6kl *ar,
Raja Mani0f60e9f2011-11-07 22:52:45 +02002250 enum ath6kl_cfg_suspend_mode mode,
2251 struct cfg80211_wowlan *wow)
Kalle Valo52d81a62011-11-01 08:44:21 +02002252{
Vivek Natarajan3d794992012-03-28 19:21:26 +05302253 struct ath6kl_vif *vif;
Raja Mani390a8c82012-03-07 11:35:04 +05302254 enum ath6kl_state prev_state;
Kalle Valo52d81a62011-11-01 08:44:21 +02002255 int ret;
2256
Kalle Valo52d81a62011-11-01 08:44:21 +02002257 switch (mode) {
Raja Manid7c44e02011-11-07 22:52:46 +02002258 case ATH6KL_CFG_SUSPEND_WOW:
2259
2260 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n");
2261
2262 /* Flush all non control pkts in TX path */
2263 ath6kl_tx_data_cleanup(ar);
2264
Raja Mani390a8c82012-03-07 11:35:04 +05302265 prev_state = ar->state;
2266
Raja Manid7c44e02011-11-07 22:52:46 +02002267 ret = ath6kl_wow_suspend(ar, wow);
Raja Mani390a8c82012-03-07 11:35:04 +05302268 if (ret) {
2269 ar->state = prev_state;
Raja Manid7c44e02011-11-07 22:52:46 +02002270 return ret;
Raja Mani390a8c82012-03-07 11:35:04 +05302271 }
Raja Mani1e9a9052012-03-06 15:03:59 +05302272
Raja Manid7c44e02011-11-07 22:52:46 +02002273 ar->state = ATH6KL_STATE_WOW;
2274 break;
2275
Kalle Valo52d81a62011-11-01 08:44:21 +02002276 case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
Raja Mani524441e2011-11-07 22:52:46 +02002277
Raja Mani40abc2d2012-03-21 15:03:38 +05302278 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n");
Raja Mani524441e2011-11-07 22:52:46 +02002279
Raja Mani40abc2d2012-03-21 15:03:38 +05302280 ret = ath6kl_cfg80211_deepsleep_suspend(ar);
Kalle Valo52d81a62011-11-01 08:44:21 +02002281 if (ret) {
Raja Mani40abc2d2012-03-21 15:03:38 +05302282 ath6kl_err("deepsleep suspend failed: %d\n", ret);
2283 return ret;
Kalle Valo52d81a62011-11-01 08:44:21 +02002284 }
2285
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002286 ar->state = ATH6KL_STATE_DEEPSLEEP;
2287
Kalle Valo52d81a62011-11-01 08:44:21 +02002288 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002289
2290 case ATH6KL_CFG_SUSPEND_CUTPOWER:
Raja Mani524441e2011-11-07 22:52:46 +02002291
Kalle Valo7125f012011-12-13 14:51:37 +02002292 ath6kl_cfg80211_stop_all(ar);
Raja Mani524441e2011-11-07 22:52:46 +02002293
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002294 if (ar->state == ATH6KL_STATE_OFF) {
2295 ath6kl_dbg(ATH6KL_DBG_SUSPEND,
2296 "suspend hw off, no action for cutpower\n");
2297 break;
2298 }
2299
2300 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n");
2301
2302 ret = ath6kl_init_hw_stop(ar);
2303 if (ret) {
2304 ath6kl_warn("failed to stop hw during suspend: %d\n",
2305 ret);
2306 }
2307
2308 ar->state = ATH6KL_STATE_CUTPOWER;
2309
2310 break;
2311
Kalle Valo10509f92011-12-13 14:52:07 +02002312 case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
2313 /*
2314 * Nothing needed for schedule scan, firmware is already in
2315 * wow mode and sleeping most of the time.
2316 */
2317 break;
2318
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002319 default:
2320 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002321 }
2322
Vivek Natarajan3d794992012-03-28 19:21:26 +05302323 list_for_each_entry(vif, &ar->vif_list, list)
2324 ath6kl_cfg80211_scan_complete_event(vif, true);
2325
Kalle Valo52d81a62011-11-01 08:44:21 +02002326 return 0;
2327}
Kalle Valod6a434d2012-01-17 20:09:36 +02002328EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
Kalle Valo52d81a62011-11-01 08:44:21 +02002329
2330int ath6kl_cfg80211_resume(struct ath6kl *ar)
2331{
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002332 int ret;
2333
2334 switch (ar->state) {
Raja Manid7c44e02011-11-07 22:52:46 +02002335 case ATH6KL_STATE_WOW:
2336 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n");
2337
2338 ret = ath6kl_wow_resume(ar);
2339 if (ret) {
2340 ath6kl_warn("wow mode resume failed: %d\n", ret);
2341 return ret;
2342 }
2343
Raja Manid7c44e02011-11-07 22:52:46 +02002344 break;
2345
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002346 case ATH6KL_STATE_DEEPSLEEP:
Raja Mani40abc2d2012-03-21 15:03:38 +05302347 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n");
2348
2349 ret = ath6kl_cfg80211_deepsleep_resume(ar);
2350 if (ret) {
2351 ath6kl_warn("deep sleep resume failed: %d\n", ret);
2352 return ret;
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002353 }
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002354 break;
2355
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002356 case ATH6KL_STATE_CUTPOWER:
2357 ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n");
2358
2359 ret = ath6kl_init_hw_start(ar);
2360 if (ret) {
2361 ath6kl_warn("Failed to boot hw in resume: %d\n", ret);
2362 return ret;
2363 }
Raja Manid7c44e02011-11-07 22:52:46 +02002364 break;
Kalle Valob4b2a0b2011-11-01 08:44:44 +02002365
Kalle Valo10509f92011-12-13 14:52:07 +02002366 case ATH6KL_STATE_SCHED_SCAN:
2367 break;
2368
Kalle Valo76a9fbe2011-11-01 08:44:28 +02002369 default:
2370 break;
Kalle Valo52d81a62011-11-01 08:44:21 +02002371 }
2372
2373 return 0;
2374}
Kalle Valod6a434d2012-01-17 20:09:36 +02002375EXPORT_SYMBOL(ath6kl_cfg80211_resume);
Kalle Valo52d81a62011-11-01 08:44:21 +02002376
Kalle Valoabcb3442011-07-22 08:26:20 +03002377#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02002378
2379/* hif layer decides what suspend mode to use */
2380static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy,
Kalle Valoabcb3442011-07-22 08:26:20 +03002381 struct cfg80211_wowlan *wow)
2382{
2383 struct ath6kl *ar = wiphy_priv(wiphy);
2384
Raja Mani0f60e9f2011-11-07 22:52:45 +02002385 return ath6kl_hif_suspend(ar, wow);
Kalle Valoabcb3442011-07-22 08:26:20 +03002386}
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002387
Kalle Valo52d81a62011-11-01 08:44:21 +02002388static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
Chilam Ngaa6cffc2011-10-05 10:12:52 +03002389{
2390 struct ath6kl *ar = wiphy_priv(wiphy);
2391
2392 return ath6kl_hif_resume(ar);
2393}
Raja Mania918fb32011-11-07 22:52:46 +02002394
2395/*
2396 * FIXME: WOW suspend mode is selected if the host sdio controller supports
2397 * both sdio irq wake up and keep power. The target pulls sdio data line to
2398 * wake up the host when WOW pattern matches. This causes sdio irq handler
2399 * is being called in the host side which internally hits ath6kl's RX path.
2400 *
2401 * Since sdio interrupt is not disabled, RX path executes even before
2402 * the host executes the actual resume operation from PM module.
2403 *
2404 * In the current scenario, WOW resume should happen before start processing
2405 * any data from the target. So It's required to perform WOW resume in RX path.
2406 * Ideally we should perform WOW resume only in the actual platform
2407 * resume path. This area needs bit rework to avoid WOW resume in RX path.
2408 *
2409 * ath6kl_check_wow_status() is called from ath6kl_rx().
2410 */
2411void ath6kl_check_wow_status(struct ath6kl *ar)
2412{
Raja Mani390a8c82012-03-07 11:35:04 +05302413 if (ar->state == ATH6KL_STATE_SUSPENDING)
2414 return;
2415
Raja Mania918fb32011-11-07 22:52:46 +02002416 if (ar->state == ATH6KL_STATE_WOW)
2417 ath6kl_cfg80211_resume(ar);
2418}
2419
2420#else
2421
2422void ath6kl_check_wow_status(struct ath6kl *ar)
2423{
2424}
Kalle Valoabcb3442011-07-22 08:26:20 +03002425#endif
2426
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302427static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
2428 bool ht_enable)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002429{
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302430 struct ath6kl_htcap *htcap = &vif->htcap;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302431
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302432 if (htcap->ht_enable == ht_enable)
2433 return 0;
Sujith Manoharane68f6752011-12-22 12:15:27 +05302434
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302435 if (ht_enable) {
2436 /* Set default ht capabilities */
2437 htcap->ht_enable = true;
2438 htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ?
2439 ath6kl_g_htcap : ath6kl_a_htcap;
2440 htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
2441 } else /* Disable ht */
2442 memset(htcap, 0, sizeof(*htcap));
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002443
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302444 return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx,
2445 band, htcap);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002446}
2447
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002448static bool ath6kl_is_p2p_ie(const u8 *pos)
2449{
2450 return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
2451 pos[2] == 0x50 && pos[3] == 0x6f &&
2452 pos[4] == 0x9a && pos[5] == 0x09;
2453}
2454
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302455static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
2456 const u8 *ies, size_t ies_len)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002457{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302458 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002459 const u8 *pos;
2460 u8 *buf = NULL;
2461 size_t len = 0;
2462 int ret;
2463
2464 /*
2465 * Filter out P2P IE(s) since they will be included depending on
2466 * the Probe Request frame in ath6kl_send_go_probe_resp().
2467 */
2468
2469 if (ies && ies_len) {
2470 buf = kmalloc(ies_len, GFP_KERNEL);
2471 if (buf == NULL)
2472 return -ENOMEM;
2473 pos = ies;
2474 while (pos + 1 < ies + ies_len) {
2475 if (pos + 2 + pos[1] > ies + ies_len)
2476 break;
2477 if (!ath6kl_is_p2p_ie(pos)) {
2478 memcpy(buf + len, pos, 2 + pos[1]);
2479 len += 2 + pos[1];
2480 }
2481 pos += 2 + pos[1];
2482 }
2483 }
2484
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302485 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2486 WMI_FRAME_PROBE_RESP, buf, len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002487 kfree(buf);
2488 return ret;
2489}
2490
Johannes Berg88600202012-02-13 15:17:18 +01002491static int ath6kl_set_ies(struct ath6kl_vif *vif,
2492 struct cfg80211_beacon_data *info)
2493{
2494 struct ath6kl *ar = vif->ar;
2495 int res;
2496
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002497 /* this also clears IE in fw if it's not set */
2498 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2499 WMI_FRAME_BEACON,
2500 info->beacon_ies,
2501 info->beacon_ies_len);
2502 if (res)
2503 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002504
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002505 /* this also clears IE in fw if it's not set */
2506 res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
2507 info->proberesp_ies_len);
2508 if (res)
2509 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002510
Aarthi Thiruvengadam17a7b162012-03-08 12:25:02 -08002511 /* this also clears IE in fw if it's not set */
2512 res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
2513 WMI_FRAME_ASSOC_RESP,
2514 info->assocresp_ies,
2515 info->assocresp_ies_len);
2516 if (res)
2517 return res;
Johannes Berg88600202012-02-13 15:17:18 +01002518
2519 return 0;
2520}
2521
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302522static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
2523 struct ieee80211_channel *chan,
2524 enum nl80211_channel_type channel_type)
2525{
2526 struct ath6kl_vif *vif;
2527
2528 /*
2529 * 'dev' could be NULL if a channel change is required for the hardware
2530 * device itself, instead of a particular VIF.
2531 *
2532 * FIXME: To be handled properly when monitor mode is supported.
2533 */
2534 if (!dev)
2535 return -EBUSY;
2536
2537 vif = netdev_priv(dev);
2538
2539 if (!ath6kl_cfg80211_ready(vif))
2540 return -EIO;
2541
2542 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
2543 __func__, chan->center_freq, chan->hw_value);
2544 vif->next_chan = chan->center_freq;
2545 vif->next_ch_type = channel_type;
2546 vif->next_ch_band = chan->band;
2547
2548 return 0;
2549}
2550
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302551static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
2552 u8 *rsn_capab)
2553{
2554 const u8 *rsn_ie;
2555 size_t rsn_ie_len;
2556 u16 cnt;
2557
2558 if (!beacon->tail)
2559 return -EINVAL;
2560
2561 rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
2562 if (!rsn_ie)
2563 return -EINVAL;
2564
2565 rsn_ie_len = *(rsn_ie + 1);
2566 /* skip element id and length */
2567 rsn_ie += 2;
2568
2569 /* skip version, group cipher */
2570 if (rsn_ie_len < 6)
2571 return -EINVAL;
2572 rsn_ie += 6;
2573 rsn_ie_len -= 6;
2574
2575 /* skip pairwise cipher suite */
2576 if (rsn_ie_len < 2)
2577 return -EINVAL;
2578 cnt = *((u16 *) rsn_ie);
2579 rsn_ie += (2 + cnt * 4);
2580 rsn_ie_len -= (2 + cnt * 4);
2581
2582 /* skip akm suite */
2583 if (rsn_ie_len < 2)
2584 return -EINVAL;
2585 cnt = *((u16 *) rsn_ie);
2586 rsn_ie += (2 + cnt * 4);
2587 rsn_ie_len -= (2 + cnt * 4);
2588
2589 if (rsn_ie_len < 2)
2590 return -EINVAL;
2591
2592 memcpy(rsn_capab, rsn_ie, 2);
2593
2594 return 0;
2595}
2596
Johannes Berg88600202012-02-13 15:17:18 +01002597static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
2598 struct cfg80211_ap_settings *info)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002599{
2600 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302601 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002602 struct ieee80211_mgmt *mgmt;
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002603 bool hidden = false;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002604 u8 *ies;
2605 int ies_len;
2606 struct wmi_connect_cmd p;
2607 int res;
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302608 int i, ret;
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302609 u16 rsn_capab = 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002610
Johannes Berg88600202012-02-13 15:17:18 +01002611 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002612
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05302613 if (!ath6kl_cfg80211_ready(vif))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002614 return -EIO;
2615
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302616 if (vif->next_mode != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002617 return -EOPNOTSUPP;
2618
Johannes Berg88600202012-02-13 15:17:18 +01002619 res = ath6kl_set_ies(vif, &info->beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002620
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002621 ar->ap_mode_bkey.valid = false;
2622
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002623 /* TODO:
2624 * info->interval
2625 * info->dtim_period
2626 */
2627
Johannes Berg88600202012-02-13 15:17:18 +01002628 if (info->beacon.head == NULL)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002629 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002630 mgmt = (struct ieee80211_mgmt *) info->beacon.head;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002631 ies = mgmt->u.beacon.variable;
Johannes Berg88600202012-02-13 15:17:18 +01002632 if (ies > info->beacon.head + info->beacon.head_len)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002633 return -EINVAL;
Johannes Berg88600202012-02-13 15:17:18 +01002634 ies_len = info->beacon.head + info->beacon.head_len - ies;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002635
2636 if (info->ssid == NULL)
2637 return -EINVAL;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302638 memcpy(vif->ssid, info->ssid, info->ssid_len);
2639 vif->ssid_len = info->ssid_len;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002640 if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
Thomas Pedersen67cd22e2012-02-28 15:08:46 -08002641 hidden = true;
2642
2643 res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
2644 if (res)
2645 return res;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002646
Vasanthakumar Thiagarajanbe5abaa2011-11-11 20:33:01 +05302647 ret = ath6kl_set_auth_type(vif, info->auth_type);
2648 if (ret)
2649 return ret;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002650
2651 memset(&p, 0, sizeof(p));
2652
2653 for (i = 0; i < info->crypto.n_akm_suites; i++) {
2654 switch (info->crypto.akm_suites[i]) {
2655 case WLAN_AKM_SUITE_8021X:
2656 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2657 p.auth_mode |= WPA_AUTH;
2658 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2659 p.auth_mode |= WPA2_AUTH;
2660 break;
2661 case WLAN_AKM_SUITE_PSK:
2662 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
2663 p.auth_mode |= WPA_PSK_AUTH;
2664 if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
2665 p.auth_mode |= WPA2_PSK_AUTH;
2666 break;
2667 }
2668 }
2669 if (p.auth_mode == 0)
2670 p.auth_mode = NONE_AUTH;
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302671 vif->auth_mode = p.auth_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002672
2673 for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
2674 switch (info->crypto.ciphers_pairwise[i]) {
2675 case WLAN_CIPHER_SUITE_WEP40:
2676 case WLAN_CIPHER_SUITE_WEP104:
2677 p.prwise_crypto_type |= WEP_CRYPT;
2678 break;
2679 case WLAN_CIPHER_SUITE_TKIP:
2680 p.prwise_crypto_type |= TKIP_CRYPT;
2681 break;
2682 case WLAN_CIPHER_SUITE_CCMP:
2683 p.prwise_crypto_type |= AES_CRYPT;
2684 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002685 case WLAN_CIPHER_SUITE_SMS4:
2686 p.prwise_crypto_type |= WAPI_CRYPT;
2687 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002688 }
2689 }
Edward Lu229ed6b2011-08-30 21:58:07 +03002690 if (p.prwise_crypto_type == 0) {
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002691 p.prwise_crypto_type = NONE_CRYPT;
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302692 ath6kl_set_cipher(vif, 0, true);
Edward Lu229ed6b2011-08-30 21:58:07 +03002693 } else if (info->crypto.n_ciphers_pairwise == 1)
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302694 ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002695
2696 switch (info->crypto.cipher_group) {
2697 case WLAN_CIPHER_SUITE_WEP40:
2698 case WLAN_CIPHER_SUITE_WEP104:
2699 p.grp_crypto_type = WEP_CRYPT;
2700 break;
2701 case WLAN_CIPHER_SUITE_TKIP:
2702 p.grp_crypto_type = TKIP_CRYPT;
2703 break;
2704 case WLAN_CIPHER_SUITE_CCMP:
2705 p.grp_crypto_type = AES_CRYPT;
2706 break;
Dai Shuibingb8214df2011-11-03 11:39:38 +02002707 case WLAN_CIPHER_SUITE_SMS4:
2708 p.grp_crypto_type = WAPI_CRYPT;
2709 break;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002710 default:
2711 p.grp_crypto_type = NONE_CRYPT;
2712 break;
2713 }
Vasanthakumar Thiagarajan240d2792011-10-25 19:34:13 +05302714 ath6kl_set_cipher(vif, info->crypto.cipher_group, false);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002715
2716 p.nw_type = AP_NETWORK;
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302717 vif->nw_type = vif->next_mode;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002718
Vasanthakumar Thiagarajan34503342011-10-25 19:34:02 +05302719 p.ssid_len = vif->ssid_len;
2720 memcpy(p.ssid, vif->ssid, vif->ssid_len);
2721 p.dot11_auth_mode = vif->dot11_auth_mode;
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05302722 p.ch = cpu_to_le16(vif->next_chan);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002723
Thirumalai Pachamuthuc1762a32012-01-12 18:21:39 +05302724 /* Enable uAPSD support by default */
2725 res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
2726 if (res < 0)
2727 return res;
2728
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08002729 if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
2730 p.nw_subtype = SUBTYPE_P2PGO;
2731 } else {
2732 /*
2733 * Due to firmware limitation, it is not possible to
2734 * do P2P mgmt operations in AP mode
2735 */
2736 p.nw_subtype = SUBTYPE_NONE;
2737 }
2738
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05302739 if (info->inactivity_timeout) {
2740 res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx,
2741 info->inactivity_timeout);
2742 if (res < 0)
2743 return res;
2744 }
2745
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302746 if (ath6kl_set_htcap(vif, vif->next_ch_band,
2747 vif->next_ch_type != NL80211_CHAN_NO_HT))
2748 return -EIO;
2749
Vasanthakumar Thiagarajand97c1212012-04-09 20:51:20 +05302750 /*
2751 * Get the PTKSA replay counter in the RSN IE. Supplicant
2752 * will use the RSN IE in M3 message and firmware has to
2753 * advertise the same in beacon/probe response. Send
2754 * the complete RSN IE capability field to firmware
2755 */
2756 if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
2757 test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
2758 ar->fw_capabilities)) {
2759 res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
2760 WLAN_EID_RSN, WMI_RSN_IE_CAPB,
2761 (const u8 *) &rsn_capab,
2762 sizeof(rsn_capab));
2763 if (res < 0)
2764 return res;
2765 }
2766
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302767 res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
Jouni Malinen9a5b1312011-08-30 21:57:52 +03002768 if (res < 0)
2769 return res;
2770
2771 return 0;
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002772}
2773
Johannes Berg88600202012-02-13 15:17:18 +01002774static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
2775 struct cfg80211_beacon_data *beacon)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002776{
Johannes Berg88600202012-02-13 15:17:18 +01002777 struct ath6kl_vif *vif = netdev_priv(dev);
2778
2779 if (!ath6kl_cfg80211_ready(vif))
2780 return -EIO;
2781
2782 if (vif->next_mode != AP_NETWORK)
2783 return -EOPNOTSUPP;
2784
2785 return ath6kl_set_ies(vif, beacon);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002786}
2787
Johannes Berg88600202012-02-13 15:17:18 +01002788static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002789{
2790 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302791 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002792
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302793 if (vif->nw_type != AP_NETWORK)
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002794 return -EOPNOTSUPP;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302795 if (!test_bit(CONNECTED, &vif->flags))
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002796 return -ENOTCONN;
2797
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302798 ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05302799 clear_bit(CONNECTED, &vif->flags);
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002800
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05302801 /* Restore ht setting in firmware */
2802 if (ath6kl_set_htcap(vif, IEEE80211_BAND_2GHZ, true))
2803 return -EIO;
2804
2805 if (ath6kl_set_htcap(vif, IEEE80211_BAND_5GHZ, true))
2806 return -EIO;
2807
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03002808 return 0;
2809}
2810
Jouni Malinen33e53082011-12-27 11:02:56 +02002811static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2812
2813static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
2814 u8 *mac)
2815{
2816 struct ath6kl *ar = ath6kl_priv(dev);
2817 struct ath6kl_vif *vif = netdev_priv(dev);
2818 const u8 *addr = mac ? mac : bcast_addr;
2819
2820 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
2821 addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
2822}
2823
Jouni Malinen23875132011-08-30 21:57:53 +03002824static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
2825 u8 *mac, struct station_parameters *params)
2826{
2827 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302828 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen23875132011-08-30 21:57:53 +03002829
Vasanthakumar Thiagarajanf5938f22011-10-25 19:34:03 +05302830 if (vif->nw_type != AP_NETWORK)
Jouni Malinen23875132011-08-30 21:57:53 +03002831 return -EOPNOTSUPP;
2832
2833 /* Use this only for authorizing/unauthorizing a station */
2834 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2835 return -EOPNOTSUPP;
2836
2837 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302838 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2839 WMI_AP_MLME_AUTHORIZE, mac, 0);
2840 return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
2841 WMI_AP_MLME_UNAUTHORIZE, mac, 0);
Jouni Malinen23875132011-08-30 21:57:53 +03002842}
2843
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002844static int ath6kl_remain_on_channel(struct wiphy *wiphy,
2845 struct net_device *dev,
2846 struct ieee80211_channel *chan,
2847 enum nl80211_channel_type channel_type,
2848 unsigned int duration,
2849 u64 *cookie)
2850{
2851 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302852 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen10522612011-10-27 16:00:13 +03002853 u32 id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002854
2855 /* TODO: if already pending or ongoing remain-on-channel,
2856 * return -EBUSY */
Jouni Malinen10522612011-10-27 16:00:13 +03002857 id = ++vif->last_roc_id;
2858 if (id == 0) {
2859 /* Do not use 0 as the cookie value */
2860 id = ++vif->last_roc_id;
2861 }
2862 *cookie = id;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002863
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302864 return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx,
2865 chan->center_freq, duration);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002866}
2867
2868static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
2869 struct net_device *dev,
2870 u64 cookie)
2871{
2872 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302873 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002874
Jouni Malinen10522612011-10-27 16:00:13 +03002875 if (cookie != vif->last_roc_id)
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002876 return -ENOENT;
Jouni Malinen10522612011-10-27 16:00:13 +03002877 vif->last_cancel_roc_id = cookie;
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002878
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302879 return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx);
Jouni Malinen63fa1e02011-08-30 21:57:55 +03002880}
2881
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302882static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2883 const u8 *buf, size_t len,
2884 unsigned int freq)
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002885{
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302886 struct ath6kl *ar = vif->ar;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002887 const u8 *pos;
2888 u8 *p2p;
2889 int p2p_len;
2890 int ret;
2891 const struct ieee80211_mgmt *mgmt;
2892
2893 mgmt = (const struct ieee80211_mgmt *) buf;
2894
2895 /* Include P2P IE(s) from the frame generated in user space. */
2896
2897 p2p = kmalloc(len, GFP_KERNEL);
2898 if (p2p == NULL)
2899 return -ENOMEM;
2900 p2p_len = 0;
2901
2902 pos = mgmt->u.probe_resp.variable;
2903 while (pos + 1 < buf + len) {
2904 if (pos + 2 + pos[1] > buf + len)
2905 break;
2906 if (ath6kl_is_p2p_ie(pos)) {
2907 memcpy(p2p + p2p_len, pos, 2 + pos[1]);
2908 p2p_len += 2 + pos[1];
2909 }
2910 pos += 2 + pos[1];
2911 }
2912
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05302913 ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq,
2914 mgmt->da, p2p, p2p_len);
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03002915 kfree(p2p);
2916 return ret;
2917}
2918
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08002919static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2920 u32 id,
2921 u32 freq,
2922 u32 wait,
2923 const u8 *buf,
2924 size_t len,
2925 bool *more_data,
2926 bool no_cck)
2927{
2928 struct ieee80211_mgmt *mgmt;
2929 struct ath6kl_sta *conn;
2930 bool is_psq_empty = false;
2931 struct ath6kl_mgmt_buff *mgmt_buf;
2932 size_t mgmt_buf_size;
2933 struct ath6kl *ar = vif->ar;
2934
2935 mgmt = (struct ieee80211_mgmt *) buf;
2936 if (is_multicast_ether_addr(mgmt->da))
2937 return false;
2938
2939 conn = ath6kl_find_sta(vif, mgmt->da);
2940 if (!conn)
2941 return false;
2942
2943 if (conn->sta_flags & STA_PS_SLEEP) {
2944 if (!(conn->sta_flags & STA_PS_POLLED)) {
2945 /* Queue the frames if the STA is sleeping */
2946 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2947 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2948 if (!mgmt_buf)
2949 return false;
2950
2951 INIT_LIST_HEAD(&mgmt_buf->list);
2952 mgmt_buf->id = id;
2953 mgmt_buf->freq = freq;
2954 mgmt_buf->wait = wait;
2955 mgmt_buf->len = len;
2956 mgmt_buf->no_cck = no_cck;
2957 memcpy(mgmt_buf->buf, buf, len);
2958 spin_lock_bh(&conn->psq_lock);
2959 is_psq_empty = skb_queue_empty(&conn->psq) &&
2960 (conn->mgmt_psq_len == 0);
2961 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2962 conn->mgmt_psq_len++;
2963 spin_unlock_bh(&conn->psq_lock);
2964
2965 /*
2966 * If this is the first pkt getting queued
2967 * for this STA, update the PVB for this
2968 * STA.
2969 */
2970 if (is_psq_empty)
2971 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2972 conn->aid, 1);
2973 return true;
2974 }
2975
2976 /*
2977 * This tx is because of a PsPoll.
2978 * Determine if MoreData bit has to be set.
2979 */
2980 spin_lock_bh(&conn->psq_lock);
2981 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2982 *more_data = true;
2983 spin_unlock_bh(&conn->psq_lock);
2984 }
2985
2986 return false;
2987}
2988
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07002989/* Check if SSID length is greater than DIRECT- */
2990static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len)
2991{
2992 const struct ieee80211_mgmt *mgmt;
2993 mgmt = (const struct ieee80211_mgmt *) buf;
2994
2995 /* variable[1] contains the SSID tag length */
2996 if (buf + len >= &mgmt->u.probe_resp.variable[1] &&
2997 (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) {
2998 return true;
2999 }
3000
3001 return false;
3002}
3003
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003004static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
3005 struct ieee80211_channel *chan, bool offchan,
3006 enum nl80211_channel_type channel_type,
3007 bool channel_type_valid, unsigned int wait,
Johannes Berge247bd902011-11-04 11:18:21 +01003008 const u8 *buf, size_t len, bool no_cck,
3009 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003010{
3011 struct ath6kl *ar = ath6kl_priv(dev);
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303012 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003013 u32 id;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003014 const struct ieee80211_mgmt *mgmt;
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003015 bool more_data, queued;
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003016
3017 mgmt = (const struct ieee80211_mgmt *) buf;
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003018 if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
3019 ieee80211_is_probe_resp(mgmt->frame_control) &&
3020 ath6kl_is_p2p_go_ssid(buf, len)) {
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003021 /*
Aarthi Thiruvengadamc86e4f42012-03-15 14:34:56 -07003022 * Send Probe Response frame in GO mode using a separate WMI
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003023 * command to allow the target to fill in the generic IEs.
3024 */
3025 *cookie = 0; /* TX status not supported */
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303026 return ath6kl_send_go_probe_resp(vif, buf, len,
Jouni Malinen8bdfbf42011-08-30 21:58:09 +03003027 chan->center_freq);
3028 }
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003029
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303030 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003031 if (id == 0) {
3032 /*
3033 * 0 is a reserved value in the WMI command and shall not be
3034 * used for the command.
3035 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303036 id = vif->send_action_id++;
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003037 }
3038
3039 *cookie = id;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003040
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003041 /* AP mode Power saving processing */
3042 if (vif->nw_type == AP_NETWORK) {
3043 queued = ath6kl_mgmt_powersave_ap(vif,
3044 id, chan->center_freq,
3045 wait, buf,
3046 len, &more_data, no_cck);
3047 if (queued)
3048 return 0;
Aarthi Thiruvengadam3ca9d1f2011-12-13 13:32:12 -08003049 }
Naveen Gangadharand0ff7382012-02-08 17:51:36 -08003050
3051 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
3052 chan->center_freq, wait,
3053 buf, len, no_cck);
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003054}
3055
Jouni Malinenae32c302011-08-30 21:58:01 +03003056static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
3057 struct net_device *dev,
3058 u16 frame_type, bool reg)
3059{
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303060 struct ath6kl_vif *vif = netdev_priv(dev);
Jouni Malinenae32c302011-08-30 21:58:01 +03003061
3062 ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
3063 __func__, frame_type, reg);
3064 if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
3065 /*
3066 * Note: This notification callback is not allowed to sleep, so
3067 * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
3068 * hardcode target to report Probe Request frames all the time.
3069 */
Vasanthakumar Thiagarajancf5333d2011-10-25 19:34:10 +05303070 vif->probe_req_report = reg;
Jouni Malinenae32c302011-08-30 21:58:01 +03003071 }
3072}
3073
Kalle Valo10509f92011-12-13 14:52:07 +02003074static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
3075 struct net_device *dev,
3076 struct cfg80211_sched_scan_request *request)
3077{
3078 struct ath6kl *ar = ath6kl_priv(dev);
3079 struct ath6kl_vif *vif = netdev_priv(dev);
3080 u16 interval;
3081 int ret;
3082 u8 i;
3083
3084 if (ar->state != ATH6KL_STATE_ON)
3085 return -EIO;
3086
3087 if (vif->sme_state != SME_DISCONNECTED)
3088 return -EBUSY;
3089
Kalle Valob4d13d32012-03-21 10:01:09 +02003090 ath6kl_cfg80211_scan_complete_event(vif, true);
3091
Kalle Valo10509f92011-12-13 14:52:07 +02003092 for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
3093 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
3094 i, DISABLE_SSID_FLAG,
3095 0, NULL);
3096 }
3097
3098 /* fw uses seconds, also make sure that it's >0 */
3099 interval = max_t(u16, 1, request->interval / 1000);
3100
3101 ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
3102 interval, interval,
3103 10, 0, 0, 0, 3, 0, 0, 0);
3104
3105 if (request->n_ssids && request->ssids[0].ssid_len) {
3106 for (i = 0; i < request->n_ssids; i++) {
3107 ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
3108 i, SPECIFIC_SSID_FLAG,
3109 request->ssids[i].ssid_len,
3110 request->ssids[i].ssid);
3111 }
3112 }
3113
3114 ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
3115 ATH6KL_WOW_MODE_ENABLE,
3116 WOW_FILTER_SSID,
3117 WOW_HOST_REQ_DELAY);
3118 if (ret) {
3119 ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
3120 return ret;
3121 }
3122
3123 /* this also clears IE in fw if it's not set */
3124 ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
3125 WMI_FRAME_PROBE_REQ,
3126 request->ie, request->ie_len);
3127 if (ret) {
3128 ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
3129 ret);
3130 return ret;
3131 }
3132
3133 ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
3134 ATH6KL_HOST_MODE_ASLEEP);
3135 if (ret) {
3136 ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
3137 ret);
3138 return ret;
3139 }
3140
3141 ar->state = ATH6KL_STATE_SCHED_SCAN;
3142
3143 return ret;
3144}
3145
3146static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
3147 struct net_device *dev)
3148{
3149 struct ath6kl_vif *vif = netdev_priv(dev);
3150 bool stopped;
3151
3152 stopped = __ath6kl_cfg80211_sscan_stop(vif);
3153
3154 if (!stopped)
3155 return -EIO;
3156
3157 return 0;
3158}
3159
Jouni Malinenf80574a2011-08-30 21:58:04 +03003160static const struct ieee80211_txrx_stypes
3161ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
3162 [NL80211_IFTYPE_STATION] = {
3163 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3164 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3165 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3166 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3167 },
Jouni Malinenba1f6fe2011-12-27 11:03:53 +02003168 [NL80211_IFTYPE_AP] = {
3169 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3170 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3171 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3172 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3173 },
Jouni Malinenf80574a2011-08-30 21:58:04 +03003174 [NL80211_IFTYPE_P2P_CLIENT] = {
3175 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3176 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3177 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3178 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3179 },
3180 [NL80211_IFTYPE_P2P_GO] = {
3181 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3182 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3183 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3184 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3185 },
3186};
3187
Kalle Valobdcd8172011-07-18 00:22:30 +03003188static struct cfg80211_ops ath6kl_cfg80211_ops = {
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303189 .add_virtual_intf = ath6kl_cfg80211_add_iface,
3190 .del_virtual_intf = ath6kl_cfg80211_del_iface,
Kalle Valobdcd8172011-07-18 00:22:30 +03003191 .change_virtual_intf = ath6kl_cfg80211_change_iface,
3192 .scan = ath6kl_cfg80211_scan,
3193 .connect = ath6kl_cfg80211_connect,
3194 .disconnect = ath6kl_cfg80211_disconnect,
3195 .add_key = ath6kl_cfg80211_add_key,
3196 .get_key = ath6kl_cfg80211_get_key,
3197 .del_key = ath6kl_cfg80211_del_key,
3198 .set_default_key = ath6kl_cfg80211_set_default_key,
3199 .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params,
3200 .set_tx_power = ath6kl_cfg80211_set_txpower,
3201 .get_tx_power = ath6kl_cfg80211_get_txpower,
3202 .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt,
3203 .join_ibss = ath6kl_cfg80211_join_ibss,
3204 .leave_ibss = ath6kl_cfg80211_leave_ibss,
3205 .get_station = ath6kl_get_station,
3206 .set_pmksa = ath6kl_set_pmksa,
3207 .del_pmksa = ath6kl_del_pmksa,
3208 .flush_pmksa = ath6kl_flush_pmksa,
Kalle Valo003353b0d2011-09-01 10:14:21 +03003209 CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
Kalle Valoabcb3442011-07-22 08:26:20 +03003210#ifdef CONFIG_PM
Kalle Valo52d81a62011-11-01 08:44:21 +02003211 .suspend = __ath6kl_cfg80211_suspend,
3212 .resume = __ath6kl_cfg80211_resume,
Kalle Valoabcb3442011-07-22 08:26:20 +03003213#endif
Jouni Malinen6a7c9ba2011-08-30 21:57:50 +03003214 .set_channel = ath6kl_set_channel,
Johannes Berg88600202012-02-13 15:17:18 +01003215 .start_ap = ath6kl_start_ap,
3216 .change_beacon = ath6kl_change_beacon,
3217 .stop_ap = ath6kl_stop_ap,
Jouni Malinen33e53082011-12-27 11:02:56 +02003218 .del_station = ath6kl_del_station,
Jouni Malinen23875132011-08-30 21:57:53 +03003219 .change_station = ath6kl_change_station,
Jouni Malinen63fa1e02011-08-30 21:57:55 +03003220 .remain_on_channel = ath6kl_remain_on_channel,
3221 .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
Jouni Malinen8a6c80602011-08-30 21:57:56 +03003222 .mgmt_tx = ath6kl_mgmt_tx,
Jouni Malinenae32c302011-08-30 21:58:01 +03003223 .mgmt_frame_register = ath6kl_mgmt_frame_register,
Kalle Valo10509f92011-12-13 14:52:07 +02003224 .sched_scan_start = ath6kl_cfg80211_sscan_start,
3225 .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
Kalle Valobdcd8172011-07-18 00:22:30 +03003226};
3227
Kalle Valo7125f012011-12-13 14:51:37 +02003228void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
Kalle Valoec4b7f62011-11-01 08:44:04 +02003229{
Kalle Valo10509f92011-12-13 14:52:07 +02003230 ath6kl_cfg80211_sscan_disable(vif);
3231
Kalle Valoec4b7f62011-11-01 08:44:04 +02003232 switch (vif->sme_state) {
Kalle Valoc97a31b2011-12-13 14:51:10 +02003233 case SME_DISCONNECTED:
3234 break;
Kalle Valoec4b7f62011-11-01 08:44:04 +02003235 case SME_CONNECTING:
3236 cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
3237 NULL, 0,
3238 WLAN_STATUS_UNSPECIFIED_FAILURE,
3239 GFP_KERNEL);
3240 break;
3241 case SME_CONNECTED:
Kalle Valoec4b7f62011-11-01 08:44:04 +02003242 cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
3243 break;
3244 }
3245
3246 if (test_bit(CONNECTED, &vif->flags) ||
3247 test_bit(CONNECT_PEND, &vif->flags))
Kalle Valo7125f012011-12-13 14:51:37 +02003248 ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003249
3250 vif->sme_state = SME_DISCONNECTED;
Kalle Valo1f40525512011-11-01 08:44:13 +02003251 clear_bit(CONNECTED, &vif->flags);
3252 clear_bit(CONNECT_PEND, &vif->flags);
Kalle Valoec4b7f62011-11-01 08:44:04 +02003253
3254 /* disable scanning */
Kalle Valo7125f012011-12-13 14:51:37 +02003255 if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
3256 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
3257 ath6kl_warn("failed to disable scan during stop\n");
Kalle Valoec4b7f62011-11-01 08:44:04 +02003258
3259 ath6kl_cfg80211_scan_complete_event(vif, true);
3260}
3261
Kalle Valo7125f012011-12-13 14:51:37 +02003262void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
3263{
3264 struct ath6kl_vif *vif;
3265
3266 vif = ath6kl_vif_first(ar);
3267 if (!vif) {
3268 /* save the current power mode before enabling power save */
3269 ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
3270
3271 if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
3272 ath6kl_warn("ath6kl_deep_sleep_enable: "
3273 "wmi_powermode_cmd failed\n");
3274 return;
3275 }
3276
3277 /*
3278 * FIXME: we should take ar->list_lock to protect changes in the
3279 * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
3280 * sleeps.
3281 */
3282 list_for_each_entry(vif, &ar->vif_list, list)
3283 ath6kl_cfg80211_stop(vif);
3284}
3285
Kalle Valoc25889e2012-01-17 20:08:27 +02003286static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
Kalle Valobdcd8172011-07-18 00:22:30 +03003287{
Vasanthakumar Thiagarajan7baef812012-01-21 15:22:50 +05303288 vif->aggr_cntxt = aggr_init(vif);
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303289 if (!vif->aggr_cntxt) {
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303290 ath6kl_err("failed to initialize aggr\n");
3291 return -ENOMEM;
3292 }
Kalle Valobdcd8172011-07-18 00:22:30 +03003293
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303294 setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303295 (unsigned long) vif->ndev);
Kalle Valo10509f92011-12-13 14:52:07 +02003296 setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
3297 (unsigned long) vif);
3298
Vasanthakumar Thiagarajande3ad712011-10-25 19:34:08 +05303299 set_bit(WMM_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan478ac022011-10-25 19:34:19 +05303300 spin_lock_init(&vif->if_lock);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303301
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303302 INIT_LIST_HEAD(&vif->mc_filter);
3303
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303304 return 0;
3305}
3306
Kalle Valoc25889e2012-01-17 20:08:27 +02003307void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303308{
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303309 struct ath6kl *ar = vif->ar;
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303310 struct ath6kl_mc_filter *mc_filter, *tmp;
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303311
Vasanthakumar Thiagarajan2132c692011-10-25 19:34:07 +05303312 aggr_module_destroy(vif->aggr_cntxt);
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303313
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303314 ar->avail_idx_map |= BIT(vif->fw_vif_idx);
3315
3316 if (vif->nw_type == ADHOC_NETWORK)
3317 ar->ibss_if_active = false;
3318
Vasanthakumar Thiagarajan80abaf92012-01-03 14:42:01 +05303319 list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
3320 list_del(&mc_filter->list);
3321 kfree(mc_filter);
3322 }
3323
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303324 unregister_netdevice(vif->ndev);
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303325
3326 ar->num_vif--;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303327}
3328
3329struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303330 enum nl80211_iftype type, u8 fw_vif_idx,
3331 u8 nw_type)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303332{
3333 struct net_device *ndev;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303334 struct ath6kl_vif *vif;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303335
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303336 ndev = alloc_netdev(sizeof(*vif), name, ether_setup);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303337 if (!ndev)
3338 return NULL;
3339
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303340 vif = netdev_priv(ndev);
3341 ndev->ieee80211_ptr = &vif->wdev;
3342 vif->wdev.wiphy = ar->wiphy;
3343 vif->ar = ar;
Vasanthakumar Thiagarajan108438b2011-10-25 19:34:00 +05303344 vif->ndev = ndev;
3345 SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
3346 vif->wdev.netdev = ndev;
3347 vif->wdev.iftype = type;
Vasanthakumar Thiagarajan334234b2011-10-25 19:34:12 +05303348 vif->fw_vif_idx = fw_vif_idx;
Kalle Valod0d670a2012-03-07 20:03:58 +02003349 vif->nw_type = nw_type;
3350 vif->next_mode = nw_type;
Raja Mani8f46fcc2012-02-20 19:08:07 +05303351 vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
Raja Manice0dc0c2012-02-20 19:08:08 +05303352 vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
Vasanthakumar Thiagarajandf90b362012-04-09 19:03:58 +05303353 vif->htcap.ht_enable = true;
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303354
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303355 memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
3356 if (fw_vif_idx != 0)
3357 ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) |
3358 0x2;
3359
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303360 init_netdev(ndev);
3361
Vasanthakumar Thiagarajane29f25f2011-10-25 19:34:15 +05303362 ath6kl_init_control_info(vif);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303363
Kalle Valoc25889e2012-01-17 20:08:27 +02003364 if (ath6kl_cfg80211_vif_init(vif))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303365 goto err;
3366
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303367 if (register_netdevice(ndev))
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303368 goto err;
3369
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303370 ar->avail_idx_map &= ~BIT(fw_vif_idx);
Vasanthakumar Thiagarajan14ee6f62011-10-25 19:34:09 +05303371 vif->sme_state = SME_DISCONNECTED;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303372 set_bit(WLAN_ENABLED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303373 ar->wlan_pwr_state = WLAN_POWER_STATE_ON;
Vasanthakumar Thiagarajan59c98442011-10-25 19:34:01 +05303374 set_bit(NETDEV_REGISTERED, &vif->flags);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303375
Vasanthakumar Thiagarajan55055972011-10-25 19:34:23 +05303376 if (type == NL80211_IFTYPE_ADHOC)
3377 ar->ibss_if_active = true;
3378
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303379 spin_lock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303380 list_add_tail(&vif->list, &ar->vif_list);
Vasanthakumar Thiagarajan11f6e402011-11-01 16:38:50 +05303381 spin_unlock_bh(&ar->list_lock);
Vasanthakumar Thiagarajan990bd912011-10-25 19:34:20 +05303382
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303383 return ndev;
3384
3385err:
Vasanthakumar Thiagarajan27929722011-10-25 19:34:21 +05303386 aggr_module_destroy(vif->aggr_cntxt);
3387 free_netdev(ndev);
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303388 return NULL;
3389}
3390
Kalle Valo46d33a22012-01-17 20:08:40 +02003391int ath6kl_cfg80211_init(struct ath6kl *ar)
3392{
3393 struct wiphy *wiphy = ar->wiphy;
3394 int ret;
3395
3396 wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
3397
3398 wiphy->max_remain_on_channel_duration = 5000;
3399
3400 /* set device pointer for wiphy */
3401 set_wiphy_dev(wiphy, ar->dev);
3402
3403 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3404 BIT(NL80211_IFTYPE_ADHOC) |
3405 BIT(NL80211_IFTYPE_AP);
3406 if (ar->p2p) {
3407 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
3408 BIT(NL80211_IFTYPE_P2P_CLIENT);
3409 }
3410
3411 /* max num of ssids that can be probed during scanning */
3412 wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
3413 wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
3414 wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
3415 wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
3416 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3417
3418 wiphy->cipher_suites = cipher_suites;
3419 wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3420
3421 wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
3422 WIPHY_WOWLAN_DISCONNECT |
3423 WIPHY_WOWLAN_GTK_REKEY_FAILURE |
3424 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
3425 WIPHY_WOWLAN_EAP_IDENTITY_REQ |
3426 WIPHY_WOWLAN_4WAY_HANDSHAKE;
3427 wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
3428 wiphy->wowlan.pattern_min_len = 1;
3429 wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
3430
3431 wiphy->max_sched_scan_ssids = 10;
3432
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303433 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
3434 WIPHY_FLAG_HAVE_AP_SME |
3435 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
3436 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
3437
3438 if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
3439 ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
3440
Vasanthakumar Thiagarajan03bdeb02012-03-21 20:58:39 +05303441 if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
3442 ar->fw_capabilities))
3443 ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER;
3444
Vasanthakumar Thiagarajanf2afdac2012-02-28 20:20:19 +05303445 ar->wiphy->probe_resp_offload =
3446 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
3447 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
3448 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
3449 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
3450
Kalle Valo46d33a22012-01-17 20:08:40 +02003451 ret = wiphy_register(wiphy);
3452 if (ret < 0) {
3453 ath6kl_err("couldn't register wiphy device\n");
3454 return ret;
3455 }
3456
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303457 ar->wiphy_registered = true;
3458
Kalle Valo46d33a22012-01-17 20:08:40 +02003459 return 0;
3460}
3461
3462void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
Vasanthakumar Thiagarajan8dafb702011-10-25 19:33:58 +05303463{
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303464 wiphy_unregister(ar->wiphy);
Vasanthakumar Thiagarajane5348a12012-02-25 14:43:17 +05303465
3466 ar->wiphy_registered = false;
Kalle Valo45eaa782012-01-17 20:09:05 +02003467}
Kalle Valo46d33a22012-01-17 20:08:40 +02003468
Kalle Valo45eaa782012-01-17 20:09:05 +02003469struct ath6kl *ath6kl_cfg80211_create(void)
3470{
3471 struct ath6kl *ar;
3472 struct wiphy *wiphy;
3473
3474 /* create a new wiphy for use with cfg80211 */
3475 wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
3476
3477 if (!wiphy) {
3478 ath6kl_err("couldn't allocate wiphy device\n");
3479 return NULL;
3480 }
3481
3482 ar = wiphy_priv(wiphy);
3483 ar->wiphy = wiphy;
3484
3485 return ar;
3486}
3487
3488/* Note: ar variable must not be accessed after calling this! */
3489void ath6kl_cfg80211_destroy(struct ath6kl *ar)
3490{
Vasanthakumar Thiagarajan1d2a4452012-01-21 15:22:53 +05303491 int i;
3492
3493 for (i = 0; i < AP_MAX_NUM_STA; i++)
3494 kfree(ar->sta_list[i].aggr_conn);
3495
Vasanthakumar Thiagarajanbe98e3a2011-10-25 19:33:57 +05303496 wiphy_free(ar->wiphy);
Kalle Valobdcd8172011-07-18 00:22:30 +03003497}
Kalle Valo45eaa782012-01-17 20:09:05 +02003498